Skip to content

Commit

Permalink
Merge branch 'release/1.10'
Browse files Browse the repository at this point in the history
  • Loading branch information
jryannel committed Mar 7, 2018
2 parents 98ace3d + ab23ca3 commit 1461a0c
Show file tree
Hide file tree
Showing 23 changed files with 1,177 additions and 638 deletions.
30 changes: 30 additions & 0 deletions docs/extending.rst
Expand Up @@ -195,3 +195,33 @@ See below for a simple example
{% endwith %}

Each tag in the JavaDoc styled comment, will be converted into a property of the object returned by `parse_doc`. All lines without a tag will be merged into the description tag.


Language Profiles
=================


QFace supports the notion of profile. A profile is a set of features supported by the named profile. The intention of a profile is to make it easier for generator writers to stick to a limited set of language features, also if the overall language is evolving.

Currently there exists three language profiles:

* Micro - A limited set of languages features. The base profile. It does not allow importing of other modules or extending an interface, neither does it support maps.
* Advanced - Builds upon micro and allows imports, maps, interface extension.
* Full - Builds up on advanced and will also contain experimental language features.

The current features defined are:
- const oeprations
- const properties
- imports
- maps
- interface extensions

The profiles and features are defined in the `qface.idl.profile` module.

.. code-block:: py
from qface.generator import FileSystem
from qface.idl.profile import EProfile
system = FileSystem.parse(input=input, profile=EProfile.MICRO)
18 changes: 18 additions & 0 deletions docs/grammar.rst
Expand Up @@ -180,6 +180,8 @@ Below is an example of a QFace file.
list<int> primitiveList;
list<Station> complexList;
map<int> simpleMap;
map<Station> complexMap;
model<int> primitiveModel;
model<Station> complexModel;
}
Expand Down Expand Up @@ -220,6 +222,22 @@ Below is an example of a QFace file.
}
Nested Types
============

A nested type is a complex type which nests another type. These are container types, e.g. list, map or model.

.. code-block:: language
list<Color> colors
map<Station> stations
model<WeatherInfo> weather
A list is an array of the provided value type. A map specifies only the value type. The key-type should be generic (e.g. a string type) and can be freely choosen by the generator. This allows for example the geenrator to add an id to each structure and use it as a key in the map.

A model is a special type of a list, it defines the model type can stream the data (e.g. add/change/remove) and the changes should be reflected by a more advanced API. Also the data could in general grow unlimited and the generator should provide some form of pagination or window API. You should use a model if you expect the data it represents can grow in a way it may influence the performance of your API.

Annotations
===========

Expand Down
4 changes: 2 additions & 2 deletions qface/__about__.py
Expand Up @@ -9,7 +9,7 @@
__title__ = "qface"
__summary__ = "A generator framework based on a common modern IDL"
__url__ = "https://pelagicore.github.io/qface/"
__version__ = "1.9.1"
__version__ = "1.10"
__author__ = "JRyannel"
__author_email__ = "qface-generator@googlegroups.com"
__copyright__ = "2019 Pelagicore"
__copyright__ = "2018 Pelagicore"
15 changes: 8 additions & 7 deletions qface/generator.py
Expand Up @@ -17,6 +17,7 @@
from .idl.parser.TLexer import TLexer
from .idl.parser.TParser import TParser
from .idl.parser.TListener import TListener
from .idl.profile import EProfile
from .idl.domain import System
from .idl.listener import DomainListener
from .utils import merge
Expand Down Expand Up @@ -256,10 +257,10 @@ class FileSystem(object):
""" enables strict parsing """

@staticmethod
def parse_document(document: Path, system: System = None):
def parse_document(document: Path, system: System = None, profile=EProfile.FULL):
error = False
try:
return FileSystem._parse_document(document, system)
return FileSystem._parse_document(document, system, profile)
except FileNotFoundError as e:
click.secho('{0}: error: file not found'.format(document), fg='red', err=True)
error = True
Expand All @@ -270,20 +271,20 @@ def parse_document(document: Path, system: System = None):
sys.exit(-1)

@staticmethod
def _parse_document(document: Path, system: System = None):
def _parse_document(document: Path, system: System = None, profile=EProfile.FULL):
"""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(document))
stream = FileStream(str(document), encoding='utf-8')
system = FileSystem._parse_stream(stream, system, document)
system = FileSystem._parse_stream(stream, system, document, profile)
FileSystem.merge_annotations(system, document.stripext() + '.yaml')
return system

@staticmethod
def _parse_stream(stream, system: System = None, document=None):
def _parse_stream(stream, system: System = None, document=None, profile=EProfile.FULL):
logger.debug('parse stream')
system = system or System()

Expand All @@ -293,7 +294,7 @@ def _parse_stream(stream, system: System = None, document=None):
parser.addErrorListener(ReportingErrorListener(document))
tree = parser.documentSymbol()
walker = ParseTreeWalker()
walker.walk(DomainListener(system), tree)
walker.walk(DomainListener(system, profile), tree)
return system

@staticmethod
Expand All @@ -311,7 +312,7 @@ def merge_annotations(system, document):
merge(symbol.tags, data)

@staticmethod
def parse(input, identifier: str = None, use_cache=False, clear_cache=True, pattern="*.qface"):
def parse(input, identifier: str = None, use_cache=False, clear_cache=True, pattern="*.qface", profile=EProfile.FULL):
"""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.
Stores the result of the run in the domain cache named after the identifier.
Expand Down
97 changes: 63 additions & 34 deletions qface/helper/qtcpp.py
Expand Up @@ -18,7 +18,7 @@ def className(symbol):
@staticmethod
def defaultValue(symbol):
prefix = Filters.classPrefix
t = symbol.type # type: qface.domain.TypeSymbol
t = symbol.type
if t.is_primitive:
if t.is_int:
return 'int(0)'
Expand All @@ -33,30 +33,29 @@ def defaultValue(symbol):
elif t.is_void:
return ''
elif t.is_enum:
module_name = upper_first(t.reference.module.module_name)
value = next(iter(t.reference.members))
return '{0}{1}Module::{2}'.format(prefix, module_name, value)
return '{0}::{0}Enum::{1}'.format(symbol.type, value)
elif symbol.kind == 'enum':
value = next(iter(symbol.members))
return '{0}::{1}'.format(symbol, value)
elif t.is_flag:
return '0'
elif symbol.type.is_list:
nested = Filters.returnType(symbol.type.nested)
elif t.is_list:
nested = Filters.returnType(t.nested)
return 'QVariantList()'.format(nested)
elif symbol.type.is_struct:
return '{0}{1}()'.format(prefix, symbol.type)
elif symbol.type.is_model:
nested = symbol.type.nested
if nested.is_primitive:
return 'new {0}VariantModel(this)'.format(prefix)
elif nested.is_complex:
return 'new {0}{1}Model(this)'.format(prefix, nested)
return 'XXX'
elif t.is_struct:
return '{0}{1}()'.format(prefix, t)
elif t.is_model:
return 'new VariantModel(this)'
elif t.is_interface:
return 'nullptr'
raise Exception("Unknown symbol type" + repr(symbol))

@staticmethod
def parameterType(symbol):
prefix = Filters.classPrefix
module_name = upper_first(symbol.module.module_name)
if symbol.type.is_enum:
return '{0}{1}Module::{2} {3}'.format(prefix, module_name, symbol.type, symbol)
return '{0}::{0}Enum {1}'.format(symbol.type, symbol)
if symbol.type.is_void or symbol.type.is_primitive:
if symbol.type.is_string:
return 'const QString &{0}'.format(symbol)
Expand All @@ -73,22 +72,20 @@ def parameterType(symbol):
nested = Filters.returnType(symbol.type.nested)
return 'const QVariantList &{1}'.format(nested, symbol)
elif symbol.type.is_model:
nested = symbol.type.nested
if nested.is_primitive:
return '{0}VariantModel *{1}'.format(prefix, symbol)
elif nested.is_complex:
return '{0}{1}Model *{2}'.format(prefix, nested, symbol)
else:
return 'const {0}{1} &{2}'.format(prefix, symbol.type, symbol)
return 'XXX'
return 'VariantModel *{0}'.format(symbol)
elif symbol.type.is_complex:
if symbol.type.is_interface:
return '{0}Base *{1}'.format(symbol.type, symbol)
else:
return 'const {0}{1} &{2}'.format(prefix, symbol.type, symbol)
raise Exception("Unknown symbol type")

@staticmethod
def returnType(symbol):
prefix = Filters.classPrefix
module_name = upper_first(symbol.module.module_name)
t = symbol.type
if t.is_enum:
return '{0}{1}Module::{2}'.format(prefix, module_name, symbol.type)
return '{0}::{0}Enum'.format(symbol.type)
if symbol.type.is_void or symbol.type.is_primitive:
if t.is_string:
return 'QString'
Expand All @@ -108,14 +105,44 @@ def returnType(symbol):
nested = Filters.returnType(symbol.type.nested)
return 'QVariantList'.format(nested)
elif symbol.type.is_model:
nested = symbol.type.nested
if nested.is_primitive:
return '{0}VariantModel *'.format(prefix)
elif nested.is_complex:
return '{0}{1}Model *'.format(prefix, nested)
else:
return '{0}{1}'.format(prefix, symbol.type)
return 'XXX'
return 'VariantModel *'
elif symbol.type.is_complex:
if symbol.type.is_interface:
return '{0}Base *'.format(symbol.type)
else:
return '{0}{1}'.format(prefix, symbol.type)
raise Exception("Unknown symbol type")

@staticmethod
def header_dependencies(symbol):
types = symbol.dependencies
lines = []
for t in types:
if t.is_primitive:
continue
if t.is_model:
lines.append('class VariantModel;')
if t.is_interface:
lines.append('class {0};'.format(t))
if t.is_struct:
lines.append('#include "{0}.h"'.format(t))
return "\n".join(lines)

@staticmethod
def source_dependencies(symbol):
types = symbol.dependencies
lines = []
module_name = symbol.module.module_name
if not symbol.kind == 'module':
lines.append('#include "{0}module.h"'.format(module_name.lower()))
for t in types:
if t.is_primitive:
continue
if t.is_model:
lines.append('#include "variantmodel.h"')
if t.is_interface:
lines.append('#include "{0}.h"'.format(t.name.lower()))
return "\n".join(lines)

@staticmethod
def open_ns(symbol):
Expand Down Expand Up @@ -221,4 +248,6 @@ def get_filters():
'identifier': Filters.identifier,
'path': Filters.path,
'className': Filters.className,
'source_dependencies': Filters.source_dependencies,
'header_dependencies': Filters.header_dependencies,
}
14 changes: 13 additions & 1 deletion qface/idl/domain.py
Expand Up @@ -124,6 +124,7 @@ def __init__(self, name: str, module: 'Module'):
self._tags = dict()

self._contentMap = ChainMap()
self._dependencies = set()
self.type = TypeSymbol('', self)
self.kind = self.__class__.__name__.lower()
""" the associated type information """
Expand Down Expand Up @@ -162,6 +163,12 @@ def contents(self):
""" return general list of symbol contents """
return self._contentMap.values()

@property
def dependencies(self):
if not self._dependencies:
self._dependencies = [x.type for x in self.contents]
return self._dependencies

def toJson(self):
o = super().toJson()
if self.type.is_valid:
Expand All @@ -184,6 +191,8 @@ def __init__(self, name: str, parent: NamedElement):
""" if type represents a complex type """
self.is_list = False # type:bool
""" if type represents a list of nested types """
self.is_map = False # type:bool
""" if type represents a map of nested types. A key type is not defined """
self.is_model = False # type:bool
""" if type represents a model of nested types """
self.nested = None
Expand All @@ -197,6 +206,7 @@ def is_valid(self):
return (self.is_primitive and self.name) \
or (self.is_complex and self.name) \
or (self.is_list and self.nested) \
or (self.is_map and self.nested) \
or (self.is_model and self.nested)

@property
Expand Down Expand Up @@ -226,7 +236,7 @@ def is_var(self):

@property
def is_enumeration(self):
'''checks if type is complex and insytance of type Enum'''
'''checks if type is complex and instance of type Enum'''
return self.is_complex and isinstance(self.reference, Enum)

@property
Expand Down Expand Up @@ -278,6 +288,8 @@ def toJson(self):
o['complex'] = self.is_complex
if self.is_list:
o['list'] = self.is_list
if self.is_map:
o['map'] = self.is_map
if self.is_model:
o['model'] = self.is_model
if self.nested:
Expand Down

0 comments on commit 1461a0c

Please sign in to comment.