178 lines
7.0 KiB
Rust

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<String>,
/// Set of function names defined in the project, used to distinguish
/// local calls from invalid references.
local_functions: std::collections::HashSet<String>,
}
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));
}
}
}
}