use super::shared::{ErrorCode, ParsingError};
#[derive(Debug, PartialEq, Clone, Copy)]
pub enum TokenId {
Whitespace = 2,
Word = 7,
RoundBracketOpen = 10,
RoundBracketClose = 11,
CurlyBracketOpen = 12,
CurlyBracketClose = 14,
SquareBracketOpen = 15,
SquareBracketClose = 16,
StringNormal = 21,
StringExtended = 22,
CommentSingleLine = 31,
CommentMultiLine = 32,
}
#[derive(Debug, PartialEq)]
enum State {
Undetermined,
Whitespace,
CommentAmbiguous,
CommentSingleLine,
CommentMultiLine,
StringAmbiguous,
StringNormal,
StringExtendedConfig,
StringExtendedBody,
StringExtendedEnd,
Word,
}
#[derive(Clone, Debug)]
pub struct Token {
pub id: TokenId,
pub index: usize,
pub length: usize,
pub lexeme: String,
}
impl Token {
#[inline]
#[allow(dead_code)]
pub fn new(id: TokenId, index: usize, length: usize) -> Token {
Token { id, index, length, lexeme: "".to_string() }
}
#[inline]
pub fn new_from_code(id: TokenId, index: usize, length: usize, code: &str) -> Token {
let i = index;
let j = index + length;
let x = &code[i..j];
let y = x.to_string();
let lexeme = y;
Token { id, index, length, lexeme }
}
#[allow(dead_code)]
pub fn finalize(self: &mut Self, code: &str) -> () {
let i = self.index;
let j = self.index + self.length;
let x = &code[i..j];
let y = x.to_string();
self.lexeme = y;
}
#[allow(dead_code)]
pub fn get_lexeme<'a>(self: &Self, code: &'a str) -> &'a str {
let i = self.index;
let j = self.index + self.length;
let x = &code[i..j];
x
}
}
const DEBUG: bool = false;
#[allow(dead_code)]
const HAS_ISSUE_132: bool = true;
#[inline(always)]
fn create_token(id: TokenId, index: usize, length: usize, code: &str) -> Token {
Token::new_from_code(id, index, length, code)
}
pub fn tokenize(code: &str, include_comments: bool, include_whitespace: bool) -> Result<Vec<Token>, ParsingError> {
let original_code = code;
let code = code.as_bytes();
if DEBUG {
println!("{}", "-".repeat(80));
let code = std::str::from_utf8(code).unwrap();
println!("Code: [{}]", code);
}
let mut tokens: Vec<Token> = Vec::new();
let mut token_id = TokenId::Whitespace;
let mut token_index = 0;
let mut token_length = 0;
let mut state: State = State::Undetermined;
let mut attempting_to_close_comment = false;
let mut attempting_to_open_comment = false;
let mut start_semicolon_count = 0;
let mut end_semicolon_count = 0;
let mut start_double_quote_count = 0;
let mut end_double_quote_count = 0;
let mut multi_line_comment_stack = Vec::new();
let mut pc = b'a';
'code_loop: for (code_index, c) in code.iter().enumerate() {
let c = *c;
let mut no_progress_iter = 0;
if DEBUG {
if code_index > 0 {
println!("> State = {:?} (end)", state);
}
println!("{}", "-".repeat(80));
println!("i = {}, c = [{}]", code_index, c);
}
'decision_loop: loop {
if DEBUG {
if no_progress_iter == 0 {
println!("> State = {:?} (start)", state);
} else {
println!("> State = {:?}", state);
}
}
{
no_progress_iter += 1;
if no_progress_iter == 3 {
panic!("No progress!");
}
}
match &state {
State::Undetermined => {
if c.is_ascii_whitespace() {
pc = c;
token_id = TokenId::Whitespace;
token_index = code_index;
token_length = 1;
state = State::Whitespace;
continue 'code_loop;
} else if c == b'(' {
pc = c;
token_id = TokenId::RoundBracketOpen;
token_index = code_index;
token_length = 1;
tokens.push(create_token(token_id, token_index, token_length, original_code));
continue 'code_loop;
} else if c == b')' {
pc = c;
token_id = TokenId::RoundBracketClose;
token_index = code_index;
token_length = 1;
tokens.push(create_token(token_id, token_index, token_length, original_code));
continue 'code_loop;
} else if c == b'[' {
pc = c;
token_id = TokenId::SquareBracketOpen;
token_index = code_index;
token_length = 1;
tokens.push(create_token(token_id, token_index, token_length, original_code));
continue 'code_loop;
} else if c == b']' {
pc = c;
token_id = TokenId::SquareBracketClose;
token_index = code_index;
token_length = 1;
tokens.push(create_token(token_id, token_index, token_length, original_code));
continue 'code_loop;
} else if c == b'{' {
pc = c;
token_id = TokenId::CurlyBracketOpen;
token_index = code_index;
token_length = 1;
tokens.push(create_token(token_id, token_index, token_length, original_code));
continue 'code_loop;
} else if c == b'}' {
pc = c;
token_id = TokenId::CurlyBracketClose;
token_index = code_index;
token_length = 1;
tokens.push(create_token(token_id, token_index, token_length, original_code));
continue 'code_loop;
} else if c == b'"' {
pc = c;
start_double_quote_count = 1;
token_index = code_index;
token_length = 1;
state = State::StringAmbiguous;
continue 'code_loop;
} else if c == b';' {
if pc == b')' {
return Err(ParsingError::new(
code,
code_index,
code_index,
ErrorCode::PotentialErroneousClosingDelimiterFollowedByComment,
"Unterminated potential erroneous closing delimiter followed by comment",
));
}
pc = c;
token_index = code_index;
token_length = 1;
start_semicolon_count = 1;
state = State::CommentAmbiguous;
continue 'code_loop;
} else {
pc = c;
token_id = TokenId::Word;
token_index = code_index;
token_length = 1;
state = State::Word;
continue 'code_loop;
}
}
State::Whitespace => {
if c.is_ascii_whitespace() {
token_length += 1;
continue 'code_loop;
} else {
if include_whitespace {
tokens.push(create_token(token_id, token_index, token_length, original_code));
}
state = State::Undetermined;
continue 'decision_loop;
}
}
State::CommentAmbiguous => {
if c == b';' {
token_length += 1;
start_semicolon_count += 1;
continue 'code_loop;
} else if c == b'(' {
multi_line_comment_stack.push(start_semicolon_count);
token_length += 1;
token_id = TokenId::CommentMultiLine;
state = State::CommentMultiLine;
attempting_to_open_comment = false;
attempting_to_close_comment = false;
continue 'code_loop;
} else if c == b'\n' {
token_id = TokenId::CommentSingleLine;
if include_comments {
tokens.push(create_token(token_id, token_index, token_length, original_code));
}
state = State::Undetermined;
continue 'decision_loop;
} else {
token_length += 1;
token_id = TokenId::CommentSingleLine;
state = State::CommentSingleLine;
continue 'code_loop;
}
}
State::CommentSingleLine => {
if c == b'\n' {
if include_comments {
tokens.push(create_token(token_id, token_index, token_length, original_code));
}
state = State::Undetermined;
continue 'decision_loop;
} else {
token_length += 1;
continue 'code_loop;
}
}
State::CommentMultiLine => {
if DEBUG {
let s: Vec<String> = (&multi_line_comment_stack).into_iter().map(|n| format!("{}{{", ";".repeat(*n))).collect();
println!(" Stack: {:?}", s);
if attempting_to_open_comment {
println!(" Opening?: {}", ";".repeat(start_semicolon_count));
} else if attempting_to_close_comment {
println!(" Closing?: ){}", ";".repeat(end_semicolon_count));
}
}
if attempting_to_open_comment {
if c == b';' {
start_semicolon_count += 1;
token_length += 1;
continue 'code_loop;
} else if c == b'(' {
attempting_to_open_comment = false;
token_length += 1;
multi_line_comment_stack.push(start_semicolon_count);
continue 'code_loop;
} else {
attempting_to_open_comment = false;
continue 'decision_loop;
}
} else if attempting_to_close_comment {
if c == b';' {
end_semicolon_count += 1;
token_length += 1;
continue 'code_loop;
} else {
if end_semicolon_count == 0 {
attempting_to_close_comment = false;
continue 'decision_loop;
} else {
attempting_to_close_comment = false;
let mut pop_off_stack = false;
let mut match_index = 0;
'stack_loop: for (i, stack_semicolon_count) in multi_line_comment_stack.iter().enumerate().rev() {
if end_semicolon_count < *stack_semicolon_count {
attempting_to_close_comment = false;
if DEBUG {
println!(" Dropped!");
}
continue 'decision_loop;
} else if end_semicolon_count == *stack_semicolon_count {
pop_off_stack = true;
match_index = i;
if DEBUG {
println!(" Matched!");
}
break 'stack_loop;
}
}
if pop_off_stack {
while multi_line_comment_stack.len() > match_index {
multi_line_comment_stack.pop();
if DEBUG {
println!(" Pop!");
}
}
if multi_line_comment_stack.len() == 0 {
if include_comments {
tokens.push(create_token(token_id, token_index, token_length, original_code));
}
state = State::Undetermined;
continue 'decision_loop;
}
}
attempting_to_close_comment = false;
continue 'decision_loop;
}
}
} else {
if c == b';' {
token_length += 1;
attempting_to_open_comment = true;
start_semicolon_count = 1;
continue 'code_loop;
} else if c == b')' {
token_length += 1;
attempting_to_close_comment = true;
end_semicolon_count = 0;
continue 'code_loop;
} else {
token_length += 1;
continue 'code_loop;
}
}
}
State::StringAmbiguous => {
if c == b'"' {
token_length += 1;
start_double_quote_count += 1;
continue 'code_loop;
} else {
if start_double_quote_count == 1 {
token_length += 1;
pc = c;
token_id = TokenId::StringNormal;
state = State::StringNormal;
continue 'code_loop;
} else if start_double_quote_count == 2 {
token_id = TokenId::StringNormal;
tokens.push(create_token(token_id, token_index, token_length, original_code));
state = State::Undetermined;
continue 'decision_loop;
} else {
token_id = TokenId::StringExtended;
state = State::StringExtendedConfig;
continue 'decision_loop;
}
}
}
State::StringNormal => {
token_length += 1;
if c == b'"' && pc != b'\\' {
tokens.push(create_token(token_id, token_index, token_length, original_code));
state = State::Undetermined;
} else {
pc = c;
}
continue 'code_loop;
}
State::StringExtendedConfig => {
token_length += 1;
if c == b'.' || c == b',' {
state = State::StringExtendedBody;
}
continue 'code_loop;
}
State::StringExtendedBody => {
token_length += 1;
if c == b'.' || c == b',' {
end_double_quote_count = 0;
state = State::StringExtendedEnd;
}
continue 'code_loop;
}
State::StringExtendedEnd => {
if c == b'"' {
token_length += 1;
end_double_quote_count += 1;
if end_double_quote_count == start_double_quote_count {
tokens.push(create_token(token_id, token_index, token_length, original_code));
state = State::Undetermined
}
} else if c == b'.' || c == b',' {
token_length += 1;
end_double_quote_count = 0;
} else {
token_length += 1;
state = State::StringExtendedBody;
}
continue 'code_loop;
}
State::Word => {
if c.is_ascii_whitespace() || c == b'(' || c == b')' || c == b'[' || c == b']' || c == b'{' || c == b'}' || c == b'"' || c == b';' {
tokens.push(create_token(token_id, token_index, token_length, original_code));
state = State::Undetermined;
continue 'decision_loop;
} else {
token_length += 1;
continue 'code_loop;
}
}
}
}
}
if DEBUG {
println!("> State = {:?} (end)", state);
println!("{}", "-".repeat(80));
}
match &state {
State::Undetermined => { }
State::Whitespace => {
if include_whitespace {
tokens.push(create_token(token_id, token_index, token_length, original_code));
}
}
State::CommentAmbiguous => {
token_id = TokenId::CommentSingleLine;
if include_comments {
tokens.push(create_token(token_id, token_index, token_length, original_code));
}
}
State::CommentSingleLine => {
if include_comments {
tokens.push(create_token(token_id, token_index, token_length, original_code));
}
}
State::CommentMultiLine => {
let mut ok = false;
if attempting_to_close_comment {
if end_semicolon_count > 0 {
let mut pop_off_stack = false;
let mut match_index = 0;
'leftover_stack_loop: for (i, stack_semicolon_count) in multi_line_comment_stack.iter().enumerate().rev() {
if end_semicolon_count < *stack_semicolon_count {
break 'leftover_stack_loop;
} else if end_semicolon_count == *stack_semicolon_count {
pop_off_stack = true;
match_index = i;
break 'leftover_stack_loop;
}
}
if pop_off_stack {
while multi_line_comment_stack.len() > match_index {
multi_line_comment_stack.pop();
}
if multi_line_comment_stack.len() == 0 {
ok = true;
if include_comments {
tokens.push(create_token(token_id, token_index, token_length, original_code));
}
}
}
}
}
if !ok {
return Err(ParsingError::new(code, token_index, code.len(), ErrorCode::UnterminatedMultiLineComment, "Unterminated multiline comment"));
}
}
State::StringAmbiguous => {
if start_double_quote_count == 1 {
return Err(ParsingError::new(code, token_index, code.len(), ErrorCode::UnterminatedNormalString, "Unterminated normal string"));
} else if start_double_quote_count == 2 {
token_id = TokenId::StringNormal;
tokens.push(create_token(token_id, token_index, token_length, original_code));
} else {
return Err(ParsingError::new(code, token_index, code.len(), ErrorCode::UnterminatedExtendedString, "Unterminated extended string"));
}
}
State::StringNormal => {
return Err(ParsingError::new(code, token_index, code.len(), ErrorCode::UnterminatedNormalString, "Unterminated normal string"));
}
State::StringExtendedConfig | State::StringExtendedBody | State::StringExtendedEnd => {
return Err(ParsingError::new(code, token_index, code.len(), ErrorCode::UnterminatedExtendedString, "Unterminated extended string"));
}
State::Word => {
tokens.push(create_token(token_id, token_index, token_length, original_code));
}
}
Ok(tokens)
}
pub fn debug_print_tokenize(code: &str) -> () {
let tokenization_result = tokenize(code, true, true);
match tokenization_result {
Err(parsing_error) => {
println!("Error code: {:?}", parsing_error.error_code);
println!("Error message:\n{}", parsing_error.error_message);
}
Ok(tokens) => {
for token in tokens {
let index = token.index;
let length = token.length;
let id = token.id;
let i = index;
let j = index + length;
let s = &code[i..j];
println!("> i: {}, l: {}, t: ({:?}) -> [{}]", index, length, id, s);
}
}
}
}
#[cfg(test)]
mod tests {
use super::tokenize as original_tokenize;
use super::*;
#[inline]
fn tokenize(code: &str) -> Result<Vec<Token>, ParsingError> {
return original_tokenize(code, true, true);
}
#[inline]
fn assert_eq_code_and_tokens_length(code: &str, tokens: &Vec<Token>) {
let m = code.len();
let mut n = 0;
for token in tokens {
n += token.length;
}
assert_eq!(m, n);
}
fn assert_code_is_ok(code: &str) {
let tokenization_result = tokenize(code);
match tokenization_result {
Ok(_) => (),
Err(parsing_error) => {
panic!(parsing_error.error_message);
}
}
}
fn assert_code_is_not_ok(code: &str) {
let tokenization_result = tokenize(code);
match tokenization_result {
Ok(_) => {
panic!();
}
Err(_) => (),
}
}
#[test]
fn test_misc() {
assert_code_is_ok("");
assert_code_is_ok(" ");
if HAS_ISSUE_132 {
assert_code_is_not_ok(");");
assert_code_is_not_ok(";(););");
assert_code_is_ok(";();");
assert_code_is_ok(";(;(););");
assert_code_is_ok(";(;(;();););");
assert_code_is_ok(";;();;");
assert_code_is_ok(";;(;;();;);;");
assert_code_is_not_ok(";(;;();;););");
}
}
#[test]
fn test_icelandic_name() {
assert_code_is_ok(r#"(var name "Davíð")"#)
}
#[test]
fn test_whitespace() {
{
let code = r#" "#;
let tokenization_result = tokenize(code);
let tokens = tokenization_result.unwrap();
assert_eq_code_and_tokens_length(code, &tokens);
assert!(tokens.len() == 1);
let token = &tokens[0];
assert!(token.id == TokenId::Whitespace);
assert!(token.index == 0);
assert!(token.length == 1);
}
}
#[test]
fn test_word() {
{
let code = r#"hallo"#;
let tokenization_result = tokenize(code);
let tokens = tokenization_result.unwrap();
assert_eq_code_and_tokens_length(code, &tokens);
assert!(tokens.len() == 1);
let token = &tokens[0];
assert!(token.id == TokenId::Word);
assert!(token.index == 0);
assert!(token.length == 5);
}
}
#[test]
fn test_round_bracket_open() {
{
let code = r#"()"#;
let tokenization_result = tokenize(code);
let tokens = tokenization_result.unwrap();
assert_eq_code_and_tokens_length(code, &tokens);
assert!(tokens.len() == 2);
let token = &tokens[0];
assert!(token.id == TokenId::RoundBracketOpen);
assert!(token.index == 0);
assert!(token.length == 1);
}
}
#[test]
fn test_round_bracket_close() {
{
let code = r#"()"#;
let tokenization_result = tokenize(code);
let tokens = tokenization_result.unwrap();
assert_eq_code_and_tokens_length(code, &tokens);
assert!(tokens.len() == 2);
let token = &tokens[1];
assert!(token.id == TokenId::RoundBracketClose);
assert!(token.index == 1);
assert!(token.length == 1);
}
}
#[test]
fn test_curly_bracket_open() {
{
let code = r#"{}"#;
let tokenization_result = tokenize(code);
let tokens = tokenization_result.unwrap();
assert_eq_code_and_tokens_length(code, &tokens);
assert!(tokens.len() == 2);
let token = &tokens[0];
assert!(token.id == TokenId::CurlyBracketOpen);
assert!(token.index == 0);
assert!(token.length == 1);
}
}
#[test]
fn test_curly_bracket_close() {
{
let code = r#"{}"#;
let tokenization_result = tokenize(code);
let tokens = tokenization_result.unwrap();
assert_eq_code_and_tokens_length(code, &tokens);
assert!(tokens.len() == 2);
let token = &tokens[1];
assert!(token.id == TokenId::CurlyBracketClose);
assert!(token.index == 1);
assert!(token.length == 1);
}
}
#[test]
fn test_square_bracket_open() {
{
let code = r#"[]"#;
let tokenization_result = tokenize(code);
let tokens = tokenization_result.unwrap();
assert_eq_code_and_tokens_length(code, &tokens);
assert!(tokens.len() == 2);
let token = &tokens[0];
assert!(token.id == TokenId::SquareBracketOpen);
assert!(token.index == 0);
assert!(token.length == 1);
}
}
#[test]
fn test_suqare_bracket_close() {
{
let code = r#"[]"#;
let tokenization_result = tokenize(code);
let tokens = tokenization_result.unwrap();
assert_eq_code_and_tokens_length(code, &tokens);
assert!(tokens.len() == 2);
let token = &tokens[1];
assert!(token.id == TokenId::SquareBracketClose);
assert!(token.index == 1);
assert!(token.length == 1);
}
}
#[test]
fn test_string_normal() {
{
let code = r#""hallo""#;
let tokenization_result = tokenize(code);
let tokens = tokenization_result.unwrap();
assert_eq_code_and_tokens_length(code, &tokens);
assert!(tokens.len() == 1);
let token = &tokens[0];
assert!(token.id == TokenId::StringNormal);
assert!(token.index == 0);
assert!(token.length == 7);
}
}
#[test]
fn test_string_extended() {
{
let code = r#"""".hallo.""""#;
let tokenization_result = tokenize(code);
let tokens = tokenization_result.unwrap();
assert_eq_code_and_tokens_length(code, &tokens);
assert!(tokens.len() == 1);
let token = &tokens[0];
assert!(token.id == TokenId::StringExtended);
assert!(token.index == 0);
assert!(token.length == 13);
}
{
let code = r#"""",hallo.""""#;
let tokenization_result = tokenize(code);
let tokens = tokenization_result.unwrap();
assert_eq_code_and_tokens_length(code, &tokens);
assert!(tokens.len() == 1);
let token = &tokens[0];
assert!(token.id == TokenId::StringExtended);
assert!(token.index == 0);
assert!(token.length == 13);
}
{
let code = r#"""".hallo,""""#;
let tokenization_result = tokenize(code);
let tokens = tokenization_result.unwrap();
assert_eq_code_and_tokens_length(code, &tokens);
assert!(tokens.len() == 1);
let token = &tokens[0];
assert!(token.id == TokenId::StringExtended);
assert!(token.index == 0);
assert!(token.length == 13);
}
{
let code = r#"""",hallo,""""#;
let tokenization_result = tokenize(code);
let tokens = tokenization_result.unwrap();
assert_eq_code_and_tokens_length(code, &tokens);
assert!(tokens.len() == 1);
let token = &tokens[0];
assert!(token.id == TokenId::StringExtended);
assert!(token.index == 0);
assert!(token.length == 13);
}
}
#[test]
fn test_comment_single_line() {
{
let code = r#";"#;
let tokenization_result = tokenize(code);
let tokens = tokenization_result.unwrap();
assert_eq_code_and_tokens_length(code, &tokens);
assert!(tokens.len() == 1);
let token = &tokens[0];
assert!(token.id == TokenId::CommentSingleLine);
assert!(token.index == 0);
assert!(token.length == 1);
}
{
let code = r#"; "#;
let tokenization_result = tokenize(code);
let tokens = tokenization_result.unwrap();
assert_eq_code_and_tokens_length(code, &tokens);
assert!(tokens.len() == 1);
let token = &tokens[0];
assert!(token.id == TokenId::CommentSingleLine);
assert!(token.index == 0);
assert!(token.length == 2);
}
{
let code = r#";a"#;
let tokenization_result = tokenize(code);
let tokens = tokenization_result.unwrap();
assert_eq_code_and_tokens_length(code, &tokens);
assert!(tokens.len() == 1);
let token = &tokens[0];
assert!(token.id == TokenId::CommentSingleLine);
assert!(token.index == 0);
assert!(token.length == 2);
}
{
let code = r#"; a"#;
let tokenization_result = tokenize(code);
let tokens = tokenization_result.unwrap();
assert_eq_code_and_tokens_length(code, &tokens);
assert!(tokens.len() == 1);
let token = &tokens[0];
assert!(token.id == TokenId::CommentSingleLine);
assert!(token.index == 0);
assert!(token.length == 3);
}
{
let code = r#";;"#;
let tokenization_result = tokenize(code);
let tokens = tokenization_result.unwrap();
assert_eq_code_and_tokens_length(code, &tokens);
assert!(tokens.len() == 1);
let token = &tokens[0];
assert!(token.id == TokenId::CommentSingleLine);
assert!(token.index == 0);
assert!(token.length == 2);
}
{
let code = r#";;;"#;
let tokenization_result = tokenize(code);
let tokens = tokenization_result.unwrap();
assert_eq_code_and_tokens_length(code, &tokens);
assert!(tokens.len() == 1);
let token = &tokens[0];
assert!(token.id == TokenId::CommentSingleLine);
assert!(token.index == 0);
assert!(token.length == 3);
}
}
#[test]
fn test_comment_multi_line_1() {
assert_code_is_ok(";();");
}
#[test]
fn test_comment_multi_line_2() {
assert_code_is_not_ok(";;();");
}
#[test]
fn test_comment_multi_line_3() {
assert_code_is_not_ok(";();;");
}
#[test]
fn test_comment_multi_line_4() {
assert_code_is_not_ok(";(;;();");
}
#[test]
fn test_comment_multi_line_5() {
let code = r#";();"#;
let tokenization_result = tokenize(code);
let tokens = tokenization_result.unwrap();
assert_eq_code_and_tokens_length(code, &tokens);
assert!(tokens.len() == 1);
let token = &tokens[0];
assert!(token.id == TokenId::CommentMultiLine);
assert!(token.index == 0);
assert!(token.length == 4);
}
#[test]
fn test_comment_multi_line_6() {
let code = r#" ;(;();); "#;
let tokenization_result = tokenize(code);
let tokens = tokenization_result.unwrap();
assert_eq_code_and_tokens_length(code, &tokens);
assert!(tokens.len() == 3);
let token = &tokens[1];
assert!(token.id == TokenId::CommentMultiLine);
assert!(token.index == 1);
assert!(token.length == 8);
}
#[test]
fn test_comment_multi_line_7() {
let code = r#";(;(););"#;
let tokenization_result = tokenize(code);
let tokens = tokenization_result.unwrap();
assert_eq_code_and_tokens_length(code, &tokens);
assert!(tokens.len() == 1);
let token = &tokens[0];
assert!(token.id == TokenId::CommentMultiLine);
assert!(token.index == 0);
assert!(token.length == 8);
}
#[test]
fn test_comment_multi_line_8() {
let code = r#" ;(;(););"#;
let tokenization_result = tokenize(code);
let tokens = tokenization_result.unwrap();
assert_eq_code_and_tokens_length(code, &tokens);
assert!(tokens.len() == 2);
let token = &tokens[1];
assert!(token.id == TokenId::CommentMultiLine);
assert!(token.index == 1);
assert!(token.length == 8);
}
#[test]
fn test_comment_multi_line_9() {
let code = r#";(;();); "#;
let tokenization_result = tokenize(code);
let tokens = tokenization_result.unwrap();
assert_eq_code_and_tokens_length(code, &tokens);
assert!(tokens.len() == 2);
let token = &tokens[0];
assert!(token.id == TokenId::CommentMultiLine);
assert!(token.index == 0);
assert!(token.length == 8);
}
#[test]
fn test_empty_normal_string() {
let code = r#""""#;
let tokenization_result = tokenize(code);
let tokens = tokenization_result.unwrap();
assert_eq_code_and_tokens_length(code, &tokens);
assert!(tokens.len() == 1);
let token = &tokens[0];
assert!(token.id == TokenId::StringNormal);
assert!(token.index == 0);
assert!(token.length == 2);
}
#[test]
fn test_unterminated_normal_string() {
{
let code = r#"""#;
let tokenization_result = tokenize(code);
match tokenization_result {
Ok(_) => panic!(),
Err(parsing_error) => {
assert_eq!(parsing_error.error_code, ErrorCode::UnterminatedNormalString);
}
}
}
{
let code = r#""a"#;
let tokenization_result = tokenize(code);
match tokenization_result {
Ok(_) => panic!(),
Err(parsing_error) => {
assert_eq!(parsing_error.error_code, ErrorCode::UnterminatedNormalString);
}
}
}
{
let code = r#""a\"#;
let tokenization_result = tokenize(code);
match tokenization_result {
Ok(_) => panic!(),
Err(parsing_error) => {
assert_eq!(parsing_error.error_code, ErrorCode::UnterminatedNormalString);
}
}
}
{
let code = r#""a\""#;
let tokenization_result = tokenize(code);
match tokenization_result {
Ok(_) => panic!(),
Err(parsing_error) => {
assert_eq!(parsing_error.error_code, ErrorCode::UnterminatedNormalString);
}
}
}
{
let tokenization_result = tokenize(r#""a\"c"#);
match tokenization_result {
Ok(_) => panic!(),
Err(parsing_error) => {
assert_eq!(parsing_error.error_code, ErrorCode::UnterminatedNormalString);
}
}
}
}
#[test]
fn test_unterminated_extended_string() {
{
let code = r#"""""#;
let tokenization_result = tokenize(code);
match tokenization_result {
Ok(_) => panic!(),
Err(parsing_error) => {
assert_eq!(parsing_error.error_code, ErrorCode::UnterminatedExtendedString);
}
}
}
{
let code = r#""""."#;
let tokenization_result = tokenize(code);
match tokenization_result {
Ok(_) => panic!(),
Err(parsing_error) => {
assert_eq!(parsing_error.error_code, ErrorCode::UnterminatedExtendedString);
}
}
}
{
let code = r#"""".""#;
let tokenization_result = tokenize(code);
match tokenization_result {
Ok(_) => panic!(),
Err(parsing_error) => {
assert_eq!(parsing_error.error_code, ErrorCode::UnterminatedExtendedString);
}
}
}
{
let code = r#""""."""#;
let tokenization_result = tokenize(code);
match tokenization_result {
Ok(_) => panic!(),
Err(parsing_error) => {
assert_eq!(parsing_error.error_code, ErrorCode::UnterminatedExtendedString);
}
}
}
}
}