Skip to content

Commit

Permalink
frontend (#17)
Browse files Browse the repository at this point in the history
* Removed provider instance from provider file

* Added use of context manager for open files

* Add facade Homotopy class

* Add docstrings to Homotopy class

* Add docs for Homotopy class

* Rework __main__.py

* Add console app info in docs

* Change mock snippet provider to avoid using assert_called_once
  • Loading branch information
Ahhhhmed committed Apr 17, 2018
1 parent 14e993e commit ad7f7a9
Show file tree
Hide file tree
Showing 12 changed files with 362 additions and 55 deletions.
52 changes: 51 additions & 1 deletion docs/contributing.rst
Original file line number Diff line number Diff line change
Expand Up @@ -300,4 +300,54 @@ Utility functionality to help the development of editor add-ons.
Application frontend
^^^^^^^^^^^^^^^^^^^^

Console application frontend.
Homotopy class
""""""""""""""

:code:`Homotopy` class scarves as a facade for the whole tool. It should be the eatery point for snippet compilation.
It can be configured to include additional paths to user defined snippet libraries.

Example:

.. code-block:: python
from homotopy import Homotopy
cpp_snippets = Homotopy("c++")
print(cpp_snippets.compile('int n=5;&for#int$i%n>printf("hello");'))
outputs:

.. code-block:: text
int n=5;
for(int i=0; i<n; i++){
printf("hello");
}
.. autoclass:: homotopy.Homotopy
:members: compile, add_lib_folder

Console application
"""""""""""""""""""

Homotopy can also be used as a console application. In fact, this is the intended way of using is via editor plugins.

.. code-block:: text
C:\dev\homotopy>homotopy -h
usage: homotopy [-h] [-t N] [-c] [-p PATH] language snippet
Compile a snippet.
positional arguments:
language Language for the snippet to be compiled to
snippet A snippet to be compiled
optional arguments:
-h, --help show this help message and exit
-t N, --tabsize N Number of spaces in one tab. Tabs remain tabs if
absent
-c, --cursor Indicate cursor marker in compiled snippet
-p PATH, --path PATH Path to snippet library folders separated by :
1 change: 1 addition & 0 deletions homotopy/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from homotopy.homotopy import Homotopy
42 changes: 31 additions & 11 deletions homotopy/__main__.py
Original file line number Diff line number Diff line change
@@ -1,21 +1,41 @@
import argparse
import homotopy.parser
from homotopy import preprocessor

from homotopy.compiler import Compiler
from homotopy.homotopy import Homotopy


def main():
parser = argparse.ArgumentParser(description="Compile a snippet.")
parser.add_argument('snippet', nargs=1, type=str, help='a snippet to be compiled')
parser.add_argument('language', type=str,
help='Language for the snippet to be compiled to')
parser.add_argument('snippet', type=str,
help='A snippet to be compiled')
parser.add_argument('-t', '--tabsize', type=int, metavar="N",
help='Number of spaces in one tab. Tabs remain tabs if absent')
parser.add_argument('-c', '--cursor', action='store_true',
help='Indicate cursor marker in compiled snippet')
parser.add_argument('-p', '--path', type=str, metavar="PATH",
help='Path to snippet library folders separated by :')

snippet = parser.parse_args().snippet[0]
args = parser.parse_args()

print(Compiler().compile(homotopy.parser.parser.parse(
preprocessor.Preprocessor.put_cursor_marker(
preprocessor.Preprocessor.expand_decorators(snippet)))),
end='')
homotopy = Homotopy(args.language)

if args.tabsize:
homotopy.set_indent(" "*args.tabsize)

if __name__ == "__main__":
main()
if args.cursor:
homotopy.enable_cursor_marker()

if args.path:
for item in args.path.split(':'):
homotopy.add_lib_folder(item)

print(homotopy.compile(args.snippet), end='')


def init():
if __name__ == "__main__":
main()


init()
14 changes: 7 additions & 7 deletions homotopy/compiler.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
from homotopy.syntax_tree import SnippetVisitor
from homotopy.snippet_provider import snippetProvider
from homotopy.parser import Parser

import re
Expand All @@ -10,9 +9,10 @@ class Compiler(SnippetVisitor):
"""
Compiler for snippets. Turns syntax tree into text.
"""
def __init__(self):
def __init__(self, snippet_provider):
self.context_manager = ContextManager()
self.context_manager.new_scope()
self.snippet_provider = snippet_provider

def visit_composite_snippet(self, composite_snippet):
"""
Expand All @@ -22,12 +22,12 @@ def visit_composite_snippet(self, composite_snippet):
:param composite_snippet: Composite snippet
:return: Text of left side replaced with right side
"""
left_side = self.expand_variable_operators(snippetProvider[self.visit(composite_snippet.left)])
left_side = self.expand_variable_operators(self.snippet_provider[self.visit(composite_snippet.left)])

if composite_snippet.operation == Parser.in_operator:
self.context_manager.new_scope()

right_side = snippetProvider[self.compile(composite_snippet.right)]
right_side = self.snippet_provider[self.compile(composite_snippet.right)]

if composite_snippet.operation == Parser.in_operator:
self.context_manager.remove_scope()
Expand All @@ -43,9 +43,9 @@ def visit_composite_snippet(self, composite_snippet):
def expansion_function(match_object):
nonlocal match_found

if not match_found and operation_text in snippetProvider[match_object.group(1)]:
if not match_found and operation_text in self.snippet_provider[match_object.group(1)]:
match_found = True
return snippetProvider[match_object.group(1)]
return self.snippet_provider[match_object.group(1)]

return match_object.group(0)

Expand All @@ -67,7 +67,7 @@ def visit_simple_snippet(self, simple_snippet):
:param simple_snippet: Simple snippet
:return: Text of compile snippet
"""
return self.expand_variable_operators(snippetProvider[simple_snippet.value])
return self.expand_variable_operators(self.snippet_provider[simple_snippet.value])

def expand_variable_operators(self, text):
"""
Expand Down
87 changes: 87 additions & 0 deletions homotopy/homotopy.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import os

from homotopy import preprocessor, parser, compiler, snippet_provider


class Homotopy:
"""
Facade class for accessing homotopy tool.
"""
stdlib_path = os.path.join(os.path.split(__file__)[0], 'stdlib')

def __init__(self, language):
"""
Initialize instance.
:param language: Language
"""
self.user_path = []
self.indent = '\t'
self.put_cursor_marker = False
self.language = language

def add_lib_folder(self, path):
"""
Add path to a snippet library
:param path: Path
"""
self.user_path.append(path)

def clear_user_lib(self):
"""
Clear user lib paths.
"""
self.user_path.clear()

def set_indent(self, indent):
"""
Set indent sequence used for indentation instead of tab.
:param indent: Indent sequence
"""
self.indent = indent

def enable_cursor_marker(self):
"""
Enable cursor marker.
"""
self.put_cursor_marker = True

def disable_cursor_marker(self):
"""
Disable cursor marker.
"""
self.put_cursor_marker = False

def set_language(self, language):
"""
Set language.
:param language: Language
"""
self.language = language

def compile(self, snippet_text):
"""
Compile a snippet.
:param snippet_text: Snippet text
:return: Compiled snippet text
"""
snippet_provider_instance = snippet_provider.SnippetProvider(
self.language, self.user_path + [Homotopy.stdlib_path]
)

preprocessor_instance = preprocessor.Preprocessor(snippet_provider_instance)
compiler_instance = compiler.Compiler(snippet_provider_instance)
parser_instance = parser.Parser()

preprocessed_text = preprocessor_instance.expand_decorators(snippet_text)
if self.put_cursor_marker:
preprocessed_text = preprocessor_instance.put_cursor_marker(preprocessed_text)

syntax_tree = parser_instance.parse(preprocessed_text)

return compiler_instance.compile(syntax_tree)

15 changes: 10 additions & 5 deletions homotopy/preprocessor.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
from homotopy.snippet_provider import snippetProvider

import re


Expand All @@ -9,8 +7,15 @@ class Preprocessor:
"""
cursor_marker = "[{cursor_marker}]"

@staticmethod
def expand_decorators(snippet_text):
def __init__(self, snippet_provider):
"""
Initialize preprocessor instance.
:param snippet_provider: Snippet provider
"""
self.snippet_provider = snippet_provider

def expand_decorators(self, snippet_text):
"""
Expand decorators to enable concise writing of common patterns.
Expand All @@ -19,7 +24,7 @@ def expand_decorators(snippet_text):
"""
return re.sub(
r'\[\[(.*?)\]\]',
lambda match_group: snippetProvider[match_group.group(1)],
lambda match_group: self.snippet_provider[match_group.group(1)],
snippet_text)

@staticmethod
Expand Down
19 changes: 8 additions & 11 deletions homotopy/snippet_provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ def __init__(self, language="", path=[]):
self.language = language
self.path = path
for item in self.path:
for file in filter(lambda x: x.endswith(".json"), os.listdir(item)):
for file_name in filter(lambda x: x.endswith(".json"), os.listdir(item)):
try:
def language_filter(filter_item):
all_languages = 'all'
Expand All @@ -38,13 +38,14 @@ def language_filter(filter_item):

return all_languages in languages or self.language.lower() in languages

for snippet in filter(language_filter, json.load(open(os.path.join(item, file)))):
if snippet[SnippetProvider.name_key] in self.data:
logging.warning("Multiple definition for %s" % snippet[SnippetProvider.name_key])
else:
self.data[snippet[SnippetProvider.name_key]] = snippet[SnippetProvider.snippet_key]
with open(os.path.join(item, file_name)) as opened_file:
for snippet in filter(language_filter, json.load(opened_file)):
if snippet[SnippetProvider.name_key] in self.data:
logging.warning("Multiple definition for %s" % snippet[SnippetProvider.name_key])
else:
self.data[snippet[SnippetProvider.name_key]] = snippet[SnippetProvider.snippet_key]
except (ValueError, OSError):
logging.warning("Could not get data from file %s" % file, exc_info=True)
logging.warning("Could not get data from file %s" % file_name, exc_info=True)

def __getitem__(self, item):
"""
Expand All @@ -57,7 +58,3 @@ def __getitem__(self, item):
return self.data[item]
return item


stdlib_path = os.path.join(os.path.split(__file__)[0], 'stdlib')

snippetProvider = SnippetProvider(language='c++', path=[stdlib_path])

0 comments on commit ad7f7a9

Please sign in to comment.