lamina 0.0.10

High-performance compiler backend for Lamina Intermediate Representation
Documentation
//! Global variable parsing for Lamina IR.

use super::state::ParserState;
use super::types::parse_type;
use crate::{GlobalDeclaration, LaminaError, Literal, PrimitiveType, Type, Value};

/// Parses a global declaration.
pub fn parse_global_declaration<'a>(
    state: &mut ParserState<'a>,
) -> Result<GlobalDeclaration<'a>, LaminaError> {
    state.consume_keyword("global")?;
    let name = state.parse_type_identifier()?;
    state.expect_char(':')?;
    let ty = parse_type(state)?;

    state.skip_whitespace_and_comments();
    let initializer = if state.current_char() == Some('=') {
        state.advance();
        let value = parse_value_with_type_hint(state, &ty)?;
        Some(value)
    } else {
        None
    };

    Ok(GlobalDeclaration {
        name,
        ty,
        initializer,
    })
}

/// Parses a value using type hints for integer literals.
pub fn parse_value_with_type_hint<'a>(
    state: &mut ParserState<'a>,
    type_hint: &Type<'a>,
) -> Result<Value<'a>, LaminaError> {
    let start_pos = state.position();
    state.skip_whitespace_and_comments();

    match state.current_char() {
        Some('%') => {
            state.advance();
            let name = state.parse_identifier_str()?;
            Ok(Value::Variable(name))
        }
        Some('@') => {
            state.advance();
            let name = state.parse_identifier_str()?;
            Ok(Value::Global(name))
        }
        Some('"') => {
            let string_value = state.parse_string_literal()?;

            match type_hint {
                Type::Array { element_type, .. } => match element_type.as_ref() {
                    Type::Primitive(PrimitiveType::I8) | Type::Primitive(PrimitiveType::Bool) => {
                        Ok(Value::Constant(Literal::String(string_value)))
                    }
                    _ => Err(state.error(format!(
                        "String literal is not compatible with type hint: {:?}",
                        type_hint
                    ))),
                },
                _ => Err(state.error(format!(
                    "String literal is not compatible with type hint: {:?}",
                    type_hint
                ))),
            }
        }
        Some('t') => {
            if state.peek_slice(4) == Some("true") {
                state.advance_by(4);

                match type_hint {
                    Type::Primitive(PrimitiveType::Bool) => {
                        Ok(Value::Constant(Literal::Bool(true)))
                    }
                    _ => Err(state.error(format!(
                        "Boolean literal not compatible with type hint: {:?}",
                        type_hint
                    ))),
                }
            } else {
                let found = state.peek_slice(10).unwrap_or("");
                let mut suggestions = Vec::new();
                const MAX_TYPO_DISTANCE: usize = 2;

                let distance = super::edit_distance(found, "true", Some(MAX_TYPO_DISTANCE));
                if distance <= MAX_TYPO_DISTANCE {
                    suggestions.push("true");
                }

                let hint = if !suggestions.is_empty() {
                    "Did you mean 'true'?".to_string()
                } else {
                    "Expected 'true' boolean literal".to_string()
                };

                Err(state.error(format!("Expected 'true'\n  Hint: {}", hint)))
            }
        }
        Some('f') => {
            if state.peek_slice(5) == Some("false") {
                state.advance_by(5);

                match type_hint {
                    Type::Primitive(PrimitiveType::Bool) => {
                        Ok(Value::Constant(Literal::Bool(false)))
                    }
                    _ => Err(state.error(format!(
                        "Boolean literal not compatible with type hint: {:?}",
                        type_hint
                    ))),
                }
            } else {
                let found = state.peek_slice(10).unwrap_or("");
                let mut suggestions = Vec::new();
                const MAX_TYPO_DISTANCE: usize = 2;

                let distance = super::edit_distance(found, "false", Some(MAX_TYPO_DISTANCE));
                if distance <= MAX_TYPO_DISTANCE {
                    suggestions.push("false");
                }

                let hint = if !suggestions.is_empty() {
                    "Did you mean 'false'?".to_string()
                } else {
                    "Expected 'false' boolean literal".to_string()
                };

                Err(state.error(format!("Expected 'false'\n  Hint: {}", hint)))
            }
        }
        Some(c) if c.is_ascii_digit() || c == '-' => {
            if matches!(type_hint, Type::Primitive(PrimitiveType::F32)) {
                if let Ok(f_val) = state.parse_float() {
                    return Ok(Value::Constant(Literal::F32(f_val)));
                }

                state.set_position(start_pos);
                if let Ok(i_val) = state.parse_integer() {
                    return Ok(Value::Constant(Literal::F32(i_val as f32)));
                }

                return Err(state.error("Expected float literal for F32 hint".to_string()));
            }

            match type_hint {
                Type::Primitive(PrimitiveType::I8) => {
                    let peek_string = state.peek_slice(20).unwrap_or("");
                    if peek_string.contains('.') {
                        return Err(state
                            .error("Float literal cannot be used with I8 type hint".to_string()));
                    }

                    let i_val = state.parse_integer()?;
                    if i_val < i8::MIN as i64 || i_val > i8::MAX as i64 {
                        return Err(state.error(format!(
                            "Integer literal {} out of range for i8 (must be between {} and {})",
                            i_val,
                            i8::MIN,
                            i8::MAX
                        )));
                    }
                    Ok(Value::Constant(Literal::I8(i_val as i8)))
                }
                Type::Primitive(PrimitiveType::I32) => {
                    let peek_string = state.peek_slice(20).unwrap_or("");
                    if peek_string.contains('.') {
                        return Err(state
                            .error("Float literal cannot be used with I32 type hint".to_string()));
                    }

                    let i_val = state.parse_integer()?;
                    if i_val < i32::MIN as i64 || i_val > i32::MAX as i64 {
                        return Err(state.error(format!(
                            "Integer literal {} out of range for i32 (must be between {} and {})",
                            i_val,
                            i32::MIN,
                            i32::MAX
                        )));
                    }
                    Ok(Value::Constant(Literal::I32(i_val as i32)))
                }
                Type::Primitive(PrimitiveType::I64) => {
                    let peek_string = state.peek_slice(20).unwrap_or("");
                    if peek_string.contains('.') {
                        return Err(state
                            .error("Float literal cannot be used with I64 type hint".to_string()));
                    }

                    let i_val = state.parse_integer()?;
                    Ok(Value::Constant(Literal::I64(i_val)))
                }
                _ => {
                    state.set_position(start_pos);
                    if let Ok(i_val) = state.parse_integer() {
                        return if i_val >= i32::MIN as i64 && i_val <= i32::MAX as i64 {
                            Ok(Value::Constant(Literal::I32(i_val as i32)))
                        } else {
                            Ok(Value::Constant(Literal::I64(i_val)))
                        };
                    }

                    state.set_position(start_pos);
                    if let Ok(f_val) = state.parse_float() {
                        return Ok(Value::Constant(Literal::F32(f_val)));
                    }

                    Err(state.error("Expected a numeric literal".to_string()))
                }
            }
        }
        _ => Err(state.error("Expected value (%, @, literal)".to_string())),
    }
}