Просмотр исходного кода

app: pydrk CLI update with missing shape fns

darkfi 2 недель назад
Родитель
Сommit
5fd59e27a4
3 измененных файлов с 43 добавлено и 3 удалено
  1. 7 0
      bin/app/pydrk/cli.py
  2. 34 1
      bin/app/src/net.rs
  3. 2 2
      openspec/changes/app-pydrk-cli/tasks.md

+ 7 - 0
bin/app/pydrk/cli.py

@@ -38,6 +38,7 @@ def error_name(err):
         return str(err)
     name = type(err).__name__
     name = name.replace("ID", "Id")
+    name = name.replace("SExpr", "Sexpr")
     return re.sub(r"(?<!^)(?=[A-Z])", "_", name).lower()
 
 
@@ -923,6 +924,12 @@ def run_selftests():
     assert NODE_TYPE_NAMES[SceneNodeType.VECTOR_ART] == "vector_art"
     assert NODE_TYPE_NAMES[SceneNodeType.PLUGIN_ROOT] == "plugin_root"
 
+    assert error_name(exc.SExprGlobalNotFound()) == "sexpr_global_not_found"
+    assert error_name(exc.PropertySExprNotAllowed()) == "property_sexpr_not_allowed"
+    assert error_name(exc.PropertyOutOfRange()) == "property_out_of_range"
+    assert error_name(exc.PropertyNotFound()) == "property_not_found"
+    assert error_name(exc.NodeSiblingNameConflict()) == "node_sibling_name_conflict"
+
     assert resolve_path([], "/") == "/"
     assert resolve_path([], "") == "/"
     assert resolve_path([], "..") == "/"

+ 34 - 1
bin/app/src/net.rs

@@ -24,7 +24,7 @@ use zeromq::{Socket, SocketRecv, SocketSend};
 use crate::{
     app::node::{create_layer, create_vector_art},
     error::{Error, Result},
-    expr::{decompile, Compiler},
+    expr::{decompile, MachineGlobals, Compiler, SExprCode, SExprMachine, SExprVal},
     gfx::{gfxtag, Renderer},
     prop::{PropertyType, Role},
     scene::{Pimpl, SceneNodeId, SceneNodePtr, SceneNodeType, ScenePath, Slot},
@@ -64,6 +64,21 @@ fn stop_ui_subtree(node: &SceneNodePtr) {
     }
 }
 
+/// Run a freshly compiled expr on a throwaway machine so unknown
+/// variables (typos) reject the set request instead of failing silently
+/// on every eval afterwards. The dummy values are irrelevant; only name
+/// resolution matters and the globals are discarded. `global_names` are
+/// the variable names the property's real eval can provide.
+fn check_expr(code: &SExprCode, global_names: &[String]) -> Result<()> {
+    let mut globals: MachineGlobals = vec![];
+    for name in global_names {
+        globals.push((name.clone(), SExprVal::Float32(1.)));
+    }
+    let mut machine = SExprMachine { globals, stmts: code };
+    machine.call()?;
+    Ok(())
+}
+
 const USE_IPV6: bool = true;
 
 #[derive(Debug, SerialDecodable)]
@@ -350,6 +365,19 @@ impl ZeroMQAdapter {
                         let expr_str = String::decode(&mut cur).unwrap();
                         debug!(target: "req", "  compiling expr \"{expr_str}\"");
                         let code = Compiler::new().compile(&expr_str)?;
+                        // The property's eval site provides its depends
+                        // names plus one of the machine global sets in
+                        // use (w, h for most rects, parent_*/rect_* for
+                        // edit behaves), so accept the union and treat
+                        // anything else as a typo.
+                        let mut names: Vec<String> =
+                            prop.get_depends().into_iter().map(|d| d.local_name).collect();
+                        names.extend(
+                            ["w", "h", "parent_w", "parent_h", "rect_w", "rect_h"].iter().map(
+                                |s| s.to_string(),
+                            ),
+                        );
+                        check_expr(&code, &names)?;
                         prop.set_expr(atom, Role::User, prop_i, code)?;
                     }
                     PropertyType::VectorShape => {
@@ -359,6 +387,9 @@ impl ZeroMQAdapter {
                         // string, y expr string, 4x f32 color; index count
                         // varint; u16 indices.
                         let cc = Compiler::new();
+                        // Shape verts eval with only the w/h globals.
+                        let shape_globals: Vec<String> =
+                            ["w", "h"].iter().map(|s| s.to_string()).collect();
                         let vert_count = VarInt::decode(&mut cur)?.0 as usize;
                         let mut verts = vec![];
                         for _ in 0..vert_count {
@@ -372,6 +403,8 @@ impl ZeroMQAdapter {
                             ];
                             let x = cc.compile(&x_src)?;
                             let y = cc.compile(&y_src)?;
+                            check_expr(&x, &shape_globals)?;
+                            check_expr(&y, &shape_globals)?;
                             verts.push(ShapeVertex::new(x, y, color));
                         }
                         let index_count = VarInt::decode(&mut cur)?.0 as usize;

+ 2 - 2
openspec/changes/app-pydrk-cli/tasks.md

@@ -78,7 +78,7 @@ run from `bin/app`.
   live: `python -m pydrk set /window/content rect 2 "w/2" --expr`
   exits 0 and `python -m pydrk get /window/content rect 2` shows
   `2: expr "w/2"`. Commit as `app: pydrk CLI set --expr`.
-- [ ] 2.3 Verify the failure paths end-to-end: `python -m pydrk set
+- [x] 2.3 Verify the failure paths end-to-end: `python -m pydrk set
   /window/content alpha 5.0` prints `property_out_of_range`;
   `python -m pydrk set /window no_such_prop 1` prints
   `property_not_found`; `set --expr "q/3"` on a rect index prints
@@ -160,7 +160,7 @@ run from `bin/app`.
   /window/content/dbg/art1 --box 0 0 w 10 1 0 0 1` — a red bar renders
   along the top of the window. Commit as
   `app: pydrk CLI set-shape with box primitive`.
-- [ ] 5.2 Add the remaining primitives from design D11: `--gbox`,
+- [x] 5.2 Add the remaining primitives from design D11: `--gbox`,
   `--vgradient`, `--outline`, `--line`, `--glow` (colors inline as
   trailing R G B A float args; coordinates may be expr strings). Verify
   live by composing one shape using at least `--vgradient`, `--outline`