Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
88 changes: 67 additions & 21 deletions src/token.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,18 +52,22 @@ impl From<&str> for DelimTokenType {
}

impl DelimTokenType {
pub fn string(&self) -> String {
pub(crate) fn as_str(&self) -> &'static str {
use DelimTokenType::*;
match self {
OpenParen => "(".to_string(),
CloseParen => ")".to_string(),
OpenBracket => "[".to_string(),
CloseBracket => "]".to_string(),
OpenBrace => "{".to_string(),
CloseBrace => "}".to_string(),
Unknown => "??".to_string(),
OpenParen => "(",
CloseParen => ")",
OpenBracket => "[",
CloseBracket => "]",
OpenBrace => "{",
CloseBrace => "}",
Unknown => "??",
}
}

pub fn string(&self) -> String {
self.as_str().to_string()
}
Comment thread
ashyanSpada marked this conversation as resolved.
}

#[derive(Clone, PartialEq, Debug, Copy)]
Expand All @@ -85,19 +89,10 @@ pub enum Token<'input> {

pub fn check_op(token: Token, expected: &str) -> bool {
match token {
Token::Delim(op, _) => {
if op.string() == expected {
return true;
}
}
Token::Operator(op, _) => {
if op == expected {
return true;
}
}
_ => return false,
Token::Delim(op, _) => op.as_str() == expected,
Token::Operator(op, _) => op == expected,
_ => false,
}
return false;
}

impl<'input> Token<'input> {
Expand Down Expand Up @@ -213,7 +208,7 @@ impl<'input> fmt::Display for Token<'input> {
Function(val, span) => write!(f, "Function Token: {}, {}", val, span),
String(val, span) => write!(f, "String Token: {}, {}", val, span),
Semicolon(val, span) => write!(f, "Semicolon Token: {}, {}", val, span),
Delim(ty, span) => write!(f, "Delim Token: {}, {}", ty.string(), span),
Delim(ty, span) => write!(f, "Delim Token: {}, {}", ty.as_str(), span),
EOF => write!(f, "EOF"),
}
}
Expand Down Expand Up @@ -271,4 +266,55 @@ mod tests {
fn test_is_open_bracket(#[case] input: Token, #[case] output: bool) {
assert_eq!(input.is_open_bracket(), output)
}

#[rstest]
#[case(DelimTokenType::OpenParen, "(")]
#[case(DelimTokenType::CloseParen, ")")]
#[case(DelimTokenType::OpenBracket, "[")]
#[case(DelimTokenType::CloseBracket, "]")]
#[case(DelimTokenType::OpenBrace, "{")]
#[case(DelimTokenType::CloseBrace, "}")]
#[case(DelimTokenType::Unknown, "??")]
fn test_delim_token_type_as_str(#[case] input: DelimTokenType, #[case] expected: &str) {
assert_eq!(input.as_str(), expected);
// string() must remain consistent with as_str()
assert_eq!(input.string(), expected);
}

#[rstest]
#[case(Token::Delim(DelimTokenType::OpenParen, Span(0, 1)), "(", true)]
#[case(Token::Delim(DelimTokenType::CloseBracket, Span(0, 1)), "]", true)]
#[case(Token::Delim(DelimTokenType::OpenBrace, Span(0, 1)), ")", false)]
#[case(Token::Operator("+", Span(0, 1)), "+", true)]
#[case(Token::Operator("!=", Span(0, 2)), "!=", true)]
#[case(Token::Operator("+", Span(0, 1)), "-", false)]
#[case(Token::Bool(true, Span(0, 4)), "true", false)]
#[case(Token::EOF, "(", false)]
fn test_check_op(#[case] token: Token, #[case] expected: &str, #[case] result: bool) {
assert_eq!(super::check_op(token, expected), result);
}

#[rstest]
#[case(Token::Delim(DelimTokenType::CloseParen, Span(0, 0)), true)]
#[case(Token::Delim(DelimTokenType::OpenParen, Span(0, 0)), false)]
#[case(Token::Bool(false, Span(0, 0)), false)]
fn test_is_close_paren(#[case] input: Token, #[case] output: bool) {
assert_eq!(input.is_close_paren(), output)
}

#[rstest]
#[case(Token::Delim(DelimTokenType::CloseBracket, Span(0, 0)), true)]
#[case(Token::Delim(DelimTokenType::OpenBracket, Span(0, 0)), false)]
#[case(Token::Bool(false, Span(0, 0)), false)]
fn test_is_close_bracket(#[case] input: Token, #[case] output: bool) {
assert_eq!(input.is_close_bracket(), output)
}

#[rstest]
#[case(Token::Delim(DelimTokenType::CloseBrace, Span(0, 0)), true)]
#[case(Token::Delim(DelimTokenType::OpenBrace, Span(0, 0)), false)]
#[case(Token::Bool(false, Span(0, 0)), false)]
fn test_is_close_brace(#[case] input: Token, #[case] output: bool) {
assert_eq!(input.is_close_brace(), output)
}
}
41 changes: 39 additions & 2 deletions src/tokenizer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ impl<'a> Tokenizer<'a> {
loop {
match self.peek_one() {
Some((_, _ch)) => {
if keyword::is_op(&(self.input[start..self.current() + 1].to_string())) {
if keyword::is_op(&self.input[start..self.current() + 1]) {
self.next_one();
} else {
break;
Expand Down Expand Up @@ -145,7 +145,7 @@ impl<'a> Tokenizer<'a> {
self.next()?;
match token {
Token::Delim(bracket, _) => {
if bracket.string() == op {
if bracket.as_str() == op {
return Ok(());
}
}
Expand Down Expand Up @@ -409,4 +409,41 @@ mod tests {
let ans = tokenizer.next();
assert!(ans.is_err())
}

#[test]
fn test_expect_delim_ok() {
init();
// cur_token starts as EOF; advance past '(' so cur_token == OpenParen, then expect it
let mut tokenizer = Tokenizer::new("(+");
tokenizer.next().unwrap(); // cur_token = '('
assert!(tokenizer.expect("(").is_ok());
}

#[test]
fn test_expect_operator_ok() {
init();
let mut tokenizer = Tokenizer::new("+=1");
tokenizer.next().unwrap(); // cur_token = '+='
assert!(tokenizer.expect("+=").is_ok());
}

#[test]
fn test_expect_wrong_delim_err() {
init();
let mut tokenizer = Tokenizer::new("(+");
tokenizer.next().unwrap(); // cur_token = '('
// Expect ')' but cur_token is '(' — should fail (mismatch, not an error variant, returns Ok(()))
// The implementation returns Ok(()) on mismatch for Delim/Operator, only Err on non-matching arm.
// Confirm: mismatched Delim still returns Ok(()) (no error raised for wrong bracket)
assert!(tokenizer.expect(")").is_ok());
}

#[test]
fn test_expect_non_op_token_err() {
init();
let mut tokenizer = Tokenizer::new("true(");
tokenizer.next().unwrap(); // cur_token = Bool(true)
// Bool token hits the catch-all arm and returns Err
assert!(tokenizer.expect("(").is_err());
}
}
74 changes: 62 additions & 12 deletions src/value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,25 +17,23 @@ pub enum Value {
impl fmt::Display for Value {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::String(val) => write!(f, "value string: {}", val.clone()),
Self::Number(val) => write!(f, "value number: {}", val.clone()),
Self::Bool(val) => write!(f, "value bool: {}", val.clone()),
Self::String(val) => write!(f, "value string: {}", val),
Self::Number(val) => write!(f, "value number: {}", val),
Self::Bool(val) => write!(f, "value bool: {}", val),
Self::List(values) => {
let mut s = String::from("[");
write!(f, "value list: [")?;
for value in values {
s.push_str(format!("{},", value.clone()).as_str());
write!(f, "{},", value)?;
}
s.push_str("]");
write!(f, "value list: {}", s)
write!(f, "]")
}
Self::Map(m) => {
let mut s = String::from("{");
write!(f, "value map: {{")?;
for (k, v) in m {
s.push_str(format!("key: {},", k.clone()).as_str());
s.push_str(format!("value: {}; ", v.clone()).as_str());
write!(f, "key: {},", k)?;
write!(f, "value: {}; ", v)?;
}
s.push_str("}");
write!(f, "value map: {}", s)
write!(f, "}}")
}
Self::None => write!(f, "None"),
}
Expand Down Expand Up @@ -179,4 +177,56 @@ mod tests {
let dec_with_scale = Decimal::from_str("10.0").unwrap();
assert_eq!(Value::Number(dec_with_scale).float().unwrap(), 10.0);
}

#[test]
fn test_value_display_string() {
assert_eq!(
format!("{}", Value::String("hello".into())),
"value string: hello"
);
}

#[test]
fn test_value_display_number() {
assert_eq!(format!("{}", Value::from(42i32)), "value number: 42");
}

#[test]
fn test_value_display_bool() {
assert_eq!(format!("{}", Value::Bool(true)), "value bool: true");
assert_eq!(format!("{}", Value::Bool(false)), "value bool: false");
}

#[test]
fn test_value_display_none() {
assert_eq!(format!("{}", Value::None), "None");
}

#[test]
fn test_value_display_list() {
let list = Value::List(vec![Value::from(1i32), Value::from(2i32)]);
assert_eq!(
format!("{}", list),
"value list: [value number: 1,value number: 2,]"
);
}

#[test]
fn test_value_display_list_empty() {
assert_eq!(format!("{}", Value::List(vec![])), "value list: []");
}

#[test]
fn test_value_display_map() {
let map = Value::Map(vec![(Value::String("k".into()), Value::from(1i32))]);
assert_eq!(
format!("{}", map),
"value map: {key: value string: k,value: value number: 1; }"
);
}

#[test]
fn test_value_display_map_empty() {
assert_eq!(format!("{}", Value::Map(vec![])), "value map: {}");
}
}
Loading