-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathtranslator.py
766 lines (620 loc) · 26 KB
/
translator.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
import os
import tests.cases as cases
import luaparser.ast as ast
import luaparser.astnodes as astnodes
from utils.random_util import RandomUtil
from utils.graph_util import *
from IR_graph import IRGraph
from IR_nodes import *
from typing import List
import coloredlogs, logging
# windows only
if os.name == 'nt':
os.environ["PATH"] += os.pathsep + 'C:/Program Files/Graphviz/bin/'
os.environ["COLOREDLOGS_LOG_FORMAT"] ='[%(hostname)s] %(funcName)s :: %(levelname)s :: %(message)s'
coloredlogs.install(level='DEBUG')
'''
Converts lua code to an event-driven finite state machine (substitute for coroutines)
- Splits functions into multiple functions, seperated by async events
- Async events defined by await(foo()) calls
- Handles events inside conditional blocks
Limitations:
- No recursion
- No nested functions (does not include anonymous functions)
TODO:
- Anonymous functions
- Error handling on invalid syntax
- Error handling on detected recursion
- Function closure (function inside function)
- Loops
- Async assignments
- Gotos and labels
- Objects/Methods
- Track global table of async functions
- Error handling on missing await()
- Update local and global assignments/references to global table
'''
'''
Constants
'''
EVENT_PTR_TABLE_NAME = 'global.event_ptrs'
EVENT_NAME_TABLE_NAME = 'global.event_names'
GENERATE_EVENT_NAME_FUNCTION_NAME = 'script.generate_event_name'
REGISTER_EVENT_FUNCTION_NAME = 'script.on_event'
'''
################################################
TRANSLATOR
################################################
'''
class Translator:
def __init__(self, source_lua_root_node, render_visual_graph):
self.source_lua_root_node = source_lua_root_node
self.render_visual_graph = render_visual_graph
# Graphs
self.IR_graph = IRGraph()
self.exeuction_IR_graphs = [self.IR_graph]
# Links
self.links = []
# Main function tracking
self.main_function_name = None
self.function_count = 0
self.inside_main_function = False
# Variable reference tracking
self.variable_refs = {}
'''
################################################
IR TRANSLATION
################################################
'''
'''
3. Construct Execution Graph:
- Separate async statements:
-- Find async node (x), add children of (x) to new IR graph, replace child of (x) with link to new IR graph
- Linearize Branches:
-- Find node (x) with a branch child, add other children of (x) to a new IR graph, then append it to the end
of each execution path of the branch child as a link node.
-- If there is no else statement present, create one
'''
'''
Translating is done as follows:
1. Build/Expand IR graph:
- Build the Intermediate Representation (IR) graph tree
-- 1a. (Build) Travsering from the root, find all regular, conditional and loop nodes and add them to the graph linearly
(without going into the branches or loops)
-- 1b. (Expand) For each node with a block, enter the block
-- 1c. Mark whether the block contains an async call/assignment
-- 1d. goto 1a
x. Check for recusion
- Make sure no functions recurse. If they do, then throw an error
x. Mark async blocks
- Find blocks that contain async statements and mark them for later
x. Modify Assignments:
- Change local assignments to global assignments
- Track all variables in variable reference table
x. Modify branches:
- Unpack assignments in conditionals
- If there is no else statement present, create one and put the post execution tree under the new else node
x. Construct Execution Graphs:
- Linearize:
-- Find node (x) with a branch child, add other children of (x) to a new IR graph, then append it to the end
of each execution path of the branch child as a link node.
- Separate:
-- Find async node (x), add children of (x) to new IR graph, replace child of (x) with link to new IR graph
x. Extract functions:
- Extract and link functions together
x. Insert event pointers:
- For each async node and its following link, insert a node that will set the event pointer before the async node
x. Construct AST headers:
- Secrete a new lua AST
x. Construct full AST
- Secrete the full lua AST
'''
def translate(self):
# Stage 1
# Build the graph tree
logging.info(f"Building IR graph from Lua source")
self.build_IR_graph(self.source_lua_root_node)
if self.render_visual_graph:
render_visual_graph(output_graph_name="IR_graph", root_nodes=[self.IR_graph.root_node])
logging.info(f"Expanding IR graph")
self.expand_nodes(self.IR_graph.root_node)
if self.render_visual_graph:
render_visual_graph(output_graph_name="Expanded_IR_graph", root_nodes=[self.IR_graph.root_node])
# logging.info(f"Checking for recursion")
# try:
# self.check_for_recursion(self.IR_graph.root_node)
# except Exception():
# logging.error("Recursion detected")
# logging.info("Modifying assignments to global assignments")
# # self.modify_assignments(self.IR_graph.root_node)
# if self.render_visual_graph:
# render_visual_graph(output_graph_name="Modified_assignments_IR_graph", root_nodes=[self.IR_graph.root_node])
# logging.info("Updating assignment references to global references")
# # self.update_assignment_references(self.IR_graph.root_node)
# if self.render_visual_graph:
# render_visual_graph(output_graph_name="Modified_assignments_IR_graph", root_nodes=[self.IR_graph.root_node])
logging.info(f"Linearizing branches into exeuction graphs")
self.linearize_branches()
if self.render_visual_graph:
root_nodes = [graph.root_node for graph in self.exeuction_IR_graphs]
# root_nodes.append(self.IR_graph.root_node)
render_visual_graph(output_graph_name="linearized_branches_IR_graphs", root_nodes=root_nodes)
logging.info(f"Separating async statements into exeuction graphs")
self.separate_async_statements()
if self.render_visual_graph:
root_nodes = [graph.root_node for graph in self.exeuction_IR_graphs]
# root_nodes.append(self.IR_graph.root_node)
render_visual_graph(output_graph_name="seperated_async_IR_graphs", root_nodes=root_nodes)
logging.info(f"Inserting event pointers")
self.insert_event_pointers()
if self.render_visual_graph:
root_nodes = [graph.root_node for graph in self.exeuction_IR_graphs]
# root_nodes.append(self.IR_graph.root_node)
render_visual_graph(output_graph_name="event_ptrs_IR_graphs", root_nodes=root_nodes)
logging.info(f"Constructing new AST")
self.construct_ast()
def build_IR_graph(self, node):
# First collect regular/async statements, branches and loops (without entering)
logging.debug("Collecting nodes")
self.visit(node)
# Enter the bodies of if statements and loops
def expand_nodes(self, node):
print(node)
print(type(node))
# If the node is a branch, expand the children
if isinstance(node, GeneratedBranchIRGraphNode):
# The lua node for the branch holds the "if" lua node
# Each subsequent conditional is found in the 'orelse' field
if_node = node.lua_node
# Find all conditional branches
conditional_nodes = [ConditionalIRGraphNode(lua_node=if_node)]
lookahead_node = if_node.orelse
while(isinstance(lookahead_node, astnodes.ElseIf)):
logging.debug(f"Found ElseIf")
conditional_nodes.append(ConditionalIRGraphNode(lua_node=lookahead_node))
lookahead_node = lookahead_node.orelse
if(lookahead_node is not None):
# Else statement is of type list<Statement>
logging.debug(f"Found Else")
node.else_statement_present = True
conditional_nodes.append(ConditionalIRGraphNode(lua_node=lookahead_node, name="Else"))
# Add block for body
body_block_node = GeneratedBlockIRGraphNode()
self.IR_graph.pointer = node
self.IR_graph.add_node(body_block_node)
for conditional_node in conditional_nodes:
self.IR_graph.pointer = body_block_node
self.IR_graph.add_node(conditional_node)
# Visit each of the conditionals in the body
for conditional_node in body_block_node.children:
self.IR_graph.pointer = conditional_node
self.visit(conditional_node.lua_node.body)
# If the node is a loop, expand
elif isinstance(node, LoopIRGraphNode):
self.IR_graph.pointer = node
if isinstance(node, FornumIRGraphNode):
self.IR_graph.add_node(GeneratedBlockIRGraphNode())
self.visit(node.lua_node.body)
if isinstance(node, ForinIRGraphNode):
self.IR_graph.add_node(GeneratedBlockIRGraphNode())
self.visit(node.lua_node.body)
if isinstance(node, WhileIRGraphNode):
self.IR_graph.add_node(GeneratedBlockIRGraphNode())
self.visit(node.lua_node.body)
if isinstance(node, RepeatIRGraphNode):
self.IR_graph.add_node(GeneratedBlockIRGraphNode())
self.visit(node.lua_node.body)
# Expand each child
for child in node.children:
self.expand_nodes(child)
# def modify_assignments(self, node):
# # If the node is an assignment node
# if isinstance(node, RegularAssignIRGraphNode, LocalAssignIRGraphNode):
# # Construct the new lua node
# global_assign_lua_node = self.construct_global_assign_lua_node()
# # Create a new GlobalAssignIRGraphNode
# newAssignNode = GlobalAssignIRGraphNode()
# node.__class__ = GlobalAssignIRGraphNode
# # Add the variables to the modified_vars set
# for target in node.lua_node.targets:
# if isinstance(target, astnodes.Name):
# self.modified_vars.add(target.id)
# # Visit the child nodes
# for child in node.children:
# self.modify_assignments(child)
# def update_references(self, node):
# # If the node is an expression node that references a variable
# if isinstance(node, RegularIRGraphNode) and isinstance(node.lua_node, astnodes.Name):
# # If the variable has been changed to a global variable
# if node.lua_node.id in self.modified_vars:
# # Change the reference to a global reference
# # This will require creating a new node type or modifying the existing node
# # For example, you might add an attribute to the Name node to indicate that it's a global reference
# node.lua_node.is_global = True
# # Visit the child nodes
# for child in node.children:
# self.update_references(child)
def linearize_branches(self):
traversal_order = self.IR_graph.postorder(self.IR_graph.root_node)
for node in traversal_order:
# Check if node have branch as children
branch_present = False
branch_node = None
for child in node.children:
if isinstance(child, GeneratedBranchIRGraphNode):
branch_present = True
branch_node = child
# The post exeuction tree are nodes that execute after the nodes in the branch
post_exeuction_tree = None
if branch_present:
block_node = None
# Find the post exeuction tree (if present)
logging.debug(f"Found Branch {branch_node.id}")
for child in branch_node.children:
if not isinstance(child, GeneratedBlockIRGraphNode):
post_exeuction_tree = child
elif isinstance(child, GeneratedBlockIRGraphNode):
block_node = child
# Continue if there is no post exeuction tree (not present or branch has already been linearized)
if post_exeuction_tree is None:
continue
logging.debug(f"Found post execution tree {post_exeuction_tree.name} {post_exeuction_tree.id}")
# '''
# Modify Branches:
# - Find Nonterminal Conditional Branches (NCBs), that is branches that do not contain an else statement.
# -- When linearizing, code that is executed after the if statement is appended to each conditional in the branch. This creates a problem in the following case:
# * The code is linearized
# * There is code after the branch (post exeuction tree)
# * The branch does not contain an else statement
# * All conditionals of the branch evaluate to false
# The linearized post exeuction tree will never be executed. We can fix this by copying the tree to a new artificially created
# 'else' conditional node under the NCB.
# '''
# if not branch_node.else_statement_present:
# logging.debug(f"Branch {branch_node.id} does not have an else statement, creating one")
# # Construct a placeholder node
# placeholder_else_node = GeneratedConditionalElseIRGraphNode()
# self.IR_graph.pointer = branch_node
# self.IR_graph.add_node(placeholder_else_node)
# Find all leaf nodes, including those inside linked subgraphs
leaf_nodes = get_subgraph_leaf_nodes(block_node)
# TODO: Why was this here?
# Remove post_execution_tree from leaf nodes
# leaf_nodes = [node for node in leaf_nodes if node is not post_exeuction_tree]
# Create a new IR graph
exeuction_IR_graph = IRGraph()
# Append a new function as the root node
placeholder_function = GeneratedFunctionIRGraphNode(generated_function_name=exeuction_IR_graph.generated_name)
exeuction_IR_graph.add_node(placeholder_function)
logging.debug(f"Constructed new IR graph with root node {exeuction_IR_graph.root_node.name} {exeuction_IR_graph.root_node.id}")
# Copy the post execution tree to the new IR graph
logging.debug(f"Copying tree from source node {post_exeuction_tree.name} {post_exeuction_tree.id} to graph {exeuction_IR_graph.generated_name[4:10]} at parent node {exeuction_IR_graph.pointer.name} {exeuction_IR_graph.pointer.id}")
copy_tree(src_node=post_exeuction_tree, dst_graph=exeuction_IR_graph, dst_node=exeuction_IR_graph.pointer)
self.exeuction_IR_graphs.append(exeuction_IR_graph)
# Add a link to thew new IR graph to each of the leaf nodes
for leaf_node in leaf_nodes:
# Find which IR graph the leaf node belongs to
IR_graph = leaf_node.IR_graph
# Add link to execution graph
IR_graph.pointer = leaf_node
logging.debug(f"Adding link to leaf node {leaf_node.name} {leaf_node.id}")
link_node = GeneratedLinkIRGraphNode(exeuction_IR_graph, async_link=False)
IR_graph.add_node(link_node)
# Track link node
self.links.append((link_node, exeuction_IR_graph))
# Remove the post execution tree from the main IR graph
self.IR_graph.remove_node(post_exeuction_tree)
# Reset the traversal order
traversal_order = self.IR_graph.postorder(self.IR_graph.root_node)
def separate_async_statements(self):
for IR_graph in self.exeuction_IR_graphs:
traversal_order = IR_graph.postorder(IR_graph.root_node)
for node in traversal_order:
if isinstance(node, AsyncIRGraphNode):
# Continue if async node has no children
if not node.children: continue
# Node should have only one child
if len(node.children) > 1:
logging.error("Async node has more than 1 child")
# Get child of async node
async_node_child = node.children[0]
# Create a new IR graph
exeuction_IR_graph = IRGraph()
# Append a placeholder function to the new graph as the root node
placeholder_function = GeneratedFunctionIRGraphNode(generated_function_name=exeuction_IR_graph.generated_name)
exeuction_IR_graph.add_node(placeholder_function)
# Copy the child tree of the async node to the new graph
copy_tree(src_node=async_node_child, dst_graph=exeuction_IR_graph, dst_node=exeuction_IR_graph.pointer)
logging.debug(f"Constructed new IR graph with root node {exeuction_IR_graph.root_node.name} {exeuction_IR_graph.root_node.id}")
self.exeuction_IR_graphs.append(exeuction_IR_graph)
# Remove async node children from main graph
self.IR_graph.remove_node(async_node_child)
# Add a link from the main graph to the new IR graph
previous_pointer = self.IR_graph.pointer
self.IR_graph.pointer = node
link_node = GeneratedLinkIRGraphNode(exeuction_IR_graph, async_link=True)
self.IR_graph.add_node(link_node)
self.IR_graph.pointer = previous_pointer
# Track link node
self.links.append((link_node, exeuction_IR_graph))
# Reset traversal order
traversal_order = self.IR_graph.postorder(self.IR_graph.root_node)
def insert_event_pointers(self):
for IR_graph in self.exeuction_IR_graphs:
traversal_order = IR_graph.preorder(IR_graph.root_node)
for node in traversal_order:
if isinstance(node, GeneratedLinkIRGraphNode):
if node.async_link:
# Create a new GeneratedSetEventPointerNode with pointer equal to the generated link name
set_event_pointer_node = GeneratedSetEventPointerNode(node.generated_link_name)
# Get the parent node of the GeneratedLinkIRGraphNode
parent_node = node.parent
# Get the grandparent node of the GeneratedLinkIRGraphNode
grandparent_node = parent_node.parent
if parent_node and grandparent_node:
# Insert the GeneratedSetEventPointerNode between the parent and the GeneratedLinkIRGraphNode
self.IR_graph.insert_between_nodes(grandparent_node, parent_node, set_event_pointer_node)
# Reset traversal order
traversal_order = self.IR_graph.preorder(self.IR_graph.root_node)
def construct_ast(self):
script_body = []
script_block_node = astnodes.Block(body=script_body)
script_chunk_node = astnodes.Chunk(body=script_block_node)
# Build headers
event_ptr_nodes = self.construct_event_ptr_assignment_nodes()
event_name_nodes = self.construct_event_name_assignment_nodes()
event_registration_nodes = self.construct_event_registration_nodes()
script_body.extend(event_ptr_nodes)
script_body.extend(event_name_nodes)
script_body.extend(event_registration_nodes)
# Build functions from IR graph
# 1 function per graph
function_nodes = []
for exeuction_IR_graph in self.exeuction_IR_graphs:
function_node = None
function_body = []
for node in exeuction_IR_graph.preorder():
if isinstance(node, RegularIRGraphNode):
if isinstance(node, FunctionIRGraphNode):
function_node = \
astnodes.Function(
name=node.lua_node.name,
args=node.lua_node.args
)
function_body.append(node.lua_node)
if isinstance(node, GeneratedIRGraphNode):
print(type(node))
else:
print(type(node))
'''
################################################
LUA AST NODE BUILDERS
################################################
'''
'''
################################################
EVENT BUS HEADERS
################################################
'''
'''
Constructs the event ptr assignments. i.e
global.event_ptrs['A_event'] = 'B_event'
global.event_ptrs['B_event'] = 'C_event'
'''
def construct_event_ptr_assignment_nodes(self):
event_ptr_assignment_nodes = []
for link, graph in self.links:
if link.async_link:
event_ptr_assignment_node = \
astnodes.Assign(
targets=[
astnodes.Index(
idx=astnodes.String(
s=link.generated_link_name
),
value=astnodes.Name(EVENT_PTR_TABLE_NAME),
notation=astnodes.IndexNotation.SQUARE,
)
],
values=[astnodes.Name(graph.root_node.generated_function_name)],
)
event_ptr_assignment_nodes.append(event_ptr_assignment_node)
return event_ptr_assignment_nodes
'''
Constructs the runtime event name generator. i.e
global.event_names['A_event'] = script.generate_event_name()
global.event_names['B_event'] = script.generate_event_name()
'''
def construct_event_name_assignment_nodes(self):
event_name_assignment_nodes = []
for link, graph in self.links:
if link.async_link:
event_name_assignment_node = \
astnodes.Assign(
targets=[
astnodes.Index(
idx=astnodes.String(
s=link.generated_link_name
),
value=astnodes.Name(EVENT_NAME_TABLE_NAME),
notation=astnodes.IndexNotation.SQUARE,
)
],
values=[astnodes.Call(
func=astnodes.Name(GENERATE_EVENT_NAME_FUNCTION_NAME),
args=[]
)
],
)
event_name_assignment_nodes.append(event_name_assignment_node)
return event_name_assignment_nodes
'''
Registers the functions with the event bus i.e
script.on_event(global.events['A_event'], function () A() end)
script.on_event(global.events['B_event'], function () B() end)
'''
def construct_event_registration_nodes(self):
event_registration_nodes = []
for link, graph in self.links:
# Replace with the condition you want to apply
if link.async_link:
# We use an anonymous function in Lua for this
func_body = astnodes.AnonymousFunction(
args=[],
body=[
astnodes.Call(
func=astnodes.Name(graph.root_node.generated_function_name),
args=[]
)
]
)
event_registration_node = \
astnodes.Call(
func=astnodes.Name(REGISTER_EVENT_FUNCTION_NAME),
args=[
astnodes.Index(
idx=astnodes.String(
s=graph.root_node.generated_function_name
),
value=astnodes.Name("global.events"), #TODO: global.events???
notation=astnodes.IndexNotation.SQUARE,
),
func_body
]
)
event_registration_nodes.append(event_registration_node)
return event_registration_nodes
'''
Else nodes are list<Statement>
'''
def construct_else_lua_node():
pass
# return astnodes.
def construct_global_assign_lua_node():
pass
def construct_function_lua_node():
pass
def construct_event_pointer_assignment():
pass
'''
################################################
LUA AST NODE VISITERS
################################################
'''
'''
---------------------------------------------------------------------------------------------------
Call sorting
---------------------------------------------------------------------------------------------------
'''
def visit(self, node):
method = 'visit_' + node.__class__.__name__
# get correct visit function
visitor = getattr(self, method, self.generic_visit)
# call visit function with current node
return visitor(node)
def generic_visit(self, node):
if isinstance(node, list):
for item in node:
self.visit(item)
elif isinstance(node, astnodes.Node):
logging.debug(f"Generic visit {node._name}")
# visit all object public attributes:
children = [
attr for attr in node.__dict__.keys() if not attr.startswith("_")
]
for child in children:
self.visit(node.__dict__[child])
'''
---------------------------------------------------------------------------------------------------
Regular nodes
---------------------------------------------------------------------------------------------------
'''
def visit_Assign(self, node):
self.IR_graph.add_node(GlobalAssignIRGraphNode(lua_node=node))
'''
Because of the execution environment, all local assignements will be converted to global assignments
TODO: Use factorio global table?
'''
def visit_LocalAssign(self, node):
self.IR_graph.add_node(LocalAssignIRGraphNode(lua_node=node))
def visit_SemiColon(self, node):
self.IR_graph.add_node(SemicolonIRGraphNode(lua_node=node))
def visit_Do(self, node):
self.IR_graph.add_node(DoIRGraphNode(lua_node=node))
'''
---------------------------------------------------------------------------------------------------
Loop nodes
---------------------------------------------------------------------------------------------------
'''
def visit_While(self, node):
self.IR_graph.add_node(WhileIRGraphNode(lua_node=node))
def visit_Forin(self, node):
self.IR_graph.add_node(ForinIRGraphNode(lua_node=node))
def visit_Fornum(self, node):
self.IR_graph.add_node(FornumIRGraphNode(lua_node=node))
def visit_Repeat(self, node):
self.IR_graph.add_node(RepeatIRGraphNode(lua_node=node))
def visit_Break(self, node):
self.IR_graph.add_node(BreakIRGraphNode(lua_node=node))
def visit_Return(self, node):
self.IR_graph.add_node(ReturnIRGraphNode(lua_node=node))
'''
---------------------------------------------------------------------------------------------------
Conditional nodes
---------------------------------------------------------------------------------------------------
'''
# def visit_ElseIf(self, node):
# logging.debug(f"Visited ElseIf")
# self.IR_graph.add_node(ConditionalIRGraphNode(lua_node=node))
# self.visit(node.body)
# self.visit(node.orelse)
def visit_If(self, node):
self.IR_graph.add_node(GeneratedBranchIRGraphNode(lua_node=node))
'''
---------------------------------------------------------------------------------------------------
Function nodes
---------------------------------------------------------------------------------------------------
'''
def visit_Function(self, node):
logging.debug(f"Visited {node.name.id} with arguments {node.args}")
if self.function_count > 0:
raise Exception("Error: More than one function defined. Scripts should only contain one function definition.")
self.function_count += 1
self.main_function_name = node.name.id
logging.info(f"Found main function {self.main_function_name}")
# We are now traversing inside the main function
self.inside_main_function = True
self.IR_graph.add_node(FunctionIRGraphNode(lua_node=node))
self.visit(node.body)
def visit_LocalFunction(self, node):
# TODO: Just change function to not be local
raise Exception("Error: Function must not be a local function")
def visit_Call(self, node):
logging.debug(f"Visited {node.func.id} with arguments {node.args}")
# Find await() calls
if node.func.id == 'await':
logging.info(f"Await call found. Function: {node.args[0].func.id}")
self.IR_graph.add_node(AsyncIRGraphNode(lua_node=node))
else:
# Regular function call
self.IR_graph.add_node(RegularIRGraphNode(lua_node=node))
'''
---------------------------------------------------------------------------------------------------
Unsupported nodes
---------------------------------------------------------------------------------------------------
'''
def visit_Label(self, node):
self.IR_graph.add_node(LabelIRGraphNode)
def visit_Goto(self, node):
self.IR_graph.add_node(GotoIRGraphNode)
def visit_Invoke(self, node):
raise Exception("Error: Invoking object methods is not supported.")
def visit_Method(self, node):
raise Exception("Error: Defining object methods is not supported.")
# Convert the source code to an AST
source_lua_root_node = ast.parse(cases.source_code_5)
#print(ast.to_pretty_str(tree))
# Create FSM graph
# Translate
translator = Translator(source_lua_root_node, render_visual_graph=True)
translator.translate()