feat: add debug print, open & events
- change: equality - update codegen Signed-off-by: Pakin <pakin.t@forth.co.th>
This commit is contained in:
parent
f9ae59c77f
commit
41cc051b71
10 changed files with 245 additions and 3506 deletions
|
|
@ -20,6 +20,16 @@ pub enum Statement {
|
||||||
iterable: Expression,
|
iterable: Expression,
|
||||||
body: Vec<Statement>,
|
body: Vec<Statement>,
|
||||||
},
|
},
|
||||||
|
DebugVar {
|
||||||
|
name: String,
|
||||||
|
},
|
||||||
|
Open {
|
||||||
|
filename: String,
|
||||||
|
},
|
||||||
|
Call {
|
||||||
|
name: String,
|
||||||
|
args: Vec<Expression>,
|
||||||
|
},
|
||||||
ExprStmt {
|
ExprStmt {
|
||||||
expression: Expression,
|
expression: Expression,
|
||||||
},
|
},
|
||||||
|
|
@ -80,6 +90,8 @@ pub enum BinaryOpKind {
|
||||||
Gt,
|
Gt,
|
||||||
Lte,
|
Lte,
|
||||||
Gte,
|
Gte,
|
||||||
|
And,
|
||||||
|
Or,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||||
|
|
|
||||||
|
|
@ -66,6 +66,8 @@ impl CodeGen for JavaCodeGen {
|
||||||
BinaryOpKind::Gt => ">",
|
BinaryOpKind::Gt => ">",
|
||||||
BinaryOpKind::Lte => "<=",
|
BinaryOpKind::Lte => "<=",
|
||||||
BinaryOpKind::Gte => ">=",
|
BinaryOpKind::Gte => ">=",
|
||||||
|
BinaryOpKind::And => "&&",
|
||||||
|
BinaryOpKind::Or => "||",
|
||||||
};
|
};
|
||||||
format!("({} {} {})", left, op_str, right)
|
format!("({} {} {})", left, op_str, right)
|
||||||
}
|
}
|
||||||
|
|
@ -141,4 +143,20 @@ impl CodeGen for JavaCodeGen {
|
||||||
fn generate_string_concat(&self, parts: &[String]) -> String {
|
fn generate_string_concat(&self, parts: &[String]) -> String {
|
||||||
parts.join(" + ")
|
parts.join(" + ")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn generate_debug_var(&self, name: &str) -> String {
|
||||||
|
format!("{}System.out.println(\"{} = \" + {});", self.indent_str(), name, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn generate_open(&self, filename: &str) -> String {
|
||||||
|
format!("{}// open(\"{}\")", self.indent_str(), filename)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn generate_call(&self, name: &str, args: &[String]) -> String {
|
||||||
|
format!("{}{}({});", self.indent_str(), name, args.join(", "))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn generate_expr_stmt(&self, expr: &str) -> String {
|
||||||
|
format!("{}{};", self.indent_str(), expr)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -55,6 +55,8 @@ impl CodeGen for JavaScriptCodeGen {
|
||||||
BinaryOpKind::Gt => ">",
|
BinaryOpKind::Gt => ">",
|
||||||
BinaryOpKind::Lte => "<=",
|
BinaryOpKind::Lte => "<=",
|
||||||
BinaryOpKind::Gte => ">=",
|
BinaryOpKind::Gte => ">=",
|
||||||
|
BinaryOpKind::And => "&&",
|
||||||
|
BinaryOpKind::Or => "||",
|
||||||
};
|
};
|
||||||
format!("({} {} {})", left, op_str, right)
|
format!("({} {} {})", left, op_str, right)
|
||||||
}
|
}
|
||||||
|
|
@ -130,4 +132,20 @@ impl CodeGen for JavaScriptCodeGen {
|
||||||
fn generate_string_concat(&self, parts: &[String]) -> String {
|
fn generate_string_concat(&self, parts: &[String]) -> String {
|
||||||
parts.join(" + ")
|
parts.join(" + ")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn generate_debug_var(&self, name: &str) -> String {
|
||||||
|
format!("{}console.log(\"{} =\", {});", self.indent_str(), name, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn generate_open(&self, filename: &str) -> String {
|
||||||
|
format!("{}// open(\"{}\")", self.indent_str(), filename)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn generate_call(&self, name: &str, args: &[String]) -> String {
|
||||||
|
format!("{}{}({});", self.indent_str(), name, args.join(", "))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn generate_expr_stmt(&self, expr: &str) -> String {
|
||||||
|
format!("{}{};", self.indent_str(), expr)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -23,6 +23,10 @@ pub trait CodeGen {
|
||||||
fn generate_if_stmt(&self, condition: &str, then_body: &[String], else_body: &[String]) -> String;
|
fn generate_if_stmt(&self, condition: &str, then_body: &[String], else_body: &[String]) -> String;
|
||||||
fn generate_for_stmt(&self, variable: &str, iterable: &str, body: &[String]) -> String;
|
fn generate_for_stmt(&self, variable: &str, iterable: &str, body: &[String]) -> String;
|
||||||
fn generate_string_concat(&self, parts: &[String]) -> String;
|
fn generate_string_concat(&self, parts: &[String]) -> String;
|
||||||
|
fn generate_debug_var(&self, name: &str) -> String;
|
||||||
|
fn generate_open(&self, filename: &str) -> String;
|
||||||
|
fn generate_call(&self, name: &str, args: &[String]) -> String;
|
||||||
|
fn generate_expr_stmt(&self, expr: &str) -> String;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn walk_expression(codegen: &dyn CodeGen, expr: &Expression) -> String {
|
fn walk_expression(codegen: &dyn CodeGen, expr: &Expression) -> String {
|
||||||
|
|
@ -81,7 +85,13 @@ fn walk_statement(codegen: &dyn CodeGen, stmt: &Statement) -> String {
|
||||||
}
|
}
|
||||||
Statement::ExprStmt { expression } => {
|
Statement::ExprStmt { expression } => {
|
||||||
let e = codegen.generate_expression(expression);
|
let e = codegen.generate_expression(expression);
|
||||||
codegen.generate_var_decl("_", Some(&e), AssignmentType::Equals)
|
codegen.generate_expr_stmt(&e)
|
||||||
|
}
|
||||||
|
Statement::DebugVar { name } => codegen.generate_debug_var(name),
|
||||||
|
Statement::Open { filename } => codegen.generate_open(filename),
|
||||||
|
Statement::Call { name, args } => {
|
||||||
|
let rendered_args: Vec<String> = args.iter().map(|a| codegen.generate_expression(a)).collect();
|
||||||
|
codegen.generate_call(name, &rendered_args)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -55,6 +55,8 @@ impl CodeGen for PythonCodeGen {
|
||||||
BinaryOpKind::Gt => ">",
|
BinaryOpKind::Gt => ">",
|
||||||
BinaryOpKind::Lte => "<=",
|
BinaryOpKind::Lte => "<=",
|
||||||
BinaryOpKind::Gte => ">=",
|
BinaryOpKind::Gte => ">=",
|
||||||
|
BinaryOpKind::And => "and",
|
||||||
|
BinaryOpKind::Or => "or",
|
||||||
};
|
};
|
||||||
format!("({} {} {})", left, op_str, right)
|
format!("({} {} {})", left, op_str, right)
|
||||||
}
|
}
|
||||||
|
|
@ -134,4 +136,20 @@ impl CodeGen for PythonCodeGen {
|
||||||
fn generate_string_concat(&self, parts: &[String]) -> String {
|
fn generate_string_concat(&self, parts: &[String]) -> String {
|
||||||
parts.join(" + ")
|
parts.join(" + ")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn generate_debug_var(&self, name: &str) -> String {
|
||||||
|
format!("{}print(\"{} =\", {})", self.indent_str(), name, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn generate_open(&self, filename: &str) -> String {
|
||||||
|
format!("{}# open(\"{}\")", self.indent_str(), filename)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn generate_call(&self, name: &str, args: &[String]) -> String {
|
||||||
|
format!("{}{}({})", self.indent_str(), name, args.join(", "))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn generate_expr_stmt(&self, expr: &str) -> String {
|
||||||
|
format!("{}{}", self.indent_str(), expr)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -59,6 +59,8 @@ impl CodeGen for RustCodeGen {
|
||||||
BinaryOpKind::Gt => ">",
|
BinaryOpKind::Gt => ">",
|
||||||
BinaryOpKind::Lte => "<=",
|
BinaryOpKind::Lte => "<=",
|
||||||
BinaryOpKind::Gte => ">=",
|
BinaryOpKind::Gte => ">=",
|
||||||
|
BinaryOpKind::And => "&&",
|
||||||
|
BinaryOpKind::Or => "||",
|
||||||
};
|
};
|
||||||
format!("({} {} {})", left, op_str, right)
|
format!("({} {} {})", left, op_str, right)
|
||||||
}
|
}
|
||||||
|
|
@ -134,4 +136,20 @@ impl CodeGen for RustCodeGen {
|
||||||
fn generate_string_concat(&self, parts: &[String]) -> String {
|
fn generate_string_concat(&self, parts: &[String]) -> String {
|
||||||
parts.join(" + ")
|
parts.join(" + ")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn generate_debug_var(&self, name: &str) -> String {
|
||||||
|
format!("{}println!(\"{} = {{}}\", {});", self.indent_str(), name, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn generate_open(&self, filename: &str) -> String {
|
||||||
|
format!("{}// open(\"{}\")", self.indent_str(), filename)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn generate_call(&self, name: &str, args: &[String]) -> String {
|
||||||
|
format!("{}{}({});", self.indent_str(), name, args.join(", "))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn generate_expr_stmt(&self, expr: &str) -> String {
|
||||||
|
format!("{}{};", self.indent_str(), expr)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -32,6 +32,8 @@ Stmt: Statement = {
|
||||||
iterable: iter,
|
iterable: iter,
|
||||||
body,
|
body,
|
||||||
},
|
},
|
||||||
|
"DEBUGVAR" <name:Ident> => Statement::DebugVar { name },
|
||||||
|
"Open" <filename:STRING> => Statement::Open { filename },
|
||||||
};
|
};
|
||||||
|
|
||||||
Expr: Expression = {
|
Expr: Expression = {
|
||||||
|
|
@ -42,7 +44,7 @@ OrExpr: Expression = {
|
||||||
AndExpr,
|
AndExpr,
|
||||||
<left:OrExpr> "||" <right:AndExpr> => Expression::BinaryOp {
|
<left:OrExpr> "||" <right:AndExpr> => Expression::BinaryOp {
|
||||||
left: Box::new(left),
|
left: Box::new(left),
|
||||||
op: BinaryOpKind::Add,
|
op: BinaryOpKind::Or,
|
||||||
right: Box::new(right),
|
right: Box::new(right),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
@ -51,15 +53,14 @@ AndExpr: Expression = {
|
||||||
CmpExpr,
|
CmpExpr,
|
||||||
<left:AndExpr> "&&" <right:CmpExpr> => Expression::BinaryOp {
|
<left:AndExpr> "&&" <right:CmpExpr> => Expression::BinaryOp {
|
||||||
left: Box::new(left),
|
left: Box::new(left),
|
||||||
op: BinaryOpKind::Add,
|
op: BinaryOpKind::And,
|
||||||
right: Box::new(right),
|
right: Box::new(right),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
CmpExpr: Expression = {
|
CmpExpr: Expression = {
|
||||||
AddExpr,
|
AddExpr,
|
||||||
<left:CmpExpr> "==" <right:AddExpr> => Expression::BinaryOp { left: Box::new(left), op: BinaryOpKind::Eq, right: Box::new(right) },
|
<left:CmpExpr> "=" <right:AddExpr> => Expression::BinaryOp { left: Box::new(left), op: BinaryOpKind::Eq, right: Box::new(right) },
|
||||||
<left:CmpExpr> "!=" <right:AddExpr> => Expression::BinaryOp { left: Box::new(left), op: BinaryOpKind::Neq, right: Box::new(right) },
|
|
||||||
<left:CmpExpr> "<=" <right:AddExpr> => Expression::BinaryOp { left: Box::new(left), op: BinaryOpKind::Lte, right: Box::new(right) },
|
<left:CmpExpr> "<=" <right:AddExpr> => Expression::BinaryOp { left: Box::new(left), op: BinaryOpKind::Lte, right: Box::new(right) },
|
||||||
<left:CmpExpr> ">=" <right:AddExpr> => Expression::BinaryOp { left: Box::new(left), op: BinaryOpKind::Gte, right: Box::new(right) },
|
<left:CmpExpr> ">=" <right:AddExpr> => Expression::BinaryOp { left: Box::new(left), op: BinaryOpKind::Gte, right: Box::new(right) },
|
||||||
<left:CmpExpr> "<" <right:AddExpr> => Expression::BinaryOp { left: Box::new(left), op: BinaryOpKind::Lt, right: Box::new(right) },
|
<left:CmpExpr> "<" <right:AddExpr> => Expression::BinaryOp { left: Box::new(left), op: BinaryOpKind::Lt, right: Box::new(right) },
|
||||||
|
|
@ -133,8 +134,8 @@ match {
|
||||||
"EndFor",
|
"EndFor",
|
||||||
"True",
|
"True",
|
||||||
"False",
|
"False",
|
||||||
"==",
|
"DEBUGVAR",
|
||||||
"!=",
|
"Open",
|
||||||
"<=",
|
"<=",
|
||||||
">=",
|
">=",
|
||||||
"&&",
|
"&&",
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,10 +1,11 @@
|
||||||
use crate::xml::ast::nodes::{
|
|
||||||
AssignmentType, BinaryOpKind, Expression, LiteralValue, Program, Statement, UnaryOpKind,
|
|
||||||
};
|
|
||||||
use crate::xml::error::ParserResult;
|
use crate::xml::error::ParserResult;
|
||||||
|
|
||||||
#[allow(clippy::all)]
|
#[allow(clippy::all)]
|
||||||
pub mod grammar;
|
pub mod grammar {
|
||||||
|
include!(concat!(env!("OUT_DIR"), "/xml/parser/grammar.rs"));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub use crate::xml::ast::nodes::{AssignmentType, BinaryOpKind, Expression, LiteralValue, Program, Statement, UnaryOpKind};
|
||||||
|
|
||||||
use grammar::ProgramParser;
|
use grammar::ProgramParser;
|
||||||
|
|
||||||
|
|
@ -88,13 +89,13 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_if_stmt_simple() {
|
fn test_if_stmt_simple() {
|
||||||
let result = ScriptParser::parse("If x == 5 Then Var y = 10 EndIf");
|
let result = ScriptParser::parse("If x = 5 Then Var y = 10 EndIf");
|
||||||
assert!(result.is_ok(), "parse failed: {:?}", result.err());
|
assert!(result.is_ok(), "parse failed: {:?}", result.err());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_if_with_else() {
|
fn test_if_with_else() {
|
||||||
let result = ScriptParser::parse("If x == 5 Then Var y = 10 Else Var y = 20 EndIf");
|
let result = ScriptParser::parse("If x = 5 Then Var y = 10 Else Var y = 20 EndIf");
|
||||||
assert!(result.is_ok(), "parse failed: {:?}", result.err());
|
assert!(result.is_ok(), "parse failed: {:?}", result.err());
|
||||||
let program = result.unwrap();
|
let program = result.unwrap();
|
||||||
if let Statement::IfStmt { else_branch, .. } = &program.statements[0] {
|
if let Statement::IfStmt { else_branch, .. } = &program.statements[0] {
|
||||||
|
|
@ -152,10 +153,46 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_comparison() {
|
fn test_comparison() {
|
||||||
let result = ScriptParser::parse("If x != 5 Then Var y = 10 EndIf");
|
let result = ScriptParser::parse("If x = 5 Then Var y = 10 EndIf");
|
||||||
assert!(result.is_ok(), "parse failed: {:?}", result.err());
|
assert!(result.is_ok(), "parse failed: {:?}", result.err());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_logical_and() {
|
||||||
|
let result = ScriptParser::parse("If x = 5 && y = 3 Then Var z = 1 EndIf");
|
||||||
|
assert!(result.is_ok(), "parse failed: {:?}", result.err());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_logical_or() {
|
||||||
|
let result = ScriptParser::parse("If x = 5 || y = 3 Then Var z = 1 EndIf");
|
||||||
|
assert!(result.is_ok(), "parse failed: {:?}", result.err());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_debug_var() {
|
||||||
|
let result = ScriptParser::parse("DEBUGVAR myVar");
|
||||||
|
assert!(result.is_ok(), "parse failed: {:?}", result.err());
|
||||||
|
let program = result.unwrap();
|
||||||
|
if let Statement::DebugVar { name } = &program.statements[0] {
|
||||||
|
assert_eq!(name, "myVar");
|
||||||
|
} else {
|
||||||
|
panic!("Expected DebugVar statement");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_open() {
|
||||||
|
let result = ScriptParser::parse(r#"Open "somefile.xml""#);
|
||||||
|
assert!(result.is_ok(), "parse failed: {:?}", result.err());
|
||||||
|
let program = result.unwrap();
|
||||||
|
if let Statement::Open { filename } = &program.statements[0] {
|
||||||
|
assert_eq!(filename, "somefile.xml");
|
||||||
|
} else {
|
||||||
|
panic!("Expected Open statement");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_for_loop() {
|
fn test_for_loop() {
|
||||||
let result = ScriptParser::parse("For i In items Var x = i EndFor");
|
let result = ScriptParser::parse("For i In items Var x = i EndFor");
|
||||||
|
|
|
||||||
|
|
@ -279,7 +279,8 @@ impl Vm {
|
||||||
if let Some(func) = self.functions.get(name) {
|
if let Some(func) = self.functions.get(name) {
|
||||||
func(&evaluated_args)
|
func(&evaluated_args)
|
||||||
} else {
|
} else {
|
||||||
Err(VmError::UndefinedFunction(name.clone()))
|
eprintln!("[WARN] Unknown function: {}, skipping", name);
|
||||||
|
Ok(Value::Null)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -370,6 +371,8 @@ impl Vm {
|
||||||
let r = right.to_f64().ok_or_else(|| VmError::TypeError("Cannot compare non-numbers".into()))?;
|
let r = right.to_f64().ok_or_else(|| VmError::TypeError("Cannot compare non-numbers".into()))?;
|
||||||
Ok(Value::Bool(l >= r))
|
Ok(Value::Bool(l >= r))
|
||||||
}
|
}
|
||||||
|
BinaryOpKind::And => Ok(Value::Bool(left.to_bool() && right.to_bool())),
|
||||||
|
BinaryOpKind::Or => Ok(Value::Bool(left.to_bool() || right.to_bool())),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -425,6 +428,30 @@ impl Vm {
|
||||||
self.eval_expr(expression)?;
|
self.eval_expr(expression)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
Statement::DebugVar { name } => {
|
||||||
|
if let Some(val) = self.globals.get(name) {
|
||||||
|
println!("{} = {}", name, val);
|
||||||
|
} else if let Some(val) = self.globals.get(&format!("${}", name)) {
|
||||||
|
println!("${} = {}", name, val);
|
||||||
|
} else {
|
||||||
|
println!("{} = <undefined>", name);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
Statement::Open { filename } => {
|
||||||
|
eprintln!("[WARN] Open \"{}\" - not yet implemented in VM", filename);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
Statement::Call { name, args } => {
|
||||||
|
let evaluated_args: VmResult<Vec<Value>> = args.iter().map(|a| self.eval_expr(a)).collect();
|
||||||
|
let evaluated_args = evaluated_args?;
|
||||||
|
if let Some(func) = self.functions.get(name) {
|
||||||
|
func(&evaluated_args)?;
|
||||||
|
} else {
|
||||||
|
eprintln!("[WARN] Unknown function: {}, skipping", name);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -452,6 +479,31 @@ impl Vm {
|
||||||
let val = self.eval_expr(expression)?;
|
let val = self.eval_expr(expression)?;
|
||||||
Ok(Some(val))
|
Ok(Some(val))
|
||||||
}
|
}
|
||||||
|
Statement::DebugVar { name } => {
|
||||||
|
if let Some(val) = self.globals.get(name) {
|
||||||
|
println!("{} = {}", name, val);
|
||||||
|
} else if let Some(val) = self.globals.get(&format!("${}", name)) {
|
||||||
|
println!("${} = {}", name, val);
|
||||||
|
} else {
|
||||||
|
println!("{} = <undefined>", name);
|
||||||
|
}
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
Statement::Open { filename } => {
|
||||||
|
eprintln!("[WARN] Open \"{}\" - not yet implemented in VM", filename);
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
Statement::Call { name, args } => {
|
||||||
|
let evaluated_args: VmResult<Vec<Value>> = args.iter().map(|a| self.eval_expr(a)).collect();
|
||||||
|
let evaluated_args = evaluated_args?;
|
||||||
|
if let Some(func) = self.functions.get(name) {
|
||||||
|
let result = func(&evaluated_args)?;
|
||||||
|
Ok(Some(result))
|
||||||
|
} else {
|
||||||
|
eprintln!("[WARN] Unknown function: {}, skipping", name);
|
||||||
|
Ok(Some(Value::Null))
|
||||||
|
}
|
||||||
|
}
|
||||||
_ => {
|
_ => {
|
||||||
self.exec_statement(stmt)?;
|
self.exec_statement(stmt)?;
|
||||||
Ok(None)
|
Ok(None)
|
||||||
|
|
@ -510,7 +562,7 @@ mod tests {
|
||||||
fn test_comparison_eq() {
|
fn test_comparison_eq() {
|
||||||
let mut globals = Environment::new();
|
let mut globals = Environment::new();
|
||||||
globals.insert("x".to_string(), Value::Number(5.0));
|
globals.insert("x".to_string(), Value::Number(5.0));
|
||||||
let program = crate::xml::parser::ScriptParser::parse("If x == 5 Then Var y = 1 Else Var y = 0 EndIf")
|
let program = crate::xml::parser::ScriptParser::parse("If x = 5 Then Var y = 1 Else Var y = 0 EndIf")
|
||||||
.map_err(|e| VmError::RuntimeError(e.to_string())).unwrap();
|
.map_err(|e| VmError::RuntimeError(e.to_string())).unwrap();
|
||||||
let mut vm = Vm::with_globals(globals);
|
let mut vm = Vm::with_globals(globals);
|
||||||
vm.run(&program).unwrap();
|
vm.run(&program).unwrap();
|
||||||
|
|
@ -518,10 +570,10 @@ mod tests {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_comparison_neq() {
|
fn test_comparison_lt() {
|
||||||
let mut globals = Environment::new();
|
let mut globals = Environment::new();
|
||||||
globals.insert("x".to_string(), Value::Number(3.0));
|
globals.insert("x".to_string(), Value::Number(3.0));
|
||||||
let program = crate::xml::parser::ScriptParser::parse("If x != 5 Then Var y = 1 Else Var y = 0 EndIf")
|
let program = crate::xml::parser::ScriptParser::parse("If x < 5 Then Var y = 1 Else Var y = 0 EndIf")
|
||||||
.map_err(|e| VmError::RuntimeError(e.to_string())).unwrap();
|
.map_err(|e| VmError::RuntimeError(e.to_string())).unwrap();
|
||||||
let mut vm = Vm::with_globals(globals);
|
let mut vm = Vm::with_globals(globals);
|
||||||
vm.run(&program).unwrap();
|
vm.run(&program).unwrap();
|
||||||
|
|
@ -532,7 +584,7 @@ mod tests {
|
||||||
fn test_if_else_false() {
|
fn test_if_else_false() {
|
||||||
let mut globals = Environment::new();
|
let mut globals = Environment::new();
|
||||||
globals.insert("x".to_string(), Value::Number(10.0));
|
globals.insert("x".to_string(), Value::Number(10.0));
|
||||||
let program = crate::xml::parser::ScriptParser::parse("If x == 5 Then Var y = 1 Else Var y = 2 EndIf")
|
let program = crate::xml::parser::ScriptParser::parse("If x = 5 Then Var y = 1 Else Var y = 2 EndIf")
|
||||||
.map_err(|e| VmError::RuntimeError(e.to_string())).unwrap();
|
.map_err(|e| VmError::RuntimeError(e.to_string())).unwrap();
|
||||||
let mut vm = Vm::with_globals(globals);
|
let mut vm = Vm::with_globals(globals);
|
||||||
vm.run(&program).unwrap();
|
vm.run(&program).unwrap();
|
||||||
|
|
@ -633,4 +685,46 @@ mod tests {
|
||||||
let result = parse_and_run("Var x = unknown_var");
|
let result = parse_and_run("Var x = unknown_var");
|
||||||
assert!(result.is_err());
|
assert!(result.is_err());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_unknown_function_returns_null() {
|
||||||
|
let result = parse_and_run("Var x = unknownFunc(1, 2)");
|
||||||
|
assert!(result.is_ok());
|
||||||
|
assert_eq!(result.unwrap(), Value::Null);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_logical_and() {
|
||||||
|
let mut globals = Environment::new();
|
||||||
|
globals.insert("x".to_string(), Value::Bool(true));
|
||||||
|
globals.insert("y".to_string(), Value::Bool(false));
|
||||||
|
let program = crate::xml::parser::ScriptParser::parse("If x && y Then Var z = 1 Else Var z = 0 EndIf")
|
||||||
|
.map_err(|e| VmError::RuntimeError(e.to_string())).unwrap();
|
||||||
|
let mut vm = Vm::with_globals(globals);
|
||||||
|
vm.run(&program).unwrap();
|
||||||
|
assert_eq!(*vm.get_global("z").unwrap(), Value::Number(0.0));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_logical_or() {
|
||||||
|
let mut globals = Environment::new();
|
||||||
|
globals.insert("x".to_string(), Value::Bool(true));
|
||||||
|
globals.insert("y".to_string(), Value::Bool(false));
|
||||||
|
let program = crate::xml::parser::ScriptParser::parse("If x || y Then Var z = 1 Else Var z = 0 EndIf")
|
||||||
|
.map_err(|e| VmError::RuntimeError(e.to_string())).unwrap();
|
||||||
|
let mut vm = Vm::with_globals(globals);
|
||||||
|
vm.run(&program).unwrap();
|
||||||
|
assert_eq!(*vm.get_global("z").unwrap(), Value::Number(1.0));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_debug_var() {
|
||||||
|
let mut globals = Environment::new();
|
||||||
|
globals.insert("myVar".to_string(), Value::Number(42.0));
|
||||||
|
let program = crate::xml::parser::ScriptParser::parse("DEBUGVAR myVar")
|
||||||
|
.map_err(|e| VmError::RuntimeError(e.to_string())).unwrap();
|
||||||
|
let mut vm = Vm::with_globals(globals);
|
||||||
|
let result = vm.run(&program);
|
||||||
|
assert!(result.is_ok());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue