use galvan_ast::{
BooleanLiteral, CharLiteral, Expression, ExpressionKind, Ident, Literal, NoneLiteral, NumberLiteral, Span, StringLiteral
};
use galvan_parse::TreeCursor;
use crate::{cursor_expect, result::CursorUtil, AstError, ReadCursor, SpanExt};
fn parse_interpolation_expression(expr_content: &str, span: &Span) -> Result<Expression, AstError> {
let wrapper_source = format!("fn __temp() {{ let __x = {}; }}", expr_content);
let source = galvan_files::Source::Str(wrapper_source.clone().into());
match galvan_parse::parse_source(&source) {
Ok(parsed_tree) => {
let mut cursor = parsed_tree.root_node().walk();
if cursor.child() && cursor.child() && cursor_goto_named(&mut cursor, "body") &&
cursor.child() && cursor.child() && cursor_goto_named(&mut cursor, "declaration") &&
cursor.child() && cursor_goto_named(&mut cursor, "expression") {
match Expression::read_cursor(&mut cursor, &wrapper_source) {
Ok(expr) => Ok(expr),
Err(_) => {
create_fallback_expression(expr_content, span)
}
}
} else {
create_fallback_expression(expr_content, span)
}
}
Err(_) => {
create_fallback_expression(expr_content, span)
}
}
}
fn cursor_goto_named(cursor: &mut TreeCursor<'_>, name: &str) -> bool {
loop {
if cursor.kind().unwrap_or("") == name {
return true;
}
if !cursor.goto_next_sibling() {
return false;
}
}
}
fn create_fallback_expression(expr_content: &str, span: &Span) -> Result<Expression, AstError> {
if expr_content.chars().all(|c| c.is_alphanumeric() || c == '_') {
Ok(Expression {
kind: ExpressionKind::Ident(Ident::new(expr_content)),
span: span.clone(),
})
} else {
Ok(Expression {
kind: ExpressionKind::Ident(Ident::new(expr_content)),
span: span.clone(),
})
}
}
impl ReadCursor for Literal {
fn read_cursor(cursor: &mut TreeCursor<'_>, source: &str) -> Result<Self, AstError> {
let node = cursor_expect!(cursor, "literal");
let span = Span::from_node(node);
cursor.child();
let inner = match cursor.kind()? {
"none_keyword" => Literal::NoneLiteral(NoneLiteral(span)),
"boolean_literal" => BooleanLiteral::read_cursor(cursor, source)?.into(),
"string_literal" => StringLiteral::read_cursor(cursor, source)?.into(),
"char_literal" => CharLiteral::read_cursor(cursor, source)?.into(),
"number_literal" => NumberLiteral::read_cursor(cursor, source)?.into(),
unknown => unreachable!("Unknown literal type: {unknown}"),
};
cursor.goto_parent();
Ok(inner)
}
}
impl ReadCursor for BooleanLiteral {
fn read_cursor(cursor: &mut TreeCursor<'_>, _source: &str) -> Result<Self, AstError> {
let node = cursor_expect!(cursor, "boolean_literal");
let span = Span::from_node(node);
cursor.child();
let lit = match cursor.kind()? {
"true_keyword" => BooleanLiteral { value: true, span },
"false_keyword" => BooleanLiteral { value: false, span },
_ => unreachable!(),
};
cursor.goto_parent();
Ok(lit)
}
}
impl ReadCursor for NumberLiteral {
fn read_cursor(cursor: &mut TreeCursor<'_>, source: &str) -> Result<Self, AstError> {
let node = cursor_expect!(cursor, "number_literal");
let span = Span::from_node(node);
let value = source[node.start_byte()..node.end_byte()].to_owned();
Ok(NumberLiteral { value, span })
}
}
impl ReadCursor for StringLiteral {
fn read_cursor(cursor: &mut TreeCursor<'_>, source: &str) -> Result<Self, AstError> {
let node = cursor_expect!(cursor, "string_literal");
let span = Span::from_node(node);
let full_text = source[node.start_byte()..node.end_byte()].to_owned();
let mut interpolations = Vec::new();
if full_text.contains('{') && full_text.contains('}') {
let mut template = String::new();
let mut chars = full_text.chars().peekable();
let mut placeholder_index = 0;
if chars.peek() == Some(&'"') {
chars.next();
template.push('"');
}
while let Some(ch) = chars.next() {
if ch == '{' {
let mut expr_content = String::new();
let mut brace_depth = 1;
while let Some(inner_ch) = chars.next() {
if inner_ch == '{' {
brace_depth += 1;
} else if inner_ch == '}' {
brace_depth -= 1;
if brace_depth == 0 {
break;
}
}
expr_content.push(inner_ch);
}
let expr = parse_interpolation_expression(&expr_content, &span)?;
interpolations.push(expr);
template.push_str(&format!("{{{}}}", placeholder_index));
placeholder_index += 1;
} else {
template.push(ch);
}
}
Ok(Self {
value: template,
interpolations,
span,
})
} else {
Ok(Self {
value: full_text,
interpolations,
span,
})
}
}
}
impl ReadCursor for CharLiteral {
fn read_cursor(cursor: &mut TreeCursor<'_>, source: &str) -> Result<Self, AstError> {
let node = cursor_expect!(cursor, "char_literal");
let span = Span::from_node(node);
let text = &source[node.start_byte()..node.end_byte()];
let char_content = &text[1..text.len()-1];
let value = if char_content.starts_with('\\') {
match char_content {
"\\n" => '\n',
"\\r" => '\r',
"\\t" => '\t',
"\\\\" => '\\',
"\\'" => '\'',
"\\\"" => '"',
_ if char_content.starts_with("\\u{") && char_content.ends_with('}') => {
let hex_str = &char_content[3..char_content.len()-1];
let code_point = u32::from_str_radix(hex_str, 16)
.map_err(|_| AstError::InvalidCharacterLiteral(span))?;
char::from_u32(code_point)
.ok_or(AstError::InvalidCharacterLiteral(span))?
}
_ => return Err(AstError::InvalidCharacterLiteral(span)),
}
} else {
char_content.chars().next()
.ok_or(AstError::InvalidCharacterLiteral(span))?
};
Ok(CharLiteral { value, span })
}
}