diff --git a/compiler/ast/tests/ast_tests.rs b/compiler/ast/tests/ast_tests.rs index ffe7f12b..584d5ea6 100644 --- a/compiler/ast/tests/ast_tests.rs +++ b/compiler/ast/tests/ast_tests.rs @@ -1,6 +1,6 @@ use techscript_ast::{ - AssignmentExpr, Block, BreakStmt, EnumDecl, EnumVariant, Expression, FieldSpec, Ident, - LiteralExpr, LiteralVal, Pattern, Program, Statement, StructDecl, VarDecl, + AssignmentExpr, EnumDecl, EnumVariant, Expression, FieldSpec, Ident, LiteralExpr, LiteralVal, + Pattern, StructDecl, VarDecl, }; use techscript_common::{NodeId, Span}; @@ -101,47 +101,5 @@ fn test_ast_serialization() { assert_eq!(lit, deserialized); } -#[test] -fn test_ast_program_construction_and_serialization() { - let span = Span::new(0, 50); - let id = NodeId(1); - - // Create a dummy statement (Break) - let break_stmt = Statement::Break(BreakStmt::new(NodeId(2), span)); - - let program = Program::new(id, vec![break_stmt.clone()], span); - - assert_eq!(program.id, id); - assert_eq!(program.statements.len(), 1); - assert_eq!(program.span, span); - - let serialized = serde_json::to_string(&program).expect("serialize should succeed"); - let deserialized: Program = - serde_json::from_str(&serialized).expect("deserialize should succeed"); - - assert_eq!(program, deserialized); -} - -#[test] -fn test_ast_block_construction_and_serialization() { - let span = Span::new(10, 20); - let id = NodeId(3); - - // Create a dummy statement (Break) - let break_stmt = Statement::Break(BreakStmt::new(NodeId(4), span)); - - let block = Block::new(id, vec![break_stmt.clone()], span); - - assert_eq!(block.id, id); - assert_eq!(block.statements.len(), 1); - assert_eq!(block.span, span); - - let serialized = serde_json::to_string(&block).expect("serialize should succeed"); - let deserialized: Block = - serde_json::from_str(&serialized).expect("deserialize should succeed"); - - assert_eq!(block, deserialized); -} - // Internal helper just to satisfy TypeSpec compilation in test_ast_struct_decl use techscript_ast::TypeSpec; diff --git a/compiler/lexer/src/lib.rs b/compiler/lexer/src/lib.rs index 034560cf..c59e1798 100644 --- a/compiler/lexer/src/lib.rs +++ b/compiler/lexer/src/lib.rs @@ -12,6 +12,7 @@ use techscript_syntax::{lookup_keyword, Token, TokenKind}; /// Private token enumeration used internally by Logos for scanning. #[derive(Logos, Debug, Clone, Copy, PartialEq, Eq)] #[logos(skip r"[ \t\r]+")] // Skip spaces, tabs, and carriage returns +#[allow(dead_code)] enum LogosToken { #[token("\n")] #[token("\r\n")] diff --git a/compiler/semantic/src/dsl_schema.rs b/compiler/semantic/src/dsl_schema.rs index d39e3c73..43c8e3e9 100644 --- a/compiler/semantic/src/dsl_schema.rs +++ b/compiler/semantic/src/dsl_schema.rs @@ -33,8 +33,7 @@ impl DSLSchema { } } -fn register_web_schemas(reg: &mut HashMap) { - // Web module schemas ────────────────────────────────────────────── +fn register_web_layout_schemas(reg: &mut HashMap) { reg.insert( "website".to_string(), DSLSchema::new( @@ -79,28 +78,64 @@ fn register_web_schemas(reg: &mut HashMap) { ); reg.insert( - "hero".to_string(), + "header".to_string(), DSLSchema::new( vec![ "title".into(), "subtitle".into(), - "tagline".into(), "background".into(), "color".into(), - "image".into(), "align".into(), "size".into(), ], vec![], + vec!["nav".into(), "button".into(), "link".into()], + ), + ); + + reg.insert( + "main".to_string(), + DSLSchema::new( + vec!["id".into(), "class".into()], + vec![], vec![ - "button".into(), - "link".into(), - "input".into(), - "start".into(), + "hero".into(), + "section".into(), + "card".into(), + "aside".into(), ], ), ); + reg.insert( + "aside".to_string(), + DSLSchema::new( + vec![ + "title".into(), + "background".into(), + "color".into(), + "width".into(), + ], + vec![], + vec!["link".into(), "nav".into(), "card".into()], + ), + ); + + reg.insert( + "footer".to_string(), + DSLSchema::new( + vec![ + "text".into(), + "color".into(), + "background".into(), + "align".into(), + "padding".into(), + ], + vec![], + vec!["link".into(), "nav".into(), "section".into()], + ), + ); + reg.insert( "section".to_string(), DSLSchema::new( @@ -128,39 +163,63 @@ fn register_web_schemas(reg: &mut HashMap) { ); reg.insert( - "card".to_string(), + "hero".to_string(), DSLSchema::new( vec![ "title".into(), "subtitle".into(), - "text".into(), - "icon".into(), - "image".into(), + "tagline".into(), + "background".into(), "color".into(), + "image".into(), + "align".into(), + "size".into(), + ], + vec![], + vec![ + "button".into(), + "link".into(), + "input".into(), + "start".into(), + ], + ), + ); + + reg.insert( + "nav".to_string(), + DSLSchema::new( + vec![ + "title".into(), + "align".into(), "background".into(), - "width".into(), - "height".into(), - "shadow".into(), - "rounded".into(), - "border".into(), + "color".into(), ], vec![], - vec!["button".into(), "link".into(), "input".into()], + vec!["link".into(), "button".into()], ), ); +} +fn register_web_component_schemas(reg: &mut HashMap) { reg.insert( - "footer".to_string(), + "card".to_string(), DSLSchema::new( vec![ + "title".into(), + "subtitle".into(), "text".into(), + "icon".into(), + "image".into(), "color".into(), "background".into(), - "align".into(), - "padding".into(), + "width".into(), + "height".into(), + "shadow".into(), + "rounded".into(), + "border".into(), ], vec![], - vec!["link".into(), "nav".into(), "section".into()], + vec!["button".into(), "link".into(), "input".into()], ), ); @@ -200,21 +259,22 @@ fn register_web_schemas(reg: &mut HashMap) { ); reg.insert( - "input".to_string(), + "start".to_string(), DSLSchema::new( vec![ "label".into(), - "placeholder".into(), - "type".into(), - "value".into(), - "required".into(), - "name".into(), + "url".into(), + "color".into(), + "background".into(), + "size".into(), ], - vec![], + vec!["label".into()], vec![], ), ); +} +fn register_web_form_schemas(reg: &mut HashMap) { reg.insert( "form".to_string(), DSLSchema::new( @@ -225,94 +285,60 @@ fn register_web_schemas(reg: &mut HashMap) { ); reg.insert( - "nav".to_string(), + "input".to_string(), DSLSchema::new( vec![ - "title".into(), - "align".into(), - "background".into(), - "color".into(), + "label".into(), + "placeholder".into(), + "type".into(), + "value".into(), + "required".into(), + "name".into(), ], vec![], - vec!["link".into(), "button".into()], + vec![], ), ); +} +fn register_web_schemas(reg: &mut HashMap) { + // Web module schemas ────────────────────────────────────────────── + register_web_layout_schemas(reg); + register_web_component_schemas(reg); + register_web_form_schemas(reg); +} +fn register_canvas_schemas(reg: &mut HashMap) { + // Canvas module schemas ────────────────────────────────────────── reg.insert( - "header".to_string(), + "logo".to_string(), DSLSchema::new( vec![ - "title".into(), - "subtitle".into(), - "background".into(), + "text".into(), "color".into(), - "align".into(), + "font".into(), "size".into(), - ], - vec![], - vec!["nav".into(), "button".into(), "link".into()], - ), - ); - - reg.insert( - "main".to_string(), - DSLSchema::new( - vec!["id".into(), "class".into()], - vec![], - vec![ - "hero".into(), - "section".into(), - "card".into(), - "aside".into(), - ], - ), - ); - - reg.insert( - "aside".to_string(), - DSLSchema::new( - vec![ - "title".into(), "background".into(), - "color".into(), - "width".into(), + "rounded".into(), + "shadow".into(), + "padding".into(), ], + vec!["text".into()], vec![], - vec!["link".into(), "nav".into(), "card".into()], ), ); reg.insert( - "start".to_string(), + "rings".to_string(), DSLSchema::new( vec![ - "label".into(), - "url".into(), + "count".into(), "color".into(), - "background".into(), "size".into(), + "thickness".into(), + "spacing".into(), + "rotation".into(), ], - vec!["label".into()], vec![], - ), - ); -} - -fn register_canvas_shape_schemas(reg: &mut HashMap) { - reg.insert( - "logo".to_string(), - DSLSchema::new( - vec![ - "text".into(), - "color".into(), - "font".into(), - "size".into(), - "background".into(), - "rounded".into(), - "shadow".into(), - "padding".into(), - ], - vec!["text".into()], vec![], ), ); @@ -348,9 +374,7 @@ fn register_canvas_shape_schemas(reg: &mut HashMap) { vec![], ), ); -} -fn register_canvas_text_schemas(reg: &mut HashMap) { reg.insert( "letter".to_string(), DSLSchema::new( @@ -369,23 +393,22 @@ fn register_canvas_text_schemas(reg: &mut HashMap) { ); reg.insert( - "title".to_string(), + "circuits".to_string(), DSLSchema::new( vec![ - "text".into(), "color".into(), - "font".into(), - "size".into(), - "align".into(), - "weight".into(), + "density".into(), + "width".into(), + "animated".into(), + "complexity".into(), ], - vec!["text".into()], + vec![], vec![], ), ); reg.insert( - "subtitle".to_string(), + "title".to_string(), DSLSchema::new( vec![ "text".into(), @@ -393,33 +416,22 @@ fn register_canvas_text_schemas(reg: &mut HashMap) { "font".into(), "size".into(), "align".into(), + "weight".into(), ], - vec![], - vec![], - ), - ); - - reg.insert( - "tagline".to_string(), - DSLSchema::new( - vec!["text".into(), "color".into(), "font".into(), "size".into()], - vec![], + vec!["text".into()], vec![], ), ); -} -fn register_canvas_misc_schemas(reg: &mut HashMap) { reg.insert( - "rings".to_string(), + "subtitle".to_string(), DSLSchema::new( vec![ - "count".into(), + "text".into(), "color".into(), + "font".into(), "size".into(), - "thickness".into(), - "spacing".into(), - "rotation".into(), + "align".into(), ], vec![], vec![], @@ -427,15 +439,9 @@ fn register_canvas_misc_schemas(reg: &mut HashMap) { ); reg.insert( - "circuits".to_string(), + "tagline".to_string(), DSLSchema::new( - vec![ - "color".into(), - "density".into(), - "width".into(), - "animated".into(), - "complexity".into(), - ], + vec!["text".into(), "color".into(), "font".into(), "size".into()], vec![], vec![], ), @@ -490,13 +496,6 @@ fn register_canvas_misc_schemas(reg: &mut HashMap) { ); } -fn register_canvas_schemas(reg: &mut HashMap) { - // Canvas module schemas ────────────────────────────────────────── - register_canvas_shape_schemas(reg); - register_canvas_text_schemas(reg); - register_canvas_misc_schemas(reg); -} - fn register_generic_schemas(reg: &mut HashMap) { // Generic DSL blocks ──────────────────────────────────────────── reg.insert( diff --git a/runtime/vm/src/executor.rs b/runtime/vm/src/executor.rs index 5b909abe..718997a7 100644 --- a/runtime/vm/src/executor.rs +++ b/runtime/vm/src/executor.rs @@ -450,8 +450,8 @@ impl VM { } // PERFORMANCE OPTIMIZATION (Bolt): - // We reuse the existing mutable frame reference acquired at the start of - // the loop iteration rather than redundantly fetching the last mutable frame + // We reuse the existing mutable `frame` reference acquired at the start of + // the loop iteration rather than redundantly calling `self.frames.last_mut()` // for these control flow and exception opcodes. This reduces bounds checking // and RefCell borrow overhead on the hottest execution paths. Opcode::Jump => { diff --git a/scripts/migrate_syntax.py b/scripts/migrate_syntax.py index 9d98f91f..b6edc7bc 100644 --- a/scripts/migrate_syntax.py +++ b/scripts/migrate_syntax.py @@ -34,7 +34,7 @@ (r'std\.io\.println\((.+?)\)', r'say \1', 0), (r'std\.io\.print\((.+?)\)', r'say \1', 0), - # ── std..yyy() calls → module.yyy() ──────────────────────────── + # ── std.xxx.yyy() calls → module.yyy() ───────────────────────────────── (r'std\.math\.', r'math.', 0), (r'std\.strings\.', r'string.', 0), (r'std\.fs\.', r'file.', 0), diff --git a/stdlib/src/database.rs b/stdlib/src/database.rs index 7be0ee38..15987f53 100644 --- a/stdlib/src/database.rs +++ b/stdlib/src/database.rs @@ -78,7 +78,8 @@ fn std_database_query( let sql = args[1].try_into_string()?; let params_list = get_params_list(&args); - let resources_borrow = ctx.resources.borrow(); + let resources = ctx.resources.clone(); + let resources_borrow = resources.borrow(); let conn = resources_borrow .get::(handle) .ok_or_else(|| { @@ -172,7 +173,8 @@ fn std_database_execute( let sql = args[1].try_into_string()?; let params_list = get_params_list(&args); - let resources_borrow = ctx.resources.borrow(); + let resources = ctx.resources.clone(); + let resources_borrow = resources.borrow(); let conn = resources_borrow .get::(handle) .ok_or_else(|| { diff --git a/stdlib/src/encoding.rs b/stdlib/src/encoding.rs index 483de578..325d5ecf 100644 --- a/stdlib/src/encoding.rs +++ b/stdlib/src/encoding.rs @@ -83,7 +83,7 @@ impl StdlibRegistry { StdlibModule { name: "std.hex".to_string(), version: "1.0.0".to_string(), - exports, + exports: exports.clone(), required_capabilities: Vec::new(), }, ); diff --git a/stdlib/src/json.rs b/stdlib/src/json.rs index 3b87d1af..06528ccf 100644 --- a/stdlib/src/json.rs +++ b/stdlib/src/json.rs @@ -205,95 +205,4 @@ mod tests { let err = result.unwrap_err(); assert!(err.to_string().contains("Cannot stringify type")); } - - #[test] - fn test_parse_json_value_simple() { - // Null - assert_eq!( - parse_json_value(serde_json::Value::Null), - RuntimeValue::Null - ); - - // Bool - assert_eq!( - parse_json_value(serde_json::Value::Bool(true)), - RuntimeValue::Bool(true) - ); - assert_eq!( - parse_json_value(serde_json::Value::Bool(false)), - RuntimeValue::Bool(false) - ); - - // Int - assert_eq!( - parse_json_value(serde_json::Value::Number(serde_json::Number::from(42))), - RuntimeValue::Int(42) - ); - assert_eq!( - parse_json_value(serde_json::Value::Number(serde_json::Number::from(-10))), - RuntimeValue::Int(-10) - ); - - // Float - assert_eq!( - parse_json_value(serde_json::Value::Number( - serde_json::Number::from_f64(3.14).unwrap() - )), - RuntimeValue::Float(3.14) - ); - - // String - assert_eq!( - parse_json_value(serde_json::Value::String("hello".to_string())), - RuntimeValue::Str("hello".to_string()) - ); - } - - #[test] - fn test_parse_json_value_complex() { - // Array - let arr = serde_json::Value::Array(vec![ - serde_json::Value::Number(serde_json::Number::from(1)), - serde_json::Value::String("two".to_string()), - serde_json::Value::Bool(false), - ]); - let parsed_arr = parse_json_value(arr); - match parsed_arr { - RuntimeValue::List { items, is_const } => { - assert!(!is_const); - let borrowed = items.borrow(); - assert_eq!(borrowed.len(), 3); - assert_eq!(borrowed[0], RuntimeValue::Int(1)); - assert_eq!(borrowed[1], RuntimeValue::Str("two".to_string())); - assert_eq!(borrowed[2], RuntimeValue::Bool(false)); - } - _ => panic!("Expected RuntimeValue::List"), - } - - // Object - let mut obj_map = serde_json::Map::new(); - obj_map.insert( - "key1".to_string(), - serde_json::Value::Number(serde_json::Number::from(100)), - ); - obj_map.insert( - "key2".to_string(), - serde_json::Value::String("value2".to_string()), - ); - let obj = serde_json::Value::Object(obj_map); - let parsed_obj = parse_json_value(obj); - match parsed_obj { - RuntimeValue::Map { entries, is_const } => { - assert!(!is_const); - let borrowed = entries.borrow(); - assert_eq!(borrowed.len(), 2); - assert_eq!(borrowed.get("key1").unwrap(), &RuntimeValue::Int(100)); - assert_eq!( - borrowed.get("key2").unwrap(), - &RuntimeValue::Str("value2".to_string()) - ); - } - _ => panic!("Expected RuntimeValue::Map"), - } - } } diff --git a/stdlib/src/notification.rs b/stdlib/src/notification.rs index e7e033a9..a29dde0b 100644 --- a/stdlib/src/notification.rs +++ b/stdlib/src/notification.rs @@ -34,11 +34,13 @@ impl StdlibRegistry { { use std::process::Command; let _ = Command::new("powershell") - .env("__TS_BODY", &body) - .env("__TS_TITLE", &title) .args([ "-Command", - "[System.Windows.MessageBox]::Show($env:__TS_BODY, $env:__TS_TITLE)", + &format!( + "[System.Windows.MessageBox]::Show('{}','{}')", + body.replace("'", "''"), + title.replace("'", "''") + ), ]) .spawn(); } diff --git a/stdlib/src/sqlite.rs b/stdlib/src/sqlite.rs index 22f81d5b..487d44da 100644 --- a/stdlib/src/sqlite.rs +++ b/stdlib/src/sqlite.rs @@ -63,6 +63,15 @@ impl StdlibRegistry { ) })?; let sql = args[1].to_string(); + let params_list = if args.len() > 2 { + if let RuntimeValue::List { items, .. } = &args[2] { + items.borrow().clone() + } else { + Vec::new() + } + } else { + Vec::new() + }; let params: Vec = if let Some(arg) = args.get(2) { if let RuntimeValue::List { items, .. } = arg { @@ -129,6 +138,15 @@ impl StdlibRegistry { ) })?; let sql = args[1].to_string(); + let params_list = if args.len() > 2 { + if let RuntimeValue::List { items, .. } = &args[2] { + items.borrow().clone() + } else { + Vec::new() + } + } else { + Vec::new() + }; let params: Vec = if let Some(arg) = args.get(2) { if let RuntimeValue::List { items, .. } = arg { diff --git a/stdlib/src/sys.rs b/stdlib/src/sys.rs index 3753b553..42d264b3 100644 --- a/stdlib/src/sys.rs +++ b/stdlib/src/sys.rs @@ -100,7 +100,7 @@ impl StdlibRegistry { StdlibModule { name: "std.fs".to_string(), version: "1.0.0".to_string(), - exports, + exports: exports.clone(), required_capabilities: vec![Capability::FileSystem], }, ); diff --git a/stdlib/src/web.rs b/stdlib/src/web.rs index 5ee75480..500b9235 100644 --- a/stdlib/src/web.rs +++ b/stdlib/src/web.rs @@ -98,7 +98,9 @@ static PAGE_CONTENT: Mutex = Mutex::new(String::new()); fn render_children(html: &mut String, dsl: &techscript_runtime::value::DslBlockValue) { for child in &dsl.children { - html.push_str(&dsl_block_to_html(child)); + html.push_str(&dsl_to_html( + &techscript_runtime::value::RuntimeValue::DslBlock(std::rc::Rc::new(child.clone())), + )); } } @@ -232,131 +234,129 @@ fn render_card(html: &mut String, dsl: &techscript_runtime::value::DslBlockValue /// Convert a DslBlockValue tree to HTML string. fn dsl_to_html(val: &RuntimeValue) -> String { match val { - RuntimeValue::DslBlock(dsl) => dsl_block_to_html(dsl), - _ => String::new(), - } -} - -fn dsl_block_to_html(dsl: &techscript_runtime::value::DslBlockValue) -> String { - let mut html = String::new(); - match dsl.kind.as_str() { - "website" => { - render_website(&mut html, dsl); - } - "page" => { - render_page(&mut html, dsl); - } - "hero" => { - render_hero(&mut html, dsl); - } - "section" => { - render_section(&mut html, dsl); - } - "card" => { - render_card(&mut html, dsl); - } - "button" => { - let label = dsl - .properties - .iter() - .find(|p| p.name == "label") - .and_then(|p| p.value.as_ref()) - .map(|v| v.to_string()) - .unwrap_or_else(|| "Button".to_string()); - let _ = write!(html, "", label); - } - "link" => { - let label = dsl - .properties - .iter() - .find(|p| p.name == "label") - .and_then(|p| p.value.as_ref()) - .map(|v| v.to_string()) - .unwrap_or_else(|| "Link".to_string()); - let url = dsl - .properties - .iter() - .find(|p| p.name == "url") - .and_then(|p| p.value.as_ref()) - .map(|v| v.to_string()); - if let Some(u) = url { - let _ = write!(html, "{}", u, label); - } else { - let _ = write!(html, "{}", label); - } - } - "nav" => { - html.push_str(""); - } - "header" => { - html.push_str("
"); - for prop in &dsl.properties { - if prop.name == "title" { - if let Some(RuntimeValue::Str(t)) = &prop.value { - let _ = write!(html, "

{}

", t); + RuntimeValue::DslBlock(dsl) => { + let mut html = String::new(); + match dsl.kind.as_str() { + "website" => { + render_website(&mut html, dsl); + } + "page" => { + render_page(&mut html, dsl); + } + "hero" => { + render_hero(&mut html, dsl); + } + "section" => { + render_section(&mut html, dsl); + } + "card" => { + render_card(&mut html, dsl); + } + "button" => { + let label = dsl + .properties + .iter() + .find(|p| p.name == "label") + .and_then(|p| p.value.as_ref()) + .map(|v| v.to_string()) + .unwrap_or_else(|| "Button".to_string()); + let _ = write!(html, "", label); + } + "link" => { + let label = dsl + .properties + .iter() + .find(|p| p.name == "label") + .and_then(|p| p.value.as_ref()) + .map(|v| v.to_string()) + .unwrap_or_else(|| "Link".to_string()); + let url = dsl + .properties + .iter() + .find(|p| p.name == "url") + .and_then(|p| p.value.as_ref()) + .map(|v| v.to_string()); + if let Some(u) = url { + let _ = write!(html, "{}", u, label); + } else { + let _ = write!(html, "{}", label); } } - } - render_children(&mut html, dsl); - html.push_str("
"); - } - "footer" => { - html.push_str("
"); - for prop in &dsl.properties { - if prop.name == "text" { - if let Some(RuntimeValue::Str(t)) = &prop.value { - let _ = write!(html, "

{}

", t); + "nav" => { + html.push_str(""); + } + "header" => { + html.push_str("
"); + for prop in &dsl.properties { + if prop.name == "title" { + if let Some(RuntimeValue::Str(t)) = &prop.value { + let _ = write!(html, "

{}

", t); + } + } } + render_children(&mut html, dsl); + html.push_str("
"); + } + "footer" => { + html.push_str("
"); + for prop in &dsl.properties { + if prop.name == "text" { + if let Some(RuntimeValue::Str(t)) = &prop.value { + let _ = write!(html, "

{}

", t); + } + } + } + render_children(&mut html, dsl); + html.push_str("
"); + } + "input" => { + let placeholder = dsl + .properties + .iter() + .find(|p| p.name == "placeholder") + .and_then(|p| p.value.as_ref()) + .map(|v| v.to_string()); + if let Some(p) = placeholder { + let _ = write!(html, "", p); + } else { + html.push_str(""); + } + } + "form" => { + html.push_str("
"); + render_children(&mut html, dsl); + html.push_str("
"); + } + "main" => { + html.push_str("
"); + render_children(&mut html, dsl); + html.push_str("
"); + } + "aside" => { + html.push_str(""); + } + "start" => { + let label = dsl + .properties + .iter() + .find(|p| p.name == "label") + .and_then(|p| p.value.as_ref()) + .map(|v| v.to_string()) + .unwrap_or_else(|| "Get Started".to_string()); + let _ = write!(html, "{}", label); + } + _ => { + let _ = write!(html, "", dsl.kind); } } - render_children(&mut html, dsl); - html.push_str("
"); - } - "input" => { - let placeholder = dsl - .properties - .iter() - .find(|p| p.name == "placeholder") - .and_then(|p| p.value.as_ref()) - .map(|v| v.to_string()); - if let Some(p) = placeholder { - let _ = write!(html, "", p); - } else { - html.push_str(""); - } - } - "form" => { - html.push_str("
"); - render_children(&mut html, dsl); - html.push_str("
"); - } - "main" => { - html.push_str("
"); - render_children(&mut html, dsl); - html.push_str("
"); - } - "aside" => { - html.push_str(""); - } - "start" => { - let label = dsl - .properties - .iter() - .find(|p| p.name == "label") - .and_then(|p| p.value.as_ref()) - .map(|v| v.to_string()) - .unwrap_or_else(|| "Get Started".to_string()); - let _ = write!(html, "{}", label); - } - _ => { - let _ = write!(html, "", dsl.kind); + html } + _ => String::new(), } - html } impl StdlibRegistry { @@ -576,12 +576,12 @@ impl StdlibRegistry { arity: 0, callback: |ctx, _args| { let env = ctx.global_env.borrow(); - let items = match env.lookup("_dsl_blocks") { - Ok(RuntimeValue::List { items, .. }) => items, + let blocks = match env.lookup("_dsl_blocks") { + Ok(RuntimeValue::List { items, .. }) => items.borrow().clone(), _ => return Ok(RuntimeValue::Str(String::new())), }; let mut html = String::new(); - for block in &*items.borrow() { + for block in &blocks { html.push_str(&dsl_to_html(block)); } Ok(RuntimeValue::Str(html)) diff --git a/stdlib/tests/stdlib_tests.rs b/stdlib/tests/stdlib_tests.rs index 785a0793..8fe9cdf7 100644 --- a/stdlib/tests/stdlib_tests.rs +++ b/stdlib/tests/stdlib_tests.rs @@ -915,9 +915,11 @@ fn test_async_and_channels() { } #[test] -fn test_hash_operations() { +fn test_crypto_hash_and_compression() { let registry = StdlibRegistry::new(); + let crypto = registry.get_module("std.crypto").unwrap(); let hash = registry.get_module("std.hash").unwrap(); + let compress = registry.get_module("std.compress").unwrap(); let mut config_unprivileged = RuntimeConfig::default(); config_unprivileged @@ -925,6 +927,11 @@ fn test_hash_operations() { .remove(&Capability::FileSystem); let mut ctx_unprivileged = RuntimeContext::new(config_unprivileged); + let mut config_fs = RuntimeConfig::default(); + config_fs.capabilities.insert(Capability::FileSystem); + let mut ctx_fs = RuntimeContext::new(config_fs); + + // 1. Test hash operations let md5_fn = hash.exports.get("md5").unwrap(); let val = md5_fn .call( @@ -954,19 +961,8 @@ fn test_hash_operations() { ) .unwrap(); assert!(val.as_int().is_some()); -} - -#[test] -fn test_crypto_operations() { - let registry = StdlibRegistry::new(); - let crypto = registry.get_module("std.crypto").unwrap(); - - let mut config_unprivileged = RuntimeConfig::default(); - config_unprivileged - .capabilities - .remove(&Capability::FileSystem); - let mut ctx_unprivileged = RuntimeContext::new(config_unprivileged); + // 2. Test crypto operations (AES-GCM & Bcrypt) let aes_enc = crypto.exports.get("aes_encrypt").unwrap(); let aes_dec = crypto.exports.get("aes_decrypt").unwrap(); @@ -1014,23 +1010,8 @@ fn test_crypto_operations() { .call(&mut ctx_unprivileged, vec![pass, hashed.clone()]) .unwrap(); assert_eq!(is_valid.as_bool(), Some(true)); -} - -#[test] -fn test_compression_operations() { - let registry = StdlibRegistry::new(); - let compress = registry.get_module("std.compress").unwrap(); - - let mut config_unprivileged = RuntimeConfig::default(); - config_unprivileged - .capabilities - .remove(&Capability::FileSystem); - let mut ctx_unprivileged = RuntimeContext::new(config_unprivileged); - - let mut config_fs = RuntimeConfig::default(); - config_fs.capabilities.insert(Capability::FileSystem); - let mut ctx_fs = RuntimeContext::new(config_fs); + // 3. Test compression capabilities & operations let temp_dir = std::env::temp_dir().join("techscript_archive_test"); std::fs::create_dir_all(&temp_dir).ok(); @@ -1092,10 +1073,15 @@ fn test_graphics_canvas_drawing() { .remove(&Capability::FileSystem); let mut ctx_unprivileged = RuntimeContext::new(config_unprivileged); + let mut config_fs = RuntimeConfig::default(); + config_fs.capabilities.insert(Capability::FileSystem); + let mut ctx_fs = RuntimeContext::new(config_fs); + let create_canvas_fn = graphics.exports.get("create_canvas").unwrap(); let draw_rect_fn = graphics.exports.get("draw_rect").unwrap(); let draw_circle_fn = graphics.exports.get("draw_circle").unwrap(); let draw_line_fn = graphics.exports.get("draw_line").unwrap(); + let save_png_fn = graphics.exports.get("save_png").unwrap(); // 1. Create a 100x100 canvas let canvas_handle_val = create_canvas_fn @@ -1151,34 +1137,6 @@ fn test_graphics_canvas_drawing() { ], ) .unwrap(); -} - -#[test] -fn test_graphics_canvas_save_png() { - let registry = StdlibRegistry::new(); - let graphics = registry.get_module("std.graphics").unwrap(); - - let mut config_unprivileged = RuntimeConfig::default(); - config_unprivileged - .capabilities - .remove(&Capability::FileSystem); - let mut ctx_unprivileged = RuntimeContext::new(config_unprivileged); - - let mut config_fs = RuntimeConfig::default(); - config_fs.capabilities.insert(Capability::FileSystem); - let mut ctx_fs = RuntimeContext::new(config_fs); - - let create_canvas_fn = graphics.exports.get("create_canvas").unwrap(); - let save_png_fn = graphics.exports.get("save_png").unwrap(); - - // Create a canvas to save - let canvas_handle_val = create_canvas_fn - .call( - &mut ctx_unprivileged, - vec![RuntimeValue::Int(100), RuntimeValue::Int(100)], - ) - .unwrap(); - let handle = canvas_handle_val.as_int().unwrap(); // 3. Save to PNG file (requires FileSystem capability) let temp_file = std::env::temp_dir().join("test_canvas.png");