Commit a6d8e1f6 authored by Marco's avatar Marco Committed by Adrian Wuillemet
Browse files

#63-#10-#55_improved-multi-file-input_removed-redundant-code_alternate-browser-tries

parent 51f7fb36
......@@ -81,14 +81,10 @@ def parse(file: IO, language: str, no_validation: bool, is_verbose: bool,
set_up_logger(is_verbose)
files = file_names.split(",")
logging.debug(f'FILES: {files}')
for file in files:
with open(file, 'r') as f:
opts[OUT_FILE] = f'{out_file}_{1 + files.index(file)}' or f.name.partition('.')[0]
logging.debug(f'MAIN: filename: {opts[OUT_FILE]}')
cli.parse(f, language, opts)
opts[OUT_FILE] = f'{out_file}_{1 + files.index(file)}' or file.name.partition('.')[0]
logging.debug(f'MAIN: filename: {opts[OUT_FILE]}')
cli.parse(file, language, opts)
@beads.command()
......
......@@ -19,5 +19,11 @@ class ParsingError(InternalException):
self.cause = cause
class BrowserError(InternalException):
""" Any error that occurs while trying to find alternative browser """
def __init__(self, cause):
self.cause = cause
class ExistingFileError(InternalException):
""" Error indicating that a file with the same name already exists and the '-re' flag is not set """
......@@ -7,6 +7,11 @@ from distutils.util import strtobool
import logging
import eel
import json
import webbrowser
import platform
import subprocess
import os
import errno
browser_options: dict = {
'window_size': (1200, 800),
......@@ -204,3 +209,28 @@ def get_info() -> str:
resource: dict = reader.load_resource('info', 'README.md')
return json.dumps(resource[CONTENT])
def get_browser(browser_path=None) -> webbrowser.BaseBrowser:
browser = webbrowser
if platform.system() == 'Windows':
browser = webbrowser.get('windows-default')
elif platform.system().lower().find('mac'):
browser = webbrowser.get('macosx')
elif browser_path:
browser = webbrowser.get(browser_path)
logging.debug(f'GUI: get_Browser: Browser - {browser}')
logging.debug(f'GUI: get_Browser: Platform - {platform.system()}')
return browser
def is_tool(name):
try:
devnull = open(os.devnull)
subprocess.Popen([name], stdout=devnull, stderr=devnull).communicate()
except OSError as e:
if e.errno == errno.ENOENT:
return False
return True
......@@ -60,34 +60,58 @@ def parse_bds(file: IO) -> dict:
data: Dict[str, List[Dict[str, str]]] = {NODES: [], TRANSITIONS: []}
for line in file:
for line in file.readlines():
line = line.decode('UTF-8')
if "#" not in line:
[s1, t, s2] = line.strip().split(':')
[s1, t, s2] = validate_line(line)
transition = {LABEL: t, FROM: s1, TO: s2}
logging.debug(f'READER: parse_bds: line \n from: {line} to: {transition}\n')
data[TRANSITIONS].append(transition)
for transition in data[TRANSITIONS]:
data[NODES] = get_nodes_bds(transition, data)
extract_nodes(transition, data)
return data
def get_nodes_bds(transition: dict, data: dict) -> List[Dict[str, str]]:
def validate_line(transition: str) -> List[str]:
"""
Append all nodes of transitions to data[NODES]
Validate a line of a Beads file format input
:param transition: str - line to validate
:return: list of names for LABEL, FROM and TO
"""
try:
trans_strings = transition.strip().split(':')
# assert transition string has valid structure
assert len(trans_strings) == 3, 'Invalid transition format\n' \
' expected: state_from:transition:state_to\n' \
' got: ' + transition
# assert names are valid
for string in trans_strings:
for e in string:
assert e.isalnum() or e == '_', f'Invalid sign "{e}" in "{string}":\n' \
' Use only alphanumeric signs (a - Z, 0 - 9) and "_"'
except AssertionError as ae:
logging.debug('VALIDATION: Failed due to AssertionError')
raise ValidationError(ae)
return trans_strings
Method to parse input file into an internally usable version (dict).
def extract_nodes(transition: dict, data: dict) -> None:
"""
Extract start and destination node from a transition and add it to data.nodes
:param transition: dict - to extract nodes from
:param data: dict - to add extracted nodes to
:return: List of nodes as Dictionaries
"""
for key in [FROM, TO]:
node = handle_start_node_bds(transition[key])
data[NODES] = append_node_bds(node, data[NODES])
return data[NODES]
append_node_bds(node, data[NODES])
def handle_start_node_bds(name: str) -> dict:
......@@ -103,32 +127,25 @@ def handle_start_node_bds(name: str) -> dict:
node: dict = {ID: name}
if not name.startswith('_'):
logging.debug(f'READER: handle_start_node: RETURNS NODE: {node}\n')
return node
else:
node[START] = True
logging.debug(f'READER: handle_start_node: RETURNS START-NODE: {node}\n')
return node
return node
def append_node_bds(node: dict, nodes: list) -> List[Dict[str, str]]:
"""
Append node to nodes if it does not exist yet and returns then nodes
Method to parse input file into an internally usable version (dict).
def append_node_bds(node: dict, nodes: list) -> None:
"""
Append node to nodes if it does not exist yet
:param node: dict - to add to nodes
:param node: dict to add to nodes
:param nodes: list to add node to
:return: List of nodes as Dictionaries
"""
if node in nodes:
return nodes
else:
if node not in nodes:
nodes.append(node)
logging.debug(f'READER: parse_bds: Node added {node}\n')
return nodes
def load_resource(subdir: str, name: str) -> dict:
"""
......
......@@ -28,71 +28,71 @@
# This FILE will demonstrate and comment on template definitions by example of Python code.
# NOTE: This file can be parsed as is and is a valid template for code generation! (version:1)
# {
#
# # The first necessary item in a language template is the 'config'.
# # The config contains meta information about the language like the representation of a gap or an indent.
# #
# # It might be noteworthy that the indent here is only three whitespaces but will add up with the trailing
# # whitespace after each $indent reference to a python conform four whitespace indent:
#
# "config": {
# "gap": "\n\n", # Representation of a gap between e.g. methods. Here: two line breaks
# "indent": " " # Representation of code indention. Here: three whitespaces
# },
#
#
# # The 'states' item holds the template strings for the global variable holding all available states.
# # In this case a python dictionary is used. Resulting code would look something like this:
# #
# # states: dict = {
# # "A": 1,
# # "B": 2,
# # }
# #
# # The type of the variable needs to be hard coded into the template as version:1 does not support it.
#
# "states": {
# "name": "states" # Name for the global variable to use throughout the code
# "body": "$states: dict = {\n$items}\n" # Body template that will be substituted
# "item": "$indent '$name': '$value',\n" # Template for a state / node that will be inserted for each node
# },
#
#
# # The 'state' item is the global variable that will be used to set the initial state and will be updated with the
# # current state as a transition is made.
# # The here defined python example would look something like this:
# #
# # state: int = states["A"]
# #
# # The type of the variable has to be hardcoded into the template as version:1 does not support types dynamically.
#
# "state": {
# "name": "state", # Name of the global variable that holds the current state
# "body": "$state: int = $value\n" # Template for the declaration and assignment of the initial state
# },
#
#
# # The 'transitions' item is the most elaborated item. It represents the definition of method/function code
# # that is written to transition between states.
# # Resulting code from this template might look something like this:
# #
# # def transition() -> None:
# # global state
# # if state == states["A"]:
# # state = states["B"]
# # elif state == states["B"]:
# # state = states["C"]
# #
# # The body is the method / function wrapper that will be filled with items for each transition with the same label.
# # In addition templates version:1 supports an item_first that can be different that the rest of the assignments as
# # in if...elif (see code above). This however is an optional key. Erasing this key will result in all items being
# # written with the default 'item' template.
#
# "transitions": {
# "body": "def $name() -> None:\n$indent global $state\n$items\n",
# "item": "$indent elif $state == $states[$name]:\n$indent $indent $state = $value\n",
# "item_first": "$indent if $state == $states[$name]:\n$indent $indent $state = $value\n"
# }
#
# }
{
# The first necessary item in a language template is the 'config'.
# The config contains meta information about the language like the representation of a gap or an indent.
#
# It might be noteworthy that the indent here is only three whitespaces but will add up with the trailing
# whitespace after each $indent reference to a python conform four whitespace indent:
"config": {
"gap": "\n\n", # Representation of a gap between e.g. methods. Here: two line breaks
"indent": " " # Representation of code indention. Here: three whitespaces
},
# The 'states' item holds the template strings for the global variable holding all available states.
# In this case a python dictionary is used. Resulting code would look something like this:
#
# states: dict = {
# "A": 1,
# "B": 2,
# }
#
# The type of the variable needs to be hard coded into the template as version:1 does not support it.
"states": {
"name": "states" # Name for the global variable to use throughout the code
"body": "$states: dict = {\n$items}\n" # Body template that will be substituted
"item": "$indent '$name': '$value',\n" # Template for a state / node that will be inserted for each node
},
# The 'state' item is the global variable that will be used to set the initial state and will be updated with the
# current state as a transition is made.
# The here defined python example would look something like this:
#
# state: int = states["A"]
#
# The type of the variable has to be hardcoded into the template as version:1 does not support types dynamically.
"state": {
"name": "state", # Name of the global variable that holds the current state
"body": "$state: int = $value\n" # Template for the declaration and assignment of the initial state
},
# The 'transitions' item is the most elaborated item. It represents the definition of method/function code
# that is written to transition between states.
# Resulting code from this template might look something like this:
#
# def transition() -> None:
# global state
# if state == states["A"]:
# state = states["B"]
# elif state == states["B"]:
# state = states["C"]
#
# The body is the method / function wrapper that will be filled with items for each transition with the same label.
# In addition templates version:1 supports an item_first that can be different that the rest of the assignments as
# in if...elif (see code above). This however is an optional key. Erasing this key will result in all items being
# written with the default 'item' template.
"transitions": {
"body": "def $name() -> None:\n$indent global $state\n$items\n",
"item": "$indent elif $state == $states[$name]:\n$indent $indent $state = $value\n",
"item_first": "$indent if $state == $states[$name]:\n$indent $indent $state = $value\n"
}
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment