ghostscope_compiler/script/
ast.rs

1#[derive(Debug, Clone)]
2pub enum Expr {
3    Int(i64),
4    Float(f64),
5    String(String),
6    Bool(bool),
7    UnaryNot(Box<Expr>),
8    Variable(String),
9    MemberAccess(Box<Expr>, String),   // person.name
10    PointerDeref(Box<Expr>),           // *ptr
11    AddressOf(Box<Expr>),              // &expr
12    ArrayAccess(Box<Expr>, Box<Expr>), // arr[0] (new)
13    ChainAccess(Vec<String>),          // person.name.first (new)
14    SpecialVar(String),                // For $arg0, $arg1, $retval, $pc, $sp etc.
15    // Builtin function call, e.g., strncmp(expr, "lit", n), starts_with(expr, "lit")
16    BuiltinCall {
17        name: String,
18        args: Vec<Expr>,
19    },
20    BinaryOp {
21        left: Box<Expr>,
22        op: BinaryOp,
23        right: Box<Expr>,
24    },
25}
26
27#[derive(Debug, Clone, PartialEq)]
28pub enum BinaryOp {
29    Add,
30    Subtract,
31    Multiply,
32    Divide,
33    // Comparison operators
34    Equal,
35    NotEqual,
36    LessThan,
37    LessEqual,
38    GreaterThan,
39    GreaterEqual,
40    // Logical operators
41    LogicalAnd,
42    LogicalOr,
43}
44
45#[derive(Debug, Clone, PartialEq)]
46pub enum VarType {
47    Int,
48    Float,
49    String,
50    Bool,
51}
52
53#[derive(Debug, Clone)]
54pub enum Statement {
55    Print(PrintStatement), // Updated to use new PrintStatement
56    Backtrace,
57    Expr(Expr),
58    VarDeclaration {
59        name: String,
60        value: Expr,
61    },
62    /// DWARF alias binding: `let name = <alias_expr>;` where alias_expr is address-of,
63    /// member/array/pointer deref/chain, or alias+constant offset. Resolved at use time.
64    AliasDeclaration {
65        name: String,
66        target: Expr,
67    },
68    TracePoint {
69        pattern: TracePattern,
70        body: Vec<Statement>,
71    },
72    If {
73        condition: Expr,
74        then_body: Vec<Statement>,
75        else_body: Option<Box<Statement>>,
76    },
77    Block(Vec<Statement>),
78}
79
80/// Print statement variants for new instruction system
81#[derive(Debug, Clone)]
82pub enum PrintStatement {
83    /// print "hello world"
84    String(String),
85    /// print variable_name
86    Variable(String),
87    /// print person.name or arr[0] (new: support complex expressions)
88    ComplexVariable(Expr),
89    /// print "format {} {}" arg1, arg2
90    Formatted { format: String, args: Vec<Expr> },
91}
92
93#[derive(Debug, Clone)]
94pub enum TracePattern {
95    FunctionName(String), // trace main { ... }
96    Wildcard(String),     // trace printf* { ... }
97    Address(u64),         // trace 0x400000 { ... }
98    AddressInModule {
99        // trace module_suffix:0xADDR { ... }
100        module: String,
101        address: u64,
102    },
103    SourceLine {
104        // trace file.c:123 { ... }
105        file_path: String,
106        line_number: u32,
107    },
108}
109
110/// Variable validation context
111#[derive(Debug, Clone)]
112pub struct VariableContext {
113    pub current_address: Option<u64>,
114    pub available_vars: Vec<String>, // Variables available at current context
115}
116
117impl VariableContext {
118    pub fn new() -> Self {
119        Self {
120            current_address: None,
121            available_vars: vec![
122                // Always available special variables
123                "$arg0".to_string(),
124                "$arg1".to_string(),
125                "$arg2".to_string(),
126                "$arg3".to_string(),
127                "$retval".to_string(),
128                "$pc".to_string(),
129                "$sp".to_string(),
130            ],
131        }
132    }
133
134    pub fn is_variable_available(&self, var_name: &str) -> bool {
135        self.available_vars.contains(&var_name.to_string())
136    }
137
138    pub fn add_variable(&mut self, var_name: String) {
139        if !self.available_vars.contains(&var_name) {
140            self.available_vars.push(var_name);
141        }
142    }
143}
144
145impl Default for VariableContext {
146    fn default() -> Self {
147        Self::new()
148    }
149}
150
151#[derive(Debug, Clone)]
152pub struct Program {
153    pub statements: Vec<Statement>,
154}
155
156impl Program {
157    pub fn new() -> Self {
158        Program {
159            statements: Vec::new(),
160        }
161    }
162
163    pub fn add_statement(&mut self, statement: Statement) {
164        self.statements.push(statement);
165    }
166}
167
168impl Default for Program {
169    fn default() -> Self {
170        Self::new()
171    }
172}
173
174// Add type inference function
175pub fn infer_type(expr: &Expr) -> Result<VarType, String> {
176    match expr {
177        Expr::Int(_) => Ok(VarType::Int),
178        Expr::Float(_) => Ok(VarType::Float),
179        Expr::String(_) => Ok(VarType::String),
180        Expr::Bool(_) => Ok(VarType::Bool),
181        Expr::UnaryNot(_) => Ok(VarType::Bool),
182        // During parsing phase, we cannot know variable types, only check literal expressions
183        // For variable references, return a default type to allow compilation to continue, actual type checking will be done in code generation phase
184        Expr::Variable(_) => Ok(VarType::Int), // Temporarily assume variables are integer type to let parsing pass
185        Expr::MemberAccess(_, _) => Ok(VarType::Int), // Same as above
186        Expr::PointerDeref(_) => Ok(VarType::Int), // Same as above
187        Expr::AddressOf(_) => Ok(VarType::Int), // Address as integer/pointer value for now
188        Expr::ArrayAccess(_, _) => Ok(VarType::Int), // New: array access returns element type (assume int for now)
189        Expr::ChainAccess(_) => Ok(VarType::Int), // New: chain access returns final member type (assume int for now)
190        Expr::SpecialVar(_) => Ok(VarType::Int),  // Special variables like $arg0, $retval etc.
191        Expr::BuiltinCall { name, args: _ } => match name.as_str() {
192            "strncmp" | "starts_with" | "memcmp" => Ok(VarType::Bool),
193            _ => Err(format!("Unknown builtin function: {name}")),
194        },
195        Expr::BinaryOp { left, op, right } => {
196            // Only check types when both sides are literals
197            let left_is_literal = matches!(
198                left.as_ref(),
199                Expr::Int(_) | Expr::Float(_) | Expr::String(_)
200            );
201            let right_is_literal = matches!(
202                right.as_ref(),
203                Expr::Int(_) | Expr::Float(_) | Expr::String(_)
204            );
205
206            if left_is_literal && right_is_literal {
207                let left_type = infer_type(left)?;
208                let right_type = infer_type(right)?;
209
210                if left_type != right_type {
211                    return Err(format!(
212                        "Type mismatch: Cannot perform operation between {left_type:?} and {right_type:?}"
213                    ));
214                }
215
216                // Strings only support addition operation and comparison operations
217                if left_type == VarType::String
218                    && !matches!(*op, BinaryOp::Add | BinaryOp::Equal | BinaryOp::NotEqual)
219                {
220                    return Err(
221                        "String type only supports addition and comparison operations".to_string(),
222                    );
223                }
224
225                // Comparison operations return boolean type
226                if matches!(
227                    *op,
228                    BinaryOp::Equal
229                        | BinaryOp::NotEqual
230                        | BinaryOp::LessThan
231                        | BinaryOp::LessEqual
232                        | BinaryOp::GreaterThan
233                        | BinaryOp::GreaterEqual
234                ) {
235                    return Ok(VarType::Bool);
236                }
237
238                // Logical operations return boolean; allow Int literals as truthy (non-zero)
239                if matches!(*op, BinaryOp::LogicalAnd | BinaryOp::LogicalOr) {
240                    match (left_type, right_type) {
241                        (VarType::Bool, VarType::Bool)
242                        | (VarType::Bool, VarType::Int)
243                        | (VarType::Int, VarType::Bool)
244                        | (VarType::Int, VarType::Int) => return Ok(VarType::Bool),
245                        _ => {
246                            return Err("Logical operations require boolean or integer operands"
247                                .to_string())
248                        }
249                    }
250                }
251
252                Ok(left_type)
253            } else {
254                // If there are variable references, assume type compatibility to let parsing pass
255                // Actual type checking will be done in code generation phase
256                if matches!(*op, BinaryOp::LogicalAnd | BinaryOp::LogicalOr)
257                    || matches!(
258                        *op,
259                        BinaryOp::Equal
260                            | BinaryOp::NotEqual
261                            | BinaryOp::LessThan
262                            | BinaryOp::LessEqual
263                            | BinaryOp::GreaterThan
264                            | BinaryOp::GreaterEqual
265                    )
266                {
267                    Ok(VarType::Bool)
268                } else {
269                    Ok(VarType::Int)
270                }
271            }
272        }
273    }
274}