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

#63, #10 - CLI - created simple file format (.bds), handle multiple-input-files

parent e0815e02
......@@ -13,7 +13,7 @@ source/beads/resources/ui/js/gui.js
source/beads/resources/options/default_options.json
# Ignore testfile
source/package/somefile.json
source/files/testOut*
# Ignore click and python runtime files
__pycache__/
......
Beads file format (*.bds):
+ Format Structure:
> stateFrom:transition:stateTo
+ When state begins with an underscore, it counts as initial state:
> _stateFrom:transition:stateTo
+ TEST-CMD:
> python main.py parse ../files/testfile1.bds,../files/testfile.json,../files/testfile2.bds --language python -o ../files -of testOutput -re
......@@ -51,7 +51,7 @@ def gui(no_window: bool, port: int, is_verbose: bool, file: IO) -> None:
set_up_logger(is_verbose)
exit(gui_handler.start(file, opts))
gui_handler.start(file, opts)
@beads.command()
......@@ -81,7 +81,14 @@ def parse(file: IO, language: str, no_validation: bool, is_verbose: bool,
set_up_logger(is_verbose)
exit(cli.parse(file, language, opts))
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)
@beads.command()
......@@ -198,4 +205,4 @@ def filter_languages(search_text: str) -> iter:
if __name__ == '__main__':
beads()
exit(beads())
from meta import SKIP_VALIDATION, FILENAME, REPLACE, OUT_DIR
from meta import SKIP_VALIDATION, FILENAME, REPLACE, OUT_DIR, OUT_FILE
from modules.logic import process
from modules.templates import get_language_association
from modules.errors import ParsingError, InternalException, ExistingFileError, ValidationError, UnsupportedLanguageError
......@@ -32,7 +32,7 @@ def parse(file: IO, language: str, opts: dict) -> None:
code = process(fsm, language, skip_validation)
logging.debug(f'CLI: Processed state machine to code:\n{code}')
filename = ''.join([opts[FILENAME], get_language_association(language)])
filename = opts[OUT_FILE] + get_language_association(language)
out_path = os.path.join(opts[OUT_DIR], filename)
try:
......
......@@ -22,7 +22,9 @@ def parse_file(file: IO) -> dict:
if file.name.endswith('.json'):
logging.debug(f'READER: Identified {file.name} as json: Calling json.load')
return json.load(file)
elif file.name.endswith('.bds'):
logging.debug(f'READER: Identified {file.name} as beads-file: parsing from bds to json')
return parse_bds(file)
else:
raise NotImplementedError
......@@ -46,6 +48,88 @@ def parse_text(text: str) -> dict:
raise ParsingError
def parse_bds(file: IO) -> dict:
"""
Parse the provided Beads file into a dictionary.
Method to parse input file into an internally usable version (dict).
:param file: IO to parse
:return: Dictionary containing the string contents
"""
data: Dict[str, List[Dict[str, str]]] = {NODES: [], TRANSITIONS: []}
for line in file:
if "#" not in line:
[s1, t, s2] = line.strip().split(':')
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)
return data
def get_nodes_bds(transition: dict, data: dict) -> dict:
"""
Append all nodes of transitions to data[NODES]
Method to parse input file into an internally usable version (dict).
:param transition: dict - to extract nodes from
:param data: dict - to add extracted nodes to
:return: Dictionary
"""
for key in [FROM, TO]:
node = handle_start_node_bds(transition[key])
data[NODES] = append_node_bds(node, data[NODES])
return data[NODES]
def handle_start_node_bds(name: str) -> dict:
"""
Append {START: True} to node if name starts with _
Method to parse input file into an internally usable version (dict).
:param name: str - name of node to check for start condition
:return: node as a Dictionary
"""
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
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).
:param node: dict - to add to nodes
:param nodes: list to add node to
:return: List of Dictionaries
"""
if node in nodes:
return nodes
else:
nodes.append(node)
logging.debug(f'READER: parse_bds: Node added {node}\n')
return nodes
def load_resource(subdir: str, name: str) -> dict:
"""
Load a resource and provide the content as a DICT.
......@@ -101,5 +185,4 @@ def strip_comments(file: IO) -> str:
"""
for line in file.readlines():
yield line.partition('#')[0]
......@@ -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"
# }
#
# }
states: dict = {
'A': 0,
'B': 1,
'C': 2,
}
state: int = states['A']
def back() -> None:
global state
if state == states['B']:
state = states['A']
elif state == states['C']:
state = states['B']
def go() -> None:
global state
if state == states['A']:
state = states['B']
elif state == states['B']:
state = states['C']
def invalid() -> None:
global state
if state == states['C']:
state = states['A']
A:jump:_B
_B:go:C
_B:to:D
D:back_jump:A
D:climb:E
\ No newline at end of file
_spawning:init:standing
standing:go:running
standing:jumps:jumping
running:stops:standing
running:fallsAndGo:running
running:falls:standing
running:fallsAbyss:death
running:fallsLava:death
jumping:fallsAndGo:running
jumping:falls:standing
jumping:fallsAbyss:death
jumping:fallsLava:death
death:spawns:_spawning
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