feat: 添加对单引号字符串的支持,更新词法分析器和相关测试用例
This commit is contained in:
@@ -750,6 +750,30 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn eval_single_quoted_string() {
|
||||
match eval_expr("'hello'") {
|
||||
Value::String(s) => assert_eq!(s, "hello"),
|
||||
v => panic!("Expected String, got {:?}", v),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn eval_mixed_quotes_concat() {
|
||||
match eval_expr("\"a\" + 'b'") {
|
||||
Value::String(s) => assert_eq!(s, "ab"),
|
||||
v => panic!("Expected String, got {:?}", v),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn eval_single_quote_containing_double() {
|
||||
match eval_expr("'he\"llo'") {
|
||||
Value::String(s) => assert_eq!(s, "he\"llo"),
|
||||
v => panic!("Expected String, got {:?}", v),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn eval_string_plus_number() {
|
||||
match eval_expr("\"a\" + 42") {
|
||||
|
||||
+61
-5
@@ -157,7 +157,7 @@ impl Lexer {
|
||||
}
|
||||
}
|
||||
|
||||
'"' => return self.string_literal(line, column, errors),
|
||||
'"' | '\'' => return self.string_literal(line, column, errors, c),
|
||||
|
||||
c if c.is_ascii_digit() => {
|
||||
return Some(self.number_literal(c, line, column));
|
||||
@@ -230,10 +230,11 @@ impl Lexer {
|
||||
line: usize,
|
||||
column: usize,
|
||||
errors: &mut Vec<RuntimeError>,
|
||||
quote: char,
|
||||
) -> Option<Token> {
|
||||
let mut value = String::new();
|
||||
|
||||
while !self.is_at_end() && self.peek() != '"' {
|
||||
while !self.is_at_end() && self.peek() != quote {
|
||||
let c = self.advance();
|
||||
if c == '\\' {
|
||||
// Handle escape sequences
|
||||
@@ -246,6 +247,7 @@ impl Lexer {
|
||||
't' => value.push('\t'),
|
||||
'r' => value.push('\r'),
|
||||
'"' => value.push('"'),
|
||||
'\'' => value.push('\''),
|
||||
'\\' => value.push('\\'),
|
||||
other => {
|
||||
errors.push(RuntimeError::lex(
|
||||
@@ -254,11 +256,11 @@ impl Lexer {
|
||||
column,
|
||||
));
|
||||
// Consume the rest of the string to avoid cascading errors
|
||||
while !self.is_at_end() && self.peek() != '"' {
|
||||
while !self.is_at_end() && self.peek() != quote {
|
||||
self.advance();
|
||||
}
|
||||
if !self.is_at_end() {
|
||||
self.advance(); // consume closing "
|
||||
self.advance(); // consume closing quote
|
||||
}
|
||||
return None;
|
||||
}
|
||||
@@ -273,7 +275,7 @@ impl Lexer {
|
||||
return None;
|
||||
}
|
||||
|
||||
self.advance(); // consume closing "
|
||||
self.advance(); // consume closing quote
|
||||
|
||||
Some(Token {
|
||||
kind: TokenKind::String(value),
|
||||
@@ -522,6 +524,60 @@ mod tests {
|
||||
assert!(errors[0].to_string().contains("Unknown escape"));
|
||||
}
|
||||
|
||||
// ----- single-quoted strings -----
|
||||
|
||||
#[test]
|
||||
fn single_quoted_string() {
|
||||
let kinds = tokenize("'hello'");
|
||||
assert_eq!(kinds, vec![TokenKind::String("hello".into())]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn single_quoted_empty_string() {
|
||||
let kinds = tokenize("''");
|
||||
assert_eq!(kinds, vec![TokenKind::String("".into())]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn single_quoted_with_escape() {
|
||||
let kinds = tokenize("'a\\'b'");
|
||||
assert_eq!(kinds, vec![TokenKind::String("a'b".into())]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn single_quoted_with_newline_escape() {
|
||||
let kinds = tokenize("'a\\nb'");
|
||||
assert_eq!(kinds, vec![TokenKind::String("a\nb".into())]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn mixed_quote_strings() {
|
||||
let kinds = tokenize("\"double\" 'single'");
|
||||
assert_eq!(kinds, vec![
|
||||
TokenKind::String("double".into()),
|
||||
TokenKind::String("single".into()),
|
||||
]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn single_quote_can_contain_double_quote() {
|
||||
let kinds = tokenize("'he\"llo'");
|
||||
assert_eq!(kinds, vec![TokenKind::String("he\"llo".into())]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn double_quote_can_contain_single_quote() {
|
||||
let kinds = tokenize("\"it's\"");
|
||||
assert_eq!(kinds, vec![TokenKind::String("it's".into())]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unterminated_single_quoted_string() {
|
||||
let (_, errors) = tokenize_full("'unterminated");
|
||||
assert_eq!(errors.len(), 1);
|
||||
assert!(errors[0].to_string().contains("Unterminated"));
|
||||
}
|
||||
|
||||
// ----- identifiers and keywords -----
|
||||
|
||||
#[test]
|
||||
|
||||
Reference in New Issue
Block a user