<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0">
<channel>
<title>Coder Legion - Recent posts tagged specification</title>
<link>https://coderlegion.com/tag/specification</link>
<description>Powered by Question2Answer</description>
<item>
<title>DevLog 20250816: Divooka Language Specification (Preliminary Look)</title>
<link>https://coderlegion.com/4757/devlog-20250816-divooka-language-specification-preliminary-look</link>
<description>&lt;h3 id=&quot;overview&quot;&gt;Overview&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Divooka&lt;/strong&gt; is a cutting-edge visual programming platform developed by Methodox Technologies, Inc. It enables users to build and deploy complex applications through a drag-and-drop, node-based interface that integrates seamlessly with C# and Python libraries.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Key Features&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;General-Purpose &amp;amp; Flexible&lt;/strong&gt;: Suitable for a wide range of use cases - from business tools to innovative software products - supporting both automation and application development.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Node-Based Visual Interface&lt;/strong&gt;: Workflows are constructed visually by connecting nodes that represent data operations, logic, APIs, and more.&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Multiple Editions&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Divooka Explore&lt;/strong&gt;: A beginner-friendly, Windows-only edition designed for learning, data analytics, dashboards, programming, and everyday utilities.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Divooka Compute&lt;/strong&gt;: A professional package built on the same engine, aimed at power users. Currently available as a Windows preview build, with Linux and macOS support planned for the full release.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cross-Platform Vision&lt;/strong&gt;: While early versions support Windows, full Linux and macOS support is planned.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Strong Architectural Foundations&lt;/strong&gt;: Based on Data-Driven Design principles, Divooka emphasizes modular, external control of behavior through data files - streamlining workflows without modifying code.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Developer-Centric Philosophy&lt;/strong&gt;: Divooka is not a new programming language per se. Instead, it focuses on decoupling functionality from specific environments to maximize flexibility and usability.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Active Development &amp;amp; Community&lt;/strong&gt;: Ongoing updates, documentation (wiki), tutorials, a Discord community, and blog posts ensure an active ecosystem.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Divooka is built around &lt;strong&gt;node graphs as executable documents&lt;/strong&gt;. Instead of writing sequential code, developers construct graphs of nodes, where each node represents a unit of computation or data. This graph-based approach supports both &lt;strong&gt;dataflow-oriented&lt;/strong&gt; and &lt;strong&gt;procedural-oriented&lt;/strong&gt; paradigms. A Divooka script file (a &quot;Divooka Document&quot;) acts as a container for node graphs.&lt;/p&gt;
&lt;p&gt;At its simplest:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;A Divooka document contains multiple graphs.&lt;/li&gt;
&lt;li&gt;Each graph contains multiple nodes.&lt;/li&gt;
&lt;li&gt;Nodes have a type, an optional ID, and attributes.&lt;/li&gt;
&lt;li&gt;Node attributes can connect to other nodes’ attributes.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;In a &lt;a rel=&quot;nofollow&quot; href=&quot;https://wiki.methodox.io/en/Divooka/Language/DataflowContext&quot;&gt;Dataflow Context&lt;/a&gt;, node connections are acyclic; in a &lt;a rel=&quot;nofollow&quot; href=&quot;https://wiki.methodox.io/en/Divooka/Language/ProceduralContext&quot;&gt;Procedural Context&lt;/a&gt;, connections may be cyclic and more flexible.&lt;/p&gt;
&lt;h3 id=&quot;interpretation&quot;&gt;Interpretation&lt;/h3&gt;
&lt;p&gt;To illustrate the simplicity of the language, we can write a minimal interpreter in &lt;strong&gt;Python&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://coderlegion.com/?qa=blob&amp;amp;qa_blobid=12773764991494639793&quot; alt=&quot;Diagram&quot;&gt;&lt;/p&gt;
&lt;p&gt;This interpreter handles an acyclic graph of nodes with &lt;code&gt;Type&lt;/code&gt;, &lt;code&gt;ID&lt;/code&gt;, attributes (all strings), and connections between attributes. Connections are represented directly as attribute values: if an attribute value starts with &lt;code&gt;@&lt;/code&gt;, it refers to another node’s attribute (e.g., &lt;code&gt;@Node1.Value&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;For example:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;DefineNumber&lt;/code&gt; outputs a number in its &lt;code&gt;Value&lt;/code&gt; attribute.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;AddNumbers&lt;/code&gt; takes two numbers as inputs and produces a &lt;code&gt;Result&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Print&lt;/code&gt; consumes the &lt;code&gt;Result&lt;/code&gt; and prints it.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The interpreter maps node types to operators, executes them, and produces results.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;# minimal_graph_interpreter.py
# A tiny, in-memory, non-cyclic node graph + interpreter.
# Nodes have: Type, ID, attrs (all strings). Connections are '@NodeID.Attr'.

from typing import Dict, Any, List, Callable, Optional, Tuple

Node = Dict[str, Any]  # {&quot;ID&quot;: str, &quot;Type&quot;: str, &quot;attrs&quot;: {str:str}, &quot;state&quot;: {str:Any}}

def is_ref(value: Any) -&amp;gt; bool:
    return isinstance(value, str) and value.startswith(&quot;@&quot;) and &quot;.&quot; in value[1:]

def parse_ref(ref: str) -&amp;gt; Tuple[str, str]:
    # &quot;@NodeID.Attr&quot; -&amp;gt; (&quot;NodeID&quot;, &quot;Attr&quot;)
    target = ref[1:]
    node_id, attr = target.split(&quot;.&quot;, 1)
    return node_id, attr

def to_number(s: Any) -&amp;gt; Optional[float]:
    if isinstance(s, (int, float)):
        return float(s)
    if not isinstance(s, str):
        return None
    try:
        return float(int(s))
    except ValueError:
        try:
            return float(s)
        except ValueError:
            return None

class Interpreter:
    def __init__(self, nodes: List[Node]):
        # normalize nodes and build index
        self.nodes: List[Node] = []
        self.by_id: Dict[str, Node] = {}
        for n in nodes:
            node = {&quot;ID&quot;: n[&quot;ID&quot;], &quot;Type&quot;: n[&quot;Type&quot;], &quot;attrs&quot;: dict(n.get(&quot;attrs&quot;, {})), &quot;state&quot;: {}}
            self.nodes.append(node)
            self.by_id[node[&quot;ID&quot;]] = node

        # map Type -&amp;gt; evaluator
        self.ops: Dict[str, Callable[[Node], bool]] = {
            &quot;DefineNumber&quot;: self.op_define_number,
            &quot;AddNumbers&quot;: self.op_add_numbers,
            &quot;Print&quot;: self.op_print,
        }

    # ---- helpers ----
    def get_attr_value(self, node_id: str, attr: str) -&amp;gt; Any:
        &quot;&quot;&quot;Return the most 'evaluated' value for an attribute (state overrides attrs).&quot;&quot;&quot;
        node = self.by_id.get(node_id)
        if not node:
            return None
        if attr in node[&quot;state&quot;]:
            return node[&quot;state&quot;][attr]
        return node[&quot;attrs&quot;].get(attr)

    def resolve(self, raw: Any) -&amp;gt; Any:
        &quot;&quot;&quot;Dereference '@Node.Attr' chains once (graph is acyclic so one hop is enough).&quot;&quot;&quot;
        if is_ref(raw):
            nid, a = parse_ref(raw)
            return self.get_attr_value(nid, a)
        return raw

    def all_resolved(self, values: List[Any]) -&amp;gt; bool:
        return all(not is_ref(v) and v is not None for v in values)

    # ---- operators ----
    def op_define_number(self, node: Node) -&amp;gt; bool:
        # Input: attrs[&quot;Value&quot;] (string number). Output: state[&quot;Value&quot;] (numeric)
        if &quot;Value&quot; in node[&quot;state&quot;]:
            return False  # already done
        raw = node[&quot;attrs&quot;].get(&quot;Value&quot;)
        val = self.resolve(raw)
        num = to_number(val)
        if num is None:
            return False  # can't parse yet
        node[&quot;state&quot;][&quot;Value&quot;] = num
        return True

    def op_add_numbers(self, node: Node) -&amp;gt; bool:
        # Inputs: attrs[&quot;Value1&quot;], attrs[&quot;Value2&quot;] (can be @ refs). Output: state[&quot;Result&quot;]
        if &quot;Result&quot; in node[&quot;state&quot;]:
            return False
        v1 = to_number(self.resolve(node[&quot;attrs&quot;].get(&quot;Value1&quot;)))
        v2 = to_number(self.resolve(node[&quot;attrs&quot;].get(&quot;Value2&quot;)))
        if v1 is None or v2 is None:
            return False
        node[&quot;state&quot;][&quot;Result&quot;] = v1 + v2
        return True

    def op_print(self, node: Node) -&amp;gt; bool:
        # Input: attrs[&quot;Result&quot;] (@ ref). Side effect: print once. Also store state[&quot;Printed&quot;]=True
        if node[&quot;state&quot;].get(&quot;Printed&quot;):
            return False
        r = self.resolve(node[&quot;attrs&quot;].get(&quot;Result&quot;))
        # Allow printing numbers or strings once the reference resolves
        if r is None or is_ref(r):
            return False
        print(r)
        node[&quot;state&quot;][&quot;Printed&quot;] = True
        return True

    # ---- execution ----
    def step(self) -&amp;gt; bool:
        &quot;&quot;&quot;Try to make progress by evaluating any node whose inputs are ready.&quot;&quot;&quot;
        progressed = False
        for node in self.nodes:
            op = self.ops.get(node[&quot;Type&quot;])
            if not op:
                # Unknown node type: ignore
                continue
            progressed = op(node) or progressed
        return progressed

    def run(self, max_iters: int = 100):
        &quot;&quot;&quot;Iteratively evaluate until no changes (DAG assumed, so this stabilizes quickly).&quot;&quot;&quot;
        for _ in range(max_iters):
            if not self.step():
                return
        raise RuntimeError(&quot;Exceeded max iterations (graph might be cyclic or ill-formed).&quot;)


if __name__ == &quot;__main__&quot;:
    # --- Example in-memory graph ---
    graph = [
        {&quot;ID&quot;: &quot;Node1&quot;, &quot;Type&quot;: &quot;DefineNumber&quot;, &quot;attrs&quot;: {&quot;Value&quot;: &quot;3&quot;}},
        {&quot;ID&quot;: &quot;Node2&quot;, &quot;Type&quot;: &quot;DefineNumber&quot;, &quot;attrs&quot;: {&quot;Value&quot;: &quot;5&quot;}},
        {
            &quot;ID&quot;: &quot;Adder&quot;,
            &quot;Type&quot;: &quot;AddNumbers&quot;,
            &quot;attrs&quot;: {&quot;Value1&quot;: &quot;@Node1.Value&quot;, &quot;Value2&quot;: &quot;@Node2.Value&quot;},
        },
        {&quot;ID&quot;: &quot;Printer&quot;, &quot;Type&quot;: &quot;Print&quot;, &quot;attrs&quot;: {&quot;Result&quot;: &quot;@Adder.Result&quot;}},
    ]

    interp = Interpreter(graph)
    interp.run()   # Should print: 8.0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Running the example graph prints:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;8.0
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;summary&quot;&gt;Summary&lt;/h3&gt;
&lt;p&gt;The Divooka language demonstrates how a &lt;strong&gt;minimalist graph-based specification&lt;/strong&gt; can serve as a foundation for both &lt;strong&gt;computation and orchestration&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Key takeaways:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Node-Centric Abstraction&lt;/strong&gt;: Everything is reduced to nodes with types, IDs, and attributes - uniform, extensible, and easy to interpret.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Simple Reference Mechanism&lt;/strong&gt;: The &lt;code&gt;@NodeID.Attr&lt;/code&gt; convention provides a straightforward but powerful way to connect attributes.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Separation of Concerns&lt;/strong&gt;: Distinguishing between &lt;em&gt;dataflow&lt;/em&gt; (acyclic, deterministic) and &lt;em&gt;procedural&lt;/em&gt; (control flow, cyclic) contexts allows Divooka to cover both declarative and imperative styles.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Composable Operators&lt;/strong&gt;: Even with just three operators (&lt;code&gt;DefineNumber&lt;/code&gt;, &lt;code&gt;AddNumbers&lt;/code&gt;, &lt;code&gt;Print&lt;/code&gt;), meaningful behaviors emerge.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Compact Interpreter Footprint&lt;/strong&gt;: The entire interpreter is under 200 lines of Python, demonstrating the specification’s simplicity and rapid prototyping potential.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;One might ask why not use traditional graph connections. The answer is simplicity: defining connections as local attribute references reduces structure while keeping graphs clean. In dataflow, inputs typically come from a single source, while in procedural contexts, outputs are unique but inputs may be shared, so we can just reverse the syntax - making this lightweight approach intuitive and efficient.&lt;/p&gt;
&lt;h3 id=&quot;reference&quot;&gt;Reference&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Wiki (WIP): &lt;a rel=&quot;nofollow&quot; href=&quot;https://wiki.methodox.io/en/Standardization/DiLS&quot;&gt;Divooka Language Specification&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
<category>Divooka</category>
<guid isPermaLink="true">https://coderlegion.com/4757/devlog-20250816-divooka-language-specification-preliminary-look</guid>
<pubDate>Sat, 16 Aug 2025 19:41:04 +0000</pubDate>
</item>
</channel>
</rss>