use crate::codegen::ast_util; use crate::codegen::syscall_map; use anyhow::{anyhow, Result}; use oxc_ast::ast::*; use oxc_ast_visit::{walk, Visit}; use oxc_span::GetSpan; /// AST Visitor that ensures the source code follows the Prometeu subset of JS/TS. /// /// Since the Prometeu Virtual Machine is highly optimized and focused on game logic, /// it does not support the full ECMA-262 specification (e.g., no `async`, `class`, /// `try/catch`, or `generators`). pub struct Validator { /// List of validation errors found during the pass. errors: Vec, /// Set of function names defined in the project, used to distinguish /// local calls from invalid references. local_functions: std::collections::HashSet, } impl Validator { /// Performs a full validation pass on a program. /// /// Returns `Ok(())` if the program is valid, or a combined error message /// listing all violations. pub fn validate(program: &Program) -> Result<()> { let mut validator = Self { errors: Vec::new(), local_functions: std::collections::HashSet::new(), }; // 1. Discovery Pass: Collect all function names and imports for item in &program.body { match item { Statement::FunctionDeclaration(f) => { if let Some(ident) = &f.id { validator.local_functions.insert(ident.name.to_string()); } } Statement::ExportNamedDeclaration(decl) => { if let Some(Declaration::FunctionDeclaration(f)) = &decl.declaration { if let Some(ident) = &f.id { validator.local_functions.insert(ident.name.to_string()); } } } Statement::ImportDeclaration(decl) => { if let Some(specifiers) = &decl.specifiers { for specifier in specifiers { match specifier { ImportDeclarationSpecifier::ImportSpecifier(s) => { validator.local_functions.insert(s.local.name.to_string()); } ImportDeclarationSpecifier::ImportDefaultSpecifier(s) => { validator.local_functions.insert(s.local.name.to_string()); } ImportDeclarationSpecifier::ImportNamespaceSpecifier(s) => { validator.local_functions.insert(s.local.name.to_string()); } } } } } _ => {} } } // 2. Traversal Pass: Check every node for compatibility validator.visit_program(program); if validator.errors.is_empty() { Ok(()) } else { Err(anyhow!("Validation errors:\n{}", validator.errors.join("\n"))) } } } impl<'a> Visit<'a> for Validator { /// Validates that only supported statements are used. fn visit_statement(&mut self, stmt: &Statement<'a>) { match stmt { Statement::VariableDeclaration(_) | Statement::ExpressionStatement(_) | Statement::IfStatement(_) | Statement::BlockStatement(_) | Statement::ExportNamedDeclaration(_) | Statement::ImportDeclaration(_) | Statement::FunctionDeclaration(_) => { // These are the only statements the PVM handles currently. walk::walk_statement(self, stmt); } _ => { self.errors.push(format!("Unsupported statement type at {:?}. Note: Prometeu does not support while/for loops or classes yet.", stmt.span())); } } } /// Validates that only supported expressions are used. fn visit_expression(&mut self, expr: &Expression<'a>) { match expr { Expression::NumericLiteral(_) | Expression::BooleanLiteral(_) | Expression::StringLiteral(_) | Expression::NullLiteral(_) | Expression::Identifier(_) | Expression::AssignmentExpression(_) | Expression::BinaryExpression(_) | Expression::LogicalExpression(_) | Expression::UnaryExpression(_) | Expression::CallExpression(_) | Expression::StaticMemberExpression(_) => { // Basic JS logic is supported. walk::walk_expression(self, expr); } _ => { self.errors.push(format!("Unsupported expression type at {:?}. Note: Closures, arrow functions, and object literals are not yet supported.", expr.span())); } } } fn visit_unary_expression(&mut self, expr: &UnaryExpression<'a>) { match expr.operator { UnaryOperator::UnaryNegation | UnaryOperator::UnaryPlus | UnaryOperator::LogicalNot => { walk::walk_unary_expression(self, expr); } _ => { self.errors.push(format!("Unsupported unary operator {:?} at {:?}", expr.operator, expr.span)); } } } fn visit_binary_expression(&mut self, expr: &BinaryExpression<'a>) { match expr.operator { BinaryOperator::Addition | BinaryOperator::Subtraction | BinaryOperator::Multiplication | BinaryOperator::Division | BinaryOperator::Equality | BinaryOperator::Inequality | BinaryOperator::LessThan | BinaryOperator::GreaterThan | BinaryOperator::LessEqualThan | BinaryOperator::GreaterEqualThan => { walk::walk_binary_expression(self, expr); } _ => { self.errors.push(format!("Unsupported binary operator {:?} at {:?}", expr.operator, expr.span)); } } } fn visit_call_expression(&mut self, expr: &CallExpression<'a>) { if let Ok(name) = ast_util::get_callee_name(&expr.callee) { if syscall_map::map_syscall(&name).is_none() && !self.local_functions.contains(&name) { self.errors.push(format!("Unsupported function call: {} at {:?}", name, expr.span)); } } else { self.errors.push(format!("Unsupported callee expression at {:?}", expr.callee.span())); } walk::walk_call_expression(self, expr); } fn visit_logical_expression(&mut self, expr: &LogicalExpression<'a>) { match expr.operator { LogicalOperator::And | LogicalOperator::Or => { walk::walk_logical_expression(self, expr); } _ => { self.errors.push(format!("Unsupported logical operator {:?} at {:?}", expr.operator, expr.span)); } } } }