Add lexing and parsing for I/O built-in functions; Refactor built-in function parsing
This commit is contained in:
parent
8dd62e623e
commit
a52e78150e
@ -1,4 +1,7 @@
|
||||
use std::fmt::{self, Display, Formatter};
|
||||
use std::{
|
||||
fmt::{self, Display, Formatter},
|
||||
io::{self, stdin},
|
||||
};
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
@ -6,9 +9,16 @@ use crate::{Type, Value};
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
|
||||
pub enum BuiltInFunction {
|
||||
// Integer and float tools
|
||||
IsEven,
|
||||
IsOdd,
|
||||
|
||||
// List tools
|
||||
Length,
|
||||
|
||||
// I/O
|
||||
ReadLine,
|
||||
WriteLine,
|
||||
}
|
||||
|
||||
impl BuiltInFunction {
|
||||
@ -17,6 +27,8 @@ impl BuiltInFunction {
|
||||
BuiltInFunction::IsEven => "is_even",
|
||||
BuiltInFunction::IsOdd => "is_odd",
|
||||
BuiltInFunction::Length => "length",
|
||||
BuiltInFunction::ReadLine => "read_line",
|
||||
BuiltInFunction::WriteLine => "write_line",
|
||||
}
|
||||
}
|
||||
|
||||
@ -24,13 +36,13 @@ impl BuiltInFunction {
|
||||
&self,
|
||||
_type_arguments: Option<Vec<Type>>,
|
||||
value_arguments: Option<Vec<Value>>,
|
||||
) -> Result<Value, BuiltInFunctionError> {
|
||||
) -> Result<Option<Value>, BuiltInFunctionError> {
|
||||
match self {
|
||||
BuiltInFunction::IsEven => {
|
||||
if let Some(value_arguments) = value_arguments {
|
||||
if value_arguments.len() == 1 {
|
||||
if let Some(integer) = value_arguments[0].as_integer() {
|
||||
Ok(Value::boolean(integer % 2 == 0))
|
||||
Ok(Some(Value::boolean(integer % 2 == 0)))
|
||||
} else {
|
||||
Err(BuiltInFunctionError::ExpectedInteger)
|
||||
}
|
||||
@ -45,7 +57,7 @@ impl BuiltInFunction {
|
||||
if let Some(value_arguments) = value_arguments {
|
||||
if value_arguments.len() == 1 {
|
||||
if let Some(integer) = value_arguments[0].as_integer() {
|
||||
Ok(Value::boolean(integer % 2 != 0))
|
||||
Ok(Some(Value::boolean(integer % 2 != 0)))
|
||||
} else {
|
||||
Err(BuiltInFunctionError::ExpectedInteger)
|
||||
}
|
||||
@ -60,7 +72,7 @@ impl BuiltInFunction {
|
||||
if let Some(value_arguments) = value_arguments {
|
||||
if value_arguments.len() == 1 {
|
||||
if let Some(list) = value_arguments[0].as_list() {
|
||||
Ok(Value::integer(list.len() as i64))
|
||||
Ok(Some(Value::integer(list.len() as i64)))
|
||||
} else {
|
||||
Err(BuiltInFunctionError::ExpectedInteger)
|
||||
}
|
||||
@ -71,6 +83,30 @@ impl BuiltInFunction {
|
||||
Err(BuiltInFunctionError::WrongNumberOfValueArguments)
|
||||
}
|
||||
}
|
||||
BuiltInFunction::ReadLine => {
|
||||
if value_arguments.is_none() {
|
||||
let mut input = String::new();
|
||||
|
||||
stdin().read_line(&mut input)?;
|
||||
|
||||
Ok(Some(Value::string(input)))
|
||||
} else {
|
||||
Err(BuiltInFunctionError::WrongNumberOfValueArguments)
|
||||
}
|
||||
}
|
||||
BuiltInFunction::WriteLine => {
|
||||
if let Some(value_arguments) = value_arguments {
|
||||
if value_arguments.len() == 1 {
|
||||
println!("{}", value_arguments[0]);
|
||||
|
||||
Ok(None)
|
||||
} else {
|
||||
Err(BuiltInFunctionError::WrongNumberOfValueArguments)
|
||||
}
|
||||
} else {
|
||||
Err(BuiltInFunctionError::WrongNumberOfValueArguments)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -79,6 +115,8 @@ impl BuiltInFunction {
|
||||
BuiltInFunction::IsEven => Some(Type::Boolean),
|
||||
BuiltInFunction::IsOdd => Some(Type::Boolean),
|
||||
BuiltInFunction::Length => Some(Type::Integer),
|
||||
BuiltInFunction::ReadLine => Some(Type::String),
|
||||
BuiltInFunction::WriteLine => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -92,5 +130,12 @@ impl Display for BuiltInFunction {
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum BuiltInFunctionError {
|
||||
ExpectedInteger,
|
||||
Io(io::ErrorKind),
|
||||
WrongNumberOfValueArguments,
|
||||
}
|
||||
|
||||
impl From<io::Error> for BuiltInFunctionError {
|
||||
fn from(error: io::Error) -> Self {
|
||||
Self::Io(error.kind())
|
||||
}
|
||||
}
|
||||
|
@ -250,6 +250,8 @@ impl<'a> Lexer<'a> {
|
||||
"is_even" => Token::IsEven,
|
||||
"is_odd" => Token::IsOdd,
|
||||
"length" => Token::Length,
|
||||
"read_line" => Token::ReadLine,
|
||||
"write_line" => Token::WriteLine,
|
||||
_ => Token::Identifier(Identifier::new(string)),
|
||||
};
|
||||
|
||||
|
@ -258,37 +258,19 @@ impl<'src> Parser<'src> {
|
||||
}
|
||||
}
|
||||
}
|
||||
(Token::IsEven, left_span) => {
|
||||
self.next_token()?;
|
||||
|
||||
if let (Token::LeftParenthesis, _) = self.current {
|
||||
self.next_token()?;
|
||||
} else {
|
||||
return Err(ParseError::ExpectedOpeningParenthesis {
|
||||
actual: self.current.0.clone(),
|
||||
span: self.current.1,
|
||||
});
|
||||
}
|
||||
|
||||
if let (Token::RightParenthesis, _) = self.current {
|
||||
self.next_token()?;
|
||||
} else {
|
||||
return Err(ParseError::ExpectedClosingParenthesis {
|
||||
actual: self.current.0.clone(),
|
||||
span: self.current.1,
|
||||
});
|
||||
}
|
||||
|
||||
Ok(Node::new(
|
||||
Statement::BuiltInFunctionCall {
|
||||
function: BuiltInFunction::IsEven,
|
||||
type_arguments: None,
|
||||
value_arguments: None,
|
||||
},
|
||||
(
|
||||
Token::IsEven | Token::IsOdd | Token::Length | Token::ReadLine | Token::WriteLine,
|
||||
left_span,
|
||||
))
|
||||
}
|
||||
(Token::IsOdd, left_span) => {
|
||||
) => {
|
||||
let function = match self.current.0 {
|
||||
Token::IsEven => BuiltInFunction::IsEven,
|
||||
Token::IsOdd => BuiltInFunction::IsOdd,
|
||||
Token::Length => BuiltInFunction::Length,
|
||||
Token::ReadLine => BuiltInFunction::ReadLine,
|
||||
Token::WriteLine => BuiltInFunction::WriteLine,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
self.next_token()?;
|
||||
|
||||
if let (Token::LeftParenthesis, _) = self.current {
|
||||
@ -300,50 +282,38 @@ impl<'src> Parser<'src> {
|
||||
});
|
||||
}
|
||||
|
||||
let mut value_arguments: Option<Vec<Node>> = None;
|
||||
|
||||
loop {
|
||||
if let (Token::RightParenthesis, _) = self.current {
|
||||
self.next_token()?;
|
||||
break;
|
||||
}
|
||||
|
||||
if let (Token::Comma, _) = self.current {
|
||||
self.next_token()?;
|
||||
continue;
|
||||
}
|
||||
|
||||
if let Ok(node) = self.parse_node(0) {
|
||||
if let Some(ref mut arguments) = value_arguments {
|
||||
arguments.push(node);
|
||||
} else {
|
||||
value_arguments = Some(vec![node]);
|
||||
}
|
||||
} else {
|
||||
return Err(ParseError::ExpectedClosingParenthesis {
|
||||
actual: self.current.0.clone(),
|
||||
span: self.current.1,
|
||||
});
|
||||
}
|
||||
|
||||
Ok(Node::new(
|
||||
Statement::BuiltInFunctionCall {
|
||||
function: BuiltInFunction::IsOdd,
|
||||
type_arguments: None,
|
||||
value_arguments: None,
|
||||
},
|
||||
left_span,
|
||||
))
|
||||
}
|
||||
(Token::Length, left_span) => {
|
||||
self.next_token()?;
|
||||
|
||||
if let (Token::LeftParenthesis, _) = self.current {
|
||||
self.next_token()?;
|
||||
} else {
|
||||
return Err(ParseError::ExpectedOpeningParenthesis {
|
||||
actual: self.current.0.clone(),
|
||||
span: self.current.1,
|
||||
});
|
||||
}
|
||||
|
||||
if let (Token::RightParenthesis, _) = self.current {
|
||||
self.next_token()?;
|
||||
} else {
|
||||
return Err(ParseError::ExpectedClosingParenthesis {
|
||||
actual: self.current.0.clone(),
|
||||
span: self.current.1,
|
||||
});
|
||||
}
|
||||
|
||||
Ok(Node::new(
|
||||
Statement::BuiltInFunctionCall {
|
||||
function: BuiltInFunction::Length,
|
||||
function,
|
||||
type_arguments: None,
|
||||
value_arguments: None,
|
||||
value_arguments,
|
||||
},
|
||||
left_span,
|
||||
))
|
||||
|
@ -20,17 +20,19 @@ pub enum Token {
|
||||
IsEven,
|
||||
IsOdd,
|
||||
Length,
|
||||
ReadLine,
|
||||
WriteLine,
|
||||
|
||||
// Symbols
|
||||
Comma,
|
||||
Dot,
|
||||
Equal,
|
||||
Plus,
|
||||
Star,
|
||||
LeftParenthesis,
|
||||
RightParenthesis,
|
||||
LeftSquareBrace,
|
||||
Plus,
|
||||
RightParenthesis,
|
||||
RightSquareBrace,
|
||||
Star,
|
||||
}
|
||||
|
||||
impl Display for Token {
|
||||
@ -45,6 +47,8 @@ impl Display for Token {
|
||||
Token::IsEven => write!(f, "is_even"),
|
||||
Token::IsOdd => write!(f, "is_odd"),
|
||||
Token::Length => write!(f, "length"),
|
||||
Token::ReadLine => write!(f, "read_line"),
|
||||
Token::WriteLine => write!(f, "write_line"),
|
||||
Token::Comma => write!(f, ","),
|
||||
Token::Dot => write!(f, "."),
|
||||
Token::Equal => write!(f, "="),
|
||||
|
@ -527,18 +527,6 @@ impl ValueInner {
|
||||
}
|
||||
}
|
||||
|
||||
pub trait ValueProperties<'a> {}
|
||||
|
||||
pub struct IntegerProperties<'a>(&'a Value);
|
||||
|
||||
impl<'a> IntegerProperties<'a> {
|
||||
pub fn is_even(&self) -> bool {
|
||||
self.0.as_integer().unwrap() % 2 == 0
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ValueProperties<'a> for IntegerProperties<'a> {}
|
||||
|
||||
impl Eq for ValueInner {}
|
||||
|
||||
impl PartialOrd for ValueInner {
|
||||
|
@ -114,7 +114,7 @@ impl Vm {
|
||||
};
|
||||
let function_call_return = function.call(None, values)?;
|
||||
|
||||
Ok(Some(function_call_return))
|
||||
Ok(function_call_return)
|
||||
}
|
||||
Statement::Constant(value) => Ok(Some(value.clone())),
|
||||
Statement::FunctionCall {
|
||||
@ -226,7 +226,7 @@ impl Vm {
|
||||
|
||||
let function_call_return = function.call(None, Some(value_arguments))?;
|
||||
|
||||
return Ok(Some(function_call_return));
|
||||
return Ok(function_call_return);
|
||||
}
|
||||
|
||||
Err(VmError::ExpectedIdentifierOrInteger {
|
||||
|
Loading…
Reference in New Issue
Block a user