hexput-ast-api 0.1.6

Core AST parsing and processing library
Documentation
use serde::Serialize;

#[derive(Debug, Clone, Copy, Serialize)]
pub struct SourceLocation {
    pub start_line: usize,
    pub start_column: usize,
    pub end_line: usize,
    pub end_column: usize,
}

impl SourceLocation {
    pub fn new(start_line: usize, start_column: usize, end_line: usize, end_column: usize) -> Self {
        Self {
            start_line,
            start_column,
            end_line,
            end_column,
        }
    }

    pub fn from_spans(source_code: &str, start_offset: usize, end_offset: usize) -> Self {
        let (start_line, start_column) = get_line_column(source_code, start_offset);
        let (end_line, end_column) = get_line_column(source_code, end_offset);
        Self::new(start_line, start_column, end_line, end_column)
    }
}


fn get_line_column(source: &str, offset: usize) -> (usize, usize) {
    let mut line = 1;
    let mut column = 1;
    
    for (i, ch) in source.chars().enumerate() {
        if i >= offset {
            break;
        }
        
        if ch == '\n' {
            line += 1;
            column = 1;
        } else {
            column += 1;
        }
    }
    
    (line, column)
}


#[derive(Debug, Clone, Copy, Serialize)]
pub struct Span {
    pub start: usize,
    pub end: usize,
}

#[derive(Debug, Clone, Serialize)]
pub struct Program {
    #[serde(rename = "type")]
    pub node_type: String,
    pub statements: Vec<Statement>,
    pub location: SourceLocation,
}

impl Program {
    pub fn new(statements: Vec<Statement>, location: SourceLocation) -> Self {
        Self {
            node_type: "PROGRAM".to_string(),
            statements,
            location,
        }
    }
}

#[derive(Debug, Clone, Serialize)]
#[serde(tag = "type")]
pub enum Statement {
    #[serde(rename = "VARIABLE_DECLARATION")]
    VariableDeclaration {
        name: String,
        value: Expression,
        location: SourceLocation,
    },
    #[serde(rename = "EXPRESSION_STATEMENT")]
    ExpressionStatement {
        expression: Expression,
        location: SourceLocation,
    },
    #[serde(rename = "IF_STATEMENT")]
    IfStatement {
        condition: Expression,
        body: Block,
        #[serde(skip_serializing_if = "Option::is_none")]
        else_body: Option<Block>,
        location: SourceLocation,
    },
    #[serde(rename = "BLOCK")]
    Block { 
        block: Block,
        location: SourceLocation,
    },
    #[serde(rename = "CALLBACK_DECLARATION")]
    CallbackDeclaration {
        name: String,
        params: Vec<String>,
        body: Block,
        location: SourceLocation,
    },
    #[serde(rename = "RETURN_STATEMENT")]
    ReturnStatement {
        value: Expression,
        location: SourceLocation,
    },
    #[serde(rename = "LOOP_STATEMENT")]
    LoopStatement {
        variable: String,
        iterable: Expression,
        body: Block,
        location: SourceLocation,
    },
    #[serde(rename = "END_STATEMENT")]
    EndStatement {
        location: SourceLocation,
    },
    #[serde(rename = "CONTINUE_STATEMENT")]
    ContinueStatement {
        location: SourceLocation,
    },
}

#[derive(Debug, Clone, Serialize)]
pub struct Block {
    #[serde(rename = "type")]
    pub node_type: String,
    pub statements: Vec<Statement>,
    pub location: SourceLocation,
}

impl Block {
    pub fn new(statements: Vec<Statement>, location: SourceLocation) -> Self {
        Self {
            node_type: "BLOCK".to_string(),
            statements,
            location,
        }
    }
}

#[derive(Debug, Clone, Serialize)]
#[serde(tag = "type")]
pub enum Expression {
    #[serde(rename = "STRING_LITERAL")]
    StringLiteral {
        value: String,
        location: SourceLocation,
    },
    #[serde(rename = "NUMBER_LITERAL")]
    NumberLiteral {
        value: f64,
        location: SourceLocation,
    },
    #[serde(rename = "IDENTIFIER")]
    Identifier {
        name: String,
        location: SourceLocation,
    },
    #[serde(rename = "BINARY_EXPRESSION")]
    BinaryExpression {
        left: Box<Expression>,
        operator: Operator,
        right: Box<Expression>,
        location: SourceLocation,
    },
    #[serde(rename = "ASSIGNMENT_EXPRESSION")]
    AssignmentExpression {
        target: String,
        value: Box<Expression>,
        location: SourceLocation,
    },
    #[serde(rename = "MEMBER_ASSIGNMENT_EXPRESSION")]
    MemberAssignmentExpression {
        object: Box<Expression>,
        #[serde(skip_serializing_if = "Option::is_none")]
        property: Option<String>,
        #[serde(skip_serializing_if = "Option::is_none")]
        property_expr: Option<Box<Expression>>,
        computed: bool,  
        value: Box<Expression>,
        location: SourceLocation,
    },
    #[serde(rename = "CALL_EXPRESSION")]
    CallExpression {
        callee: String,
        arguments: Vec<Expression>,
        location: SourceLocation,
    },
    #[serde(rename = "MEMBER_CALL_EXPRESSION")]
    MemberCallExpression {
        object: Box<Expression>,
        #[serde(skip_serializing_if = "Option::is_none")]
        property: Option<String>,
        #[serde(skip_serializing_if = "Option::is_none")]
        property_expr: Option<Box<Expression>>,
        computed: bool,
        arguments: Vec<Expression>,
        location: SourceLocation,
    },
    #[serde(rename = "INLINE_CALLBACK_EXPRESSION")]
    InlineCallbackExpression {
        name: String,
        params: Vec<String>,
        body: Block,
        location: SourceLocation,
    },
    #[serde(rename = "ARRAY_EXPRESSION")]
    ArrayExpression {
        elements: Vec<Expression>,
        location: SourceLocation,
    },
    #[serde(rename = "OBJECT_EXPRESSION")]
    ObjectExpression {
        properties: Vec<Property>,
        location: SourceLocation,
    },
    #[serde(rename = "MEMBER_EXPRESSION")]
    MemberExpression {
        object: Box<Expression>,
        #[serde(skip_serializing_if = "Option::is_none")]
        property: Option<String>,
        #[serde(skip_serializing_if = "Option::is_none")]
        property_expr: Option<Box<Expression>>,
        computed: bool,  
        location: SourceLocation,
    },
    #[serde(rename = "KEYS_OF_EXPRESSION")]
    KeysOfExpression {
        object: Box<Expression>,
        location: SourceLocation,
    },
    #[serde(rename = "BOOLEAN_LITERAL")]
    BooleanLiteral {
        value: bool,
        location: SourceLocation,
    },
    #[serde(rename = "UNARY_EXPRESSION")]
    UnaryExpression {
        operator: UnaryOperator,
        operand: Box<Expression>,
        location: SourceLocation,
    },
    #[serde(rename = "NULL_LITERAL")]
    NullLiteral {
        location: SourceLocation,
    },
}

#[derive(Debug, Clone, Serialize)]
pub struct Property {
    #[serde(rename = "type")]
    pub node_type: String,
    pub key: String,
    pub value: Expression,
    pub location: SourceLocation,
}

impl Property {
    pub fn new(key: String, value: Expression, location: SourceLocation) -> Self {
        Self {
            node_type: "PROPERTY".to_string(),
            key,
            value,
            location,
        }
    }
}

#[derive(Debug, Clone, PartialEq, Serialize)]
pub enum Operator {
    Equal,
    NotEqual,
    Plus,
    Minus,
    Multiply,
    Divide,
    Greater,
    Less,
    GreaterEqual,
    LessEqual,
    And,
    Or,
}

#[derive(Debug, Clone, PartialEq, Serialize)]
pub enum UnaryOperator {
    Not,
}