Jaseci-Labs / jaseci

The Official Jaseci Code Repository
https://jaseci.org
154 stars 207 forks source link

[JacMachine] Dynamic Architype Creation #1331

Open ChrisIsKing opened 4 days ago

ChrisIsKing commented 4 days ago

The current JacMachine VM provides a robust framework for handling operations on loaded Jac programs, including listing and updating nodes, walkers, and other key components. For example:

import:py from jaclang.runtimelib.machine {JacMachine}

walker test_walker {

    can visit_nodes with `root entry {
        visit [-->](`?test_node);
    }
}

walker child_walker :test_walker: {}

node test_node {
    has value:int;

    can print_value with test_walker entry {
        print("Value:", f'{self.value}');
    }
}

with entry {
    modules = JacMachine.get().list_modules();
    print(modules);
    nodes = JacMachine.get().list_nodes('__main__');
    print(nodes);
}

To further enhance the flexibility of JacMachine, we propose an extension to allow for the dynamic creation of architypes (such as nodes and walkers) through direct source code input via the VM interface. This would allow developers to define new architypes at runtime without needing to predefine them in static code.

A desired interface for this feature might look like the following:

import:py from jaclang.runtimelib.machine {JacMachine}

# Dynamically create a node architype
source_code = """
node dynamic_node {
    has value:int;
    can print_value with entry {
        print("Dynamic Node Value:", f'{self.value}');
    }
}
"""

# Create a new walker architype dynamically
walker_code = """
walker dynamic_walker {
    can visit_nodes with entry {
        visit [-->](`?dynamic_node);
    }
}
"""

with entry {
    # Dynamically define the architypes within JacMachine
    JacMachine.get().create_architype_from_source(source_code);
    JacMachine.get().create_architype_from_source(walker_code);

    # Instantiate dynamically created node and walker
    node_obj = JacMachine.get().spawn_node('dynamic_node', {'value': 99});
    node_obj.print_value();

    walker_obj = JacMachine.get().spawn_walker('dynamic_walker');
    root spawn walker_obj;
}

Considerations/Requirements:

ChrisIsKing commented 4 days ago

So for the dependency stuff @AshishMahendra I'm thinking we can do something like:

import:py from jaclang.runtimelib.machine {JacMachine}

# Dynamic node source code with Jac and Python module imports
node_source_code = """
node test_node {
    has value:int;

    can calculate_value with entry {
        import:py random;                 # Python module
        import:jac my_jac_module;         # Jac module within current namespace

        let random_value = random.randint(1, 100);
        self.value = my_jac_module.add_value(self.value, random_value);  # Using Jac module functionality
        print("Calculated Value:", f'{self.value}');
    }
}
"""

with entry {
    # Specify allowed modules with flexibility for both Jac and Python
    allowed_modules = {
        'python': ['random', 'math'],               # Python modules
        'jac': ['my_jac_module', 'another_module']  # Jac modules (within the current namespace or globally)
    }

    # Dynamically define the node architype with module injection
    JacMachine.get().create_architype_from_source(
        node_source_code, 
        allowed_modules=allowed_modules
    )

    # Instantiate the dynamically created node
    node_obj = JacMachine.get().spawn_node('test_node', {'value': 42})
    node_obj.calculate_value()
}