summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJuergen Bocklage-Ryannel <juergen.bocklage-ryannel@pelagicore.com>2017-01-30 10:56:04 +0100
committerJuergen Bocklage-Ryannel <juergen.bocklage-ryannel@pelagicore.com>2017-01-30 10:56:04 +0100
commitee1ef38757970c93932e5028f8fe86fc1aa8d7e0 (patch)
treec319e263e849d47e9dd1ec9e1f16412449c62e6a
parent67992af4d5dad3ed8eb7952694cfa58af1ba9de3 (diff)
downloadqtivi-qface-ee1ef38757970c93932e5028f8fe86fc1aa8d7e0.tar.gz
Added initial support for external annotation files using the YAML syntax. See grammar documentation
-rw-r--r--docs/grammar.rst29
-rw-r--r--qface/generator.py28
-rw-r--r--tests/in/com.pelagicore.ivi.tuner.yaml2
-rw-r--r--tests/test_tags.py7
4 files changed, 61 insertions, 5 deletions
diff --git a/docs/grammar.rst b/docs/grammar.rst
index f7977ce..c858e77 100644
--- a/docs/grammar.rst
+++ b/docs/grammar.rst
@@ -105,7 +105,36 @@ Below is an example of a QFace file.
}
+Tags / Annotations
+==================
+Tags allows an interface author to extend the existing grammar with additional meta information, called tags, aka annotations. One or several annotations can stand in from of a module, interface, struct or enum. They are also allowed before an operation, property or event. Everywhere where a documentation comment is allowed you can also add annotations.
+
+An annotation looks like this::
+
+ @service(port=12345)
+ interface Tuner {
+ }
+
+A annotation format is very similar to an operation signature prefixed with an `@` sign and no return value.
+
+The annotation are available later when navigating the domain model.
+
+.. note:: QFace does not specify specific annotations, but defines just the annotation format. The set of annotations supported must be defined and documented by the generator.
+
+.. rubric:: Annotation Documents
+
+QFace allows also to specify these annotations in external documents using the `YAML` syntax. For this you need to create a document with the same name as the QFace document but with the extension `.yaml`. It should have roughly the following format
+
+.. code-block:: yaml
+
+ com.pelagicore.ivi.Tuner:
+ service:
+ port: 12345
+
+On the root level should be a fully qualified name of a symbol. The symbol will be looked up and the following annotation information merged with the existing annotations form the QFace document.
+
+.. warning:: External annotation with the same name will override the QFace document annotation with the same name on the specified symbol.
diff --git a/qface/generator.py b/qface/generator.py
index 0da3613..01e7dfb 100644
--- a/qface/generator.py
+++ b/qface/generator.py
@@ -7,6 +7,7 @@ from antlr4.error import DiagnosticErrorListener
import shelve
import logging
import hashlib
+import yaml
from .idl.parser.TLexer import TLexer
from .idl.parser.TParser import TParser
@@ -96,15 +97,17 @@ class FileSystem(object):
"""QFace helper functions to work with the file system"""
@staticmethod
- def parse_document(path: str, system: System = None):
+ def parse_document(document: Path, system: System = None):
"""Parses a document and returns the resulting domain system
:param path: document path to parse
:param system: system to be used (optional)
"""
- logger.debug('parse document: {0}'.format(path))
- stream = FileStream(str(path), encoding='utf-8')
- return FileSystem._parse_stream(stream, system)
+ logger.debug('parse document: {0}'.format(document))
+ stream = FileStream(str(document), encoding='utf-8')
+ system = FileSystem._parse_stream(stream, system)
+ FileSystem._merge_meta_information(system, document)
+ return system
@staticmethod
def _parse_stream(stream, system: System = None):
@@ -121,6 +124,20 @@ class FileSystem(object):
return system
@staticmethod
+ def _merge_meta_information(system, document):
+ """Tries to find a YAML document named after the qface document and
+ for each root symbol identifier updated the tag information of that symbol
+ """
+ path = document.stripext() + '.yaml'
+ if path.exists():
+ click.secho('merge tags from {0}'.format(path), fg='blue')
+ meta = yaml.load(path.text())
+ for identifier, data in meta.items():
+ symbol = system.lookup(identifier)
+ if symbol:
+ symbol.tags.update(data)
+
+ @staticmethod
def parse(input, identifier: str = None, use_cache=False, clear_cache=True, pattern="*.qface"):
"""Input can be either a file or directory or a list of files or directory.
A directory will be parsed recursively. The function returns the resulting system.
@@ -131,7 +148,7 @@ class FileSystem(object):
:param clear_cache: clears the domain cache (defaults to true)
"""
inputs = input if isinstance(input, (list, tuple)) else [input]
- logging.debug('parse input={0}'.format(inputs))
+ logger.debug('parse input={0}'.format(inputs))
identifier = 'system' if not identifier else identifier
system = System()
cache = None
@@ -150,6 +167,7 @@ class FileSystem(object):
else:
for document in path.walkfiles(pattern):
FileSystem.parse_document(document, system)
+
if use_cache:
cache[identifier] = system
return system
diff --git a/tests/in/com.pelagicore.ivi.tuner.yaml b/tests/in/com.pelagicore.ivi.tuner.yaml
new file mode 100644
index 0000000..948c50c
--- /dev/null
+++ b/tests/in/com.pelagicore.ivi.tuner.yaml
@@ -0,0 +1,2 @@
+com.pelagicore.ivi.tuner.Tuner:
+ port: 12345
diff --git a/tests/test_tags.py b/tests/test_tags.py
index e8f8ca7..527b623 100644
--- a/tests/test_tags.py
+++ b/tests/test_tags.py
@@ -37,3 +37,10 @@ def test_tag():
assert enum is module.lookup('Waveband')
assert 'default' in enum.tags
assert enum.attribute('default', 'value') == 'FM'
+
+
+def test_meta_tags():
+ system = loadTuner()
+ interface = system.lookup('com.pelagicore.ivi.tuner.Tuner')
+ assert interface
+ assert 'port' in interface.tags