Add modulo support
This commit is contained in:
parent
2cf580d111
commit
24a2642f17
@ -36,7 +36,7 @@ count_slowly = fn (
|
|||||||
|
|
||||||
async {
|
async {
|
||||||
count_slowly(200) # Finishes last
|
count_slowly(200) # Finishes last
|
||||||
count_slowly(100) # Finishes seconds
|
count_slowly(100) # Finishes second
|
||||||
count_slowly(50) # Finishes first
|
count_slowly(50) # Finishes first
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
@ -233,6 +233,7 @@ pub enum BinaryOperator {
|
|||||||
// Math
|
// Math
|
||||||
Add,
|
Add,
|
||||||
Divide,
|
Divide,
|
||||||
|
Modulo,
|
||||||
Multiply,
|
Multiply,
|
||||||
Subtract,
|
Subtract,
|
||||||
|
|
||||||
@ -257,6 +258,7 @@ impl Display for BinaryOperator {
|
|||||||
BinaryOperator::GreaterOrEqual => write!(f, ">="),
|
BinaryOperator::GreaterOrEqual => write!(f, ">="),
|
||||||
BinaryOperator::Less => write!(f, "<"),
|
BinaryOperator::Less => write!(f, "<"),
|
||||||
BinaryOperator::LessOrEqual => write!(f, "<="),
|
BinaryOperator::LessOrEqual => write!(f, "<="),
|
||||||
|
BinaryOperator::Modulo => write!(f, "%"),
|
||||||
BinaryOperator::Multiply => write!(f, "*"),
|
BinaryOperator::Multiply => write!(f, "*"),
|
||||||
BinaryOperator::Or => write!(f, "||"),
|
BinaryOperator::Or => write!(f, "||"),
|
||||||
BinaryOperator::Subtract => write!(f, "-"),
|
BinaryOperator::Subtract => write!(f, "-"),
|
||||||
|
@ -119,7 +119,7 @@ impl Lexer {
|
|||||||
(Token::Minus, (self.position - 1, self.position))
|
(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)?,
|
||||||
'\'' => self.lex_string('\'', source)?,
|
'\'' => self.lex_string('\'', source)?,
|
||||||
'+' => {
|
'+' => {
|
||||||
@ -204,6 +204,11 @@ impl Lexer {
|
|||||||
|
|
||||||
(Token::Slash, (self.position - 1, self.position))
|
(Token::Slash, (self.position - 1, self.position))
|
||||||
}
|
}
|
||||||
|
'%' => {
|
||||||
|
self.position += 1;
|
||||||
|
|
||||||
|
(Token::Percent, (self.position - 1, self.position))
|
||||||
|
}
|
||||||
_ => {
|
_ => {
|
||||||
self.position += 1;
|
self.position += 1;
|
||||||
|
|
||||||
@ -320,14 +325,14 @@ impl Lexer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Lex an identifier token.
|
/// Lex an identifier token.
|
||||||
fn lex_alphabetical<'src>(
|
fn lex_alphanumeric<'src>(
|
||||||
&mut self,
|
&mut self,
|
||||||
source: &'src str,
|
source: &'src str,
|
||||||
) -> Result<(Token<'src>, Span), LexError> {
|
) -> Result<(Token<'src>, Span), LexError> {
|
||||||
let start_pos = self.position;
|
let start_pos = self.position;
|
||||||
|
|
||||||
while let Some(c) = self.peek_char(source) {
|
while let Some(c) = self.peek_char(source) {
|
||||||
if c.is_ascii_alphabetic() || c == '_' {
|
if c.is_ascii_alphanumeric() || c == '_' {
|
||||||
self.next_char(source);
|
self.next_char(source);
|
||||||
} else {
|
} else {
|
||||||
break;
|
break;
|
||||||
@ -430,6 +435,21 @@ impl From<ParseIntError> for LexError {
|
|||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
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]
|
#[test]
|
||||||
fn divide() {
|
fn divide() {
|
||||||
let input = "42 / 2";
|
let input = "42 / 2";
|
||||||
|
@ -302,6 +302,23 @@ impl<'src> Parser<'src> {
|
|||||||
(left_start, right_end),
|
(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 {
|
match self.current.0 {
|
||||||
Token::Greater | Token::GreaterEqual | Token::Less | Token::LessEqual => 5,
|
Token::Greater | Token::GreaterEqual | Token::Less | Token::LessEqual => 5,
|
||||||
Token::Dot => 4,
|
Token::Dot => 4,
|
||||||
|
Token::Percent => 3,
|
||||||
Token::Star => 2,
|
Token::Star => 2,
|
||||||
Token::Slash => 2,
|
Token::Slash => 2,
|
||||||
Token::Plus => 1,
|
Token::Plus => 1,
|
||||||
@ -595,6 +613,26 @@ mod tests {
|
|||||||
|
|
||||||
use super::*;
|
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]
|
#[test]
|
||||||
fn divide() {
|
fn divide() {
|
||||||
let input = "42 / 2";
|
let input = "42 / 2";
|
||||||
|
@ -37,6 +37,7 @@ pub enum Token<'src> {
|
|||||||
Less,
|
Less,
|
||||||
LessEqual,
|
LessEqual,
|
||||||
Minus,
|
Minus,
|
||||||
|
Percent,
|
||||||
Plus,
|
Plus,
|
||||||
RightCurlyBrace,
|
RightCurlyBrace,
|
||||||
RightParenthesis,
|
RightParenthesis,
|
||||||
@ -69,6 +70,7 @@ impl<'src> Token<'src> {
|
|||||||
Token::Less => TokenOwned::Less,
|
Token::Less => TokenOwned::Less,
|
||||||
Token::LessEqual => TokenOwned::LessOrEqual,
|
Token::LessEqual => TokenOwned::LessOrEqual,
|
||||||
Token::Minus => TokenOwned::Minus,
|
Token::Minus => TokenOwned::Minus,
|
||||||
|
Token::Percent => TokenOwned::Percent,
|
||||||
Token::Plus => TokenOwned::Plus,
|
Token::Plus => TokenOwned::Plus,
|
||||||
Token::ReadLine => TokenOwned::ReadLine,
|
Token::ReadLine => TokenOwned::ReadLine,
|
||||||
Token::RightCurlyBrace => TokenOwned::RightCurlyBrace,
|
Token::RightCurlyBrace => TokenOwned::RightCurlyBrace,
|
||||||
@ -104,6 +106,7 @@ impl<'src> Token<'src> {
|
|||||||
Token::Less => "<",
|
Token::Less => "<",
|
||||||
Token::LessEqual => "<=",
|
Token::LessEqual => "<=",
|
||||||
Token::Minus => "-",
|
Token::Minus => "-",
|
||||||
|
Token::Percent => "%",
|
||||||
Token::Plus => "+",
|
Token::Plus => "+",
|
||||||
Token::ReadLine => "read_line",
|
Token::ReadLine => "read_line",
|
||||||
Token::RightCurlyBrace => "}",
|
Token::RightCurlyBrace => "}",
|
||||||
@ -148,6 +151,7 @@ impl<'src> PartialEq for Token<'src> {
|
|||||||
(Token::Less, Token::Less) => true,
|
(Token::Less, Token::Less) => true,
|
||||||
(Token::LessEqual, Token::LessEqual) => true,
|
(Token::LessEqual, Token::LessEqual) => true,
|
||||||
(Token::Minus, Token::Minus) => true,
|
(Token::Minus, Token::Minus) => true,
|
||||||
|
(Token::Percent, Token::Percent) => true,
|
||||||
(Token::Plus, Token::Plus) => true,
|
(Token::Plus, Token::Plus) => true,
|
||||||
(Token::ReadLine, Token::ReadLine) => true,
|
(Token::ReadLine, Token::ReadLine) => true,
|
||||||
(Token::RightCurlyBrace, Token::RightCurlyBrace) => true,
|
(Token::RightCurlyBrace, Token::RightCurlyBrace) => true,
|
||||||
@ -198,6 +202,7 @@ pub enum TokenOwned {
|
|||||||
Less,
|
Less,
|
||||||
LessOrEqual,
|
LessOrEqual,
|
||||||
Minus,
|
Minus,
|
||||||
|
Percent,
|
||||||
Plus,
|
Plus,
|
||||||
RightCurlyBrace,
|
RightCurlyBrace,
|
||||||
RightParenthesis,
|
RightParenthesis,
|
||||||
@ -230,6 +235,7 @@ impl Display for TokenOwned {
|
|||||||
TokenOwned::Less => Token::Less.fmt(f),
|
TokenOwned::Less => Token::Less.fmt(f),
|
||||||
TokenOwned::LessOrEqual => Token::LessEqual.fmt(f),
|
TokenOwned::LessOrEqual => Token::LessEqual.fmt(f),
|
||||||
TokenOwned::Minus => Token::Minus.fmt(f),
|
TokenOwned::Minus => Token::Minus.fmt(f),
|
||||||
|
TokenOwned::Percent => Token::Percent.fmt(f),
|
||||||
TokenOwned::Plus => Token::Plus.fmt(f),
|
TokenOwned::Plus => Token::Plus.fmt(f),
|
||||||
TokenOwned::ReadLine => Token::ReadLine.fmt(f),
|
TokenOwned::ReadLine => Token::ReadLine.fmt(f),
|
||||||
TokenOwned::RightCurlyBrace => Token::RightCurlyBrace.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> {
|
pub fn less_than(&self, other: &Value) -> Result<Value, ValueError> {
|
||||||
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::boolean(left < right)),
|
(ValueInner::Float(left), ValueInner::Float(right)) => Ok(Value::boolean(left < right)),
|
||||||
@ -778,6 +792,7 @@ pub enum ValueError {
|
|||||||
CannotGreaterThanOrEqual(Value, Value),
|
CannotGreaterThanOrEqual(Value, Value),
|
||||||
CannotLessThan(Value, Value),
|
CannotLessThan(Value, Value),
|
||||||
CannotLessThanOrEqual(Value, Value),
|
CannotLessThanOrEqual(Value, Value),
|
||||||
|
CannotModulo(Value, Value),
|
||||||
CannotMultiply(Value, Value),
|
CannotMultiply(Value, Value),
|
||||||
CannotSubtract(Value, Value),
|
CannotSubtract(Value, Value),
|
||||||
CannotOr(Value, Value),
|
CannotOr(Value, Value),
|
||||||
@ -800,6 +815,9 @@ impl Display for ValueError {
|
|||||||
ValueError::CannotDivide(left, right) => {
|
ValueError::CannotDivide(left, right) => {
|
||||||
write!(f, "Cannot divide {} by {}", left, right)
|
write!(f, "Cannot divide {} by {}", left, right)
|
||||||
}
|
}
|
||||||
|
ValueError::CannotModulo(left, right) => {
|
||||||
|
write!(f, "Cannot modulo {} by {}", left, right)
|
||||||
|
}
|
||||||
ValueError::CannotMultiply(left, right) => {
|
ValueError::CannotMultiply(left, right) => {
|
||||||
write!(f, "Cannot multiply {} and {}", left, right)
|
write!(f, "Cannot multiply {} and {}", left, right)
|
||||||
}
|
}
|
||||||
|
@ -102,6 +102,7 @@ impl Vm {
|
|||||||
}
|
}
|
||||||
BinaryOperator::Less => left_value.less_than(&right_value),
|
BinaryOperator::Less => left_value.less_than(&right_value),
|
||||||
BinaryOperator::LessOrEqual => left_value.less_than_or_equal(&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::Multiply => left_value.multiply(&right_value),
|
||||||
BinaryOperator::Or => left_value.or(&right_value),
|
BinaryOperator::Or => left_value.or(&right_value),
|
||||||
BinaryOperator::Subtract => left_value.subtract(&right_value),
|
BinaryOperator::Subtract => left_value.subtract(&right_value),
|
||||||
@ -432,6 +433,13 @@ impl Display for VmError {
|
|||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn modulo() {
|
||||||
|
let input = "42 % 2";
|
||||||
|
|
||||||
|
assert_eq!(run(input, &mut HashMap::new()), Ok(Some(Value::integer(0))));
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn divide() {
|
fn divide() {
|
||||||
let input = "42 / 2";
|
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