-
Notifications
You must be signed in to change notification settings - Fork 79
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Using Textual Python, I have created a GUI/app.This app allows you to write/paste3 xDSL IR into the "Input" TextArea, which is mirrored in the "Output" TextArea. This app is still under construction, this is the first step towards building the full tool. --------- Co-authored-by: Sasha Lopoukhine <[email protected]>
- Loading branch information
1 parent
704707a
commit 2e6656a
Showing
7 changed files
with
233 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
import pytest | ||
|
||
from xdsl.builder import ImplicitBuilder | ||
from xdsl.dialects import arith, func | ||
from xdsl.dialects.builtin import IndexType, IntegerAttr, ModuleOp | ||
from xdsl.interactive.app import InputApp | ||
from xdsl.ir import Block, Region | ||
from xdsl.utils.exceptions import ParseError | ||
|
||
|
||
@pytest.mark.asyncio() | ||
async def test_input(): | ||
"""Test pressing keys has the desired result.""" | ||
app = InputApp() | ||
async with app.run_test() as pilot: # pyright: ignore | ||
# Test no input | ||
assert app.output_text_area.text == "No input" | ||
assert app.current_module is None | ||
|
||
# Test inccorect input | ||
app.input_text_area.insert("dkjfd") | ||
await pilot.pause() | ||
assert ( | ||
app.output_text_area.text | ||
== "(Span[5:6](text=''), 'Operation builtin.unregistered does not have a custom format.')" | ||
) | ||
assert isinstance(app.current_module, ParseError) | ||
assert ( | ||
str(app.current_module) | ||
== "(Span[5:6](text=''), 'Operation builtin.unregistered does not have a custom format.')" | ||
) | ||
|
||
# Test corect input | ||
app.input_text_area.clear() | ||
app.input_text_area.insert( | ||
""" | ||
func.func @hello(%n : index) -> index { | ||
%two = arith.constant 2 : index | ||
%res = arith.muli %n, %two : index | ||
func.return %res : index | ||
} | ||
""" | ||
) | ||
await pilot.pause() | ||
assert ( | ||
app.output_text_area.text | ||
== """builtin.module { | ||
func.func @hello(%n : index) -> index { | ||
%two = arith.constant 2 : index | ||
%res = arith.muli %n, %two : index | ||
func.return %res : index | ||
} | ||
} | ||
""" | ||
) | ||
|
||
index = IndexType() | ||
|
||
expected_module = ModuleOp(Region([Block()])) | ||
with ImplicitBuilder(expected_module.body): | ||
function = func.FuncOp("hello", ((index,), (index,))) | ||
with ImplicitBuilder(function.body) as (n,): | ||
two = arith.Constant(IntegerAttr(2, index)).result | ||
res = arith.Muli(n, two) | ||
func.Return(res) | ||
|
||
assert isinstance(app.current_module, ModuleOp) | ||
assert app.current_module.is_structurally_equivalent(expected_module) |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,139 @@ | ||
""" | ||
An interactive command-line tool to explore compilation pipeline construction. | ||
Execute `xdsl-gui` in your terminal to run it. | ||
Run `terminal -m xdsl.interactive.app:InputApp --def` to run in development mode. Please be sure to install `textual-dev` to run this command. | ||
""" | ||
|
||
from io import StringIO | ||
|
||
from rich.style import Style | ||
from textual import events, on | ||
from textual.app import App, ComposeResult | ||
from textual.containers import Horizontal, Vertical | ||
from textual.reactive import reactive | ||
from textual.widgets import Footer, TextArea | ||
from textual.widgets.text_area import TextAreaTheme | ||
|
||
from xdsl.dialects.builtin import ModuleOp | ||
from xdsl.ir import MLContext | ||
from xdsl.parser import Parser | ||
from xdsl.printer import Printer | ||
from xdsl.tools.command_line_tool import get_all_dialects | ||
|
||
|
||
class OutputTextArea(TextArea): | ||
"""Used to prevent users from being able to change/alter the Output TextArea""" | ||
|
||
async def _on_key(self, event: events.Key) -> None: | ||
event.prevent_default() | ||
|
||
|
||
class InputApp(App[None]): | ||
""" | ||
Interactive application for constructing compiler pipelines. | ||
""" | ||
|
||
CSS_PATH = "app.tcss" | ||
|
||
BINDINGS = [ | ||
("d", "toggle_dark", "Toggle dark mode"), | ||
("q", "quit_app", "Quit"), | ||
] | ||
|
||
# defines a theme for the Input/Output TextArea's | ||
_DEFAULT_THEME = TextAreaTheme( | ||
name="my_theme_design", | ||
base_style=Style(bgcolor="white"), | ||
syntax_styles={ | ||
"string": Style(color="red"), | ||
"comment": Style(color="magenta"), | ||
}, | ||
) | ||
|
||
current_module: reactive[ModuleOp | Exception | None] = reactive(None) | ||
""" | ||
Reactive variable used to save the current state of the modified Input TextArea (i.e. is the Output TextArea) | ||
""" | ||
|
||
input_text_area = TextArea(id="input") | ||
output_text_area = OutputTextArea(id="output") | ||
|
||
def compose(self) -> ComposeResult: | ||
""" | ||
Creates the required widgets, events, etc. | ||
Get the list of xDSL passes, add them to an array in "Selection" format (so it can be added to a Selection List) | ||
and sort the list in alphabetical order. | ||
""" | ||
|
||
with Horizontal(id="input_output"): | ||
with Vertical(id="input_container"): | ||
yield self.input_text_area | ||
with Vertical(id="output_container"): | ||
yield self.output_text_area | ||
yield Footer() | ||
|
||
@on(TextArea.Changed, "#input") | ||
def update_current_module(self) -> None: | ||
""" | ||
Function called when the Input TextArea is cahnged. This function parses the Input IR and updates | ||
the current_module reactive variable. | ||
""" | ||
input_text = self.input_text_area.text | ||
try: | ||
ctx = MLContext(True) | ||
for dialect in get_all_dialects(): | ||
ctx.load_dialect(dialect) | ||
parser = Parser(ctx, input_text) | ||
module = parser.parse_module() | ||
self.current_module = module | ||
except Exception as e: | ||
self.current_module = e | ||
|
||
def watch_current_module(self): | ||
""" | ||
Function called when the current_module reactive variable is updated. This function updates | ||
the Output TextArea. | ||
""" | ||
match self.current_module: | ||
case None: | ||
output_text = "No input" | ||
case Exception() as e: | ||
output_stream = StringIO() | ||
Printer(output_stream).print(e) | ||
output_text = output_stream.getvalue() | ||
case ModuleOp(): | ||
output_stream = StringIO() | ||
Printer(output_stream).print(self.current_module) | ||
output_text = output_stream.getvalue() | ||
|
||
self.output_text_area.load_text(output_text) | ||
|
||
def on_mount(self) -> None: | ||
"""Configure widgets in this application before it is first shown.""" | ||
|
||
# register's the theme for the Input/Output TextArea's | ||
self.input_text_area.register_theme(InputApp._DEFAULT_THEME) | ||
self.output_text_area.register_theme(InputApp._DEFAULT_THEME) | ||
self.input_text_area.theme = "my_theme_design" | ||
self.output_text_area.theme = "my_theme_design" | ||
|
||
self.query_one("#input_container").border_title = "Input xDSL IR" | ||
self.query_one("#output_container").border_title = "Output xDSL IR" | ||
|
||
def action_toggle_dark(self) -> None: | ||
"""An action to toggle dark mode.""" | ||
self.dark = not self.dark | ||
|
||
def action_quit_app(self) -> None: | ||
"""An action to quit the app.""" | ||
self.exit() | ||
|
||
|
||
def main(): | ||
return InputApp().run() | ||
|
||
|
||
if __name__ == "__main__": | ||
main() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
|
||
# Vertical(TextArea) | ||
#input_container { | ||
margin: 1; | ||
border: heavy $warning-lighten-1; | ||
border-title-color: $primary; | ||
border-title-align: center; | ||
} | ||
|
||
# Vertical(OutputTextArea) | ||
#output_container { | ||
margin: 1; | ||
border: heavy $warning-lighten-1; | ||
border-title-color: $primary; | ||
border-title-align: center; | ||
} |