Replies: 1 comment
-
Since the idea code generation seems to be a common and reasonable thought, I want to elaborate in a bit more detail. First of all, I don't think it loses its meaning at all if there's no direct code generation. I'd say there are in general two very different possible approches in this context:
While the first option is useful in many domains and can be done now using Ryven Console or ryvencore directly, the second option is just one possible group of domains, amongst many others. Ryven, however, is a general purpose editor, it's not supposed to optimise towards specific domains as this would inevitably punish the other ones. In other words, Ryven is much more general, the nodes are just python classes, so they execute python code when according events are triggered (like the If you want your nodes to generate source code, just go ahead and program them such that they do. The process of generating the code is exactly what the node is doing then. Also, since every node in Ryven has direct access to the whole ryvencore session, they could even analyze their flow to make this code generation much more intelligent. ExampleThere are many different ways to implement this. You could build your own small parsers for some kind of metacode language to easily describe the resulting source code of a node (I actually did that two years ago for a predecessor project), or you can generate the code more manually. One prototype I wrote yesterday which uses the latter approach (because way more dynamic) is the following node class as base for source code generating nodes: class CodeGenNode(Node):
INDENT = 4
ID_CTR = 0 # for nodes which need unique descriptors for variables
GENERATING_CODE = False
def __init__(self, params):
super().__init__(params)
self.code: str = None
# generate unique ID
self.cgn_id = CodeGenNode.ID_CTR
CodeGenNode.ID_CTR += 1
def id(self):
return self.cgn_id
def indent(self, code: str, times: int = 1):
"""add indent to all lines of code"""
return '\n'.join([
' '*self.INDENT*times + l
for l in code.splitlines(keepends=False)
])
def inp_code(self, index: int):
"""get the code of some data input"""
return self.input(index) # no modifications for now
def out_code(self, index: int):
"""generates and returns the complete sub-graph src code at some exec output"""
if len(self.outputs[index].connections) > 0:
self.exec_output(index)
s = '\n'.join([c.inp.node.code for c in self.outputs[index].connections])
else:
s = ''
return s
def get_code(self, inp: int) -> str:
"""
Reimplement this method. All data outputs should be set here and output code
should be generated based on the inp parameter. Do not generate output code
for active nodes without checking on inp.
"""
# usual process:
# 1) set data all outputs
# 2) then check on inp
# 3) generate and return source code
pass
def generate_code(self, inp=-1) -> str:
"""starts the code gen process and returns the code generated from this node"""
CodeGenNode.GENERATING_CODE = True
self.update(inp)
CodeGenNode.GENERATING_CODE = False
return self.code
def update_event(self, inp=-1):
if self.GENERATING_CODE:
self.code = self.get_code(inp) Based on this as parent class, you could then implement source code generating nodes, like this JavaScript for-loop class ForLoopNode(CodeGenNode):
title = 'ForLoop'
init_inputs = [
NodeInputBP(type_='exec'),
NodeInputBP('From', dtype=dtypes.Data(0)),
NodeInputBP('To', dtype=dtypes.Data(0)),
NodeInputBP('Incr', dtype=dtypes.Data(1)),
]
init_outputs = [
NodeOutputBP('Loop Body', type_='exec'),
NodeOutputBP('Complete', type_='exec'),
NodeOutputBP('Index'),
]
def get_code(self, inp: int) -> str:
i = f'i{self.id()}'
self.set_output_val(2, i)
if inp == 0:
return \
f'''for(let {i} = {self.inp_code(1)}; {i} < {self.inp_code(2)}; {i} += {self.inp_code(3)}){{
{self.indent(self.out_code(0))}
}}
{self.out_code(1)}''' and this print class PrintNode(CodeGenNode):
[...]
def get_code(self, inp: int) -> str:
if inp == 0:
return \
f'''console.log({self.inp_code(1)});
{self.out_code(0)}''' I used this to make a copy of this JS demo. The implementation above currently runs recursive, because the nodes are executed recursively, but I'd like to add support for iterative flow execution for Ryven anyway, so I'll leave it like this for now. You might also want to add an infinite feedback loop checker. Hope this helps. Any thoughts, any ideas, any showcases, just write a post here. |
Beta Was this translation helpful? Give feedback.
-
Great idea, cool interface.
But it loses its meaning if is not possible to save (export) a created program as a python script.
I think it would be logical to add this possibility (File => Export => Python script).
Beta Was this translation helpful? Give feedback.
All reactions