stfc / fparser

This project maintains and develops a Fortran parser called fparser2 written purely in Python which supports Fortran 2003 and some Fortran 2008. A legacy parser fparser1 is also available but is not supported. The parsers were originally part of the f2py project by Pearu Peterson.
https://fparser.readthedocs.io
Other
64 stars 29 forks source link

how to change ast? #424

Closed PressFforlove closed 1 year ago

PressFforlove commented 1 year ago

Hello there, i am new in fparser, and i am trying to do some transform in ast. But i meet some problem while doing so. I want to change some binaryOp in ast,such as "+", "-","/", "*",which is str in ast. For example: change “a+b” to "a-b" But i can't directly change the str in place the ast. The reason is that, in fparser's ast, a code line like c = a + b is an {Assignment_Stmt} class,in which "a + b" is stored in {tuple}(E.G. children = {tuple}[3], children[0] = a, children[1]=str("+"),children[2]="b"). In python, str and tuple are both immutable object. Is there any option to make the ast structure of "c = a + b" mutable object, that i can do this transform inplace? Or is there any interface in ast , so that i can use the interface to change the str?

Also, i come up a solution that iterate the parent node until the data structure is {list}, and then replace the children with a brand new children node which changed some specific childNodes. But i don't know how to copy the node while change some specific childNode in python.Another problem is that, when i iterate to the parents, it is unclear to detemine where will i meet the list structure, which make me more confused.

Any help will be apprecited, thank you for your attention to this issue~

rupertford commented 1 year ago

Hi @PressFforlove. The short answer is no, there is no direct support in the ast for modifying the tree. Depending on what you want to do there are a couple of options you could take. If you want to do a lot a manipulation I would suggest using the PSyIR internal representation and associated tools, which is part of another project called PSyclone but uses fparser2 to parse the Fortran code in the first instance. If you want to make relatively small changes to the Fortran then you can modify the fparser2 ast directly by turning tuples into lists and back again. The code below shows how you could modify a "+" to a "-" in both approaches.

# PSyIR example
from psyclone.psyir.frontend.fortran import FortranReader
from psyclone.psyir.backend.fortran import FortranWriter
from psyclone.psyir.nodes import BinaryOperation
code = (
    "program test\n"
    "  integer :: a, b, c\n"
    "  a = b + c\n"
    "end program")
psyir = FortranReader().psyir_from_source(code)
print(psyir.view())
assignment = psyir.children[0].children[0]
add = assignment.rhs
rhs_add = add.children[1].detach()
lhs_add = add.children[0].detach()
minus = BinaryOperation.create(
    BinaryOperation.Operator.SUB, lhs_add, rhs_add)
add.replace_with(minus)
print(psyir.view())
print(FortranWriter()(psyir))

# fparser2 example
from fparser.common.readfortran import FortranStringReader
from fparser.two.parser import ParserFactory
reader = FortranStringReader(code, ignore_comments=False)
f2008_parser = ParserFactory().create(std="f2008")
parse_tree = f2008_parser(reader)
print(repr(parse_tree))
main_program = parse_tree.children[0]
execution_part = main_program.children[2]
assignment = execution_part.children[0]
level_2_expr = assignment.children[2]
# As both "+" and "-" are a level-2-expr we can replace the string.
child_list = list(level_2_expr.items)
child_list[1] = "-"
level_2_expr.items = tuple(child_list)
print(repr(parse_tree))
print(parse_tree)`
PressFforlove commented 1 year ago

thank you very much~ it helps a lot~

hiker commented 1 year ago

Note that it would be quite simple in PSyclone to add a method to just change an operation, which would significantly simplify the PSyclone version, e.g. in Rupert's example:

add = assignment.rhs
rhs_add = add.children[1].detach()
lhs_add = add.children[0].detach()
minus = BinaryOperation.create(
    BinaryOperation.Operator.SUB, lhs_add, rhs_add)
add.replace_with(minus)

we would then just need:

add = assignment.rhs
add.operator = BinaryOperation.Operator.SUB

(though you would likely use a better name for the variable :) )

We just never had a need to do that :)

PressFforlove commented 1 year ago

thank you for your help~ i will try PSyclone to do this~