├── .github └── workflows │ └── testrunner.yml ├── .gitignore ├── LICENSE.txt ├── MANIFEST.in ├── NEWS.md ├── README.md ├── TODO.md ├── data ├── allplan-ifc-examples │ ├── README.md │ ├── test-01.ifc │ └── test-01.ifcXML ├── formal │ ├── express.g4 │ ├── iso-10303-11--2004.bnf │ ├── iso-10303-11--2004.ebnf │ ├── iso-10303-21--2002.bnf │ └── iso-10303-21--2016.ebnf ├── p21examples │ └── README.md └── schema │ ├── IFC4x2.exp │ ├── IFC4x2.xsd │ └── ap203.exp ├── docs ├── Makefile ├── make.bat ├── source │ ├── conf.py │ ├── express.rst │ ├── index.rst │ ├── p21.rst │ └── step-file-loc.rst └── stepcode │ ├── README.md │ ├── SCL │ ├── AggregationDataTypes.py │ ├── BaseType.py │ ├── Builtin.py │ ├── ConstructedDataTypes.py │ ├── Model.py │ ├── Part21.py │ ├── Rules.py │ ├── SCLBase.py │ ├── SimpleDataTypes.py │ ├── TypeChecker.py │ ├── Utils.py │ ├── __init__.py │ └── essa_par.py │ └── ifc4x2.py ├── examples └── create_simple_p21_file.py ├── profiling ├── express_antlr4.py ├── express_pyparser.py ├── p21_loader.py ├── result-express-antlr4-cpython.txt ├── result-express-antlr4-pypy3.txt ├── result-express-pyparser-cpython.txt ├── result-p21-fast-loader-cpython.txt ├── result-p21-fast-loader-pypy3.txt ├── result-p21-ply-cpython.txt ├── result-p21-ply-pypy3.txt ├── result-p21-pyparsing-cpython.txt └── result-p21-pyparsing-pypy3.txt ├── pytest.ini ├── requirements.txt ├── setup.cfg ├── setup.py ├── src └── steputils │ ├── __init__.py │ ├── exceptions.py │ ├── express │ ├── __init__.py │ ├── ast.py │ ├── express.interp │ ├── express.tokens │ ├── expressLexer.interp │ ├── expressLexer.py │ ├── expressLexer.tokens │ ├── expressListener.py │ ├── expressParser.py │ └── pyparser.py │ ├── p21.py │ ├── strings.py │ ├── tools.py │ └── version.py └── tests ├── express ├── test_antlr4_parser.py ├── test_ast.py ├── test_constant_decl.py ├── test_entity.py ├── test_parse_actions.py ├── test_simple_expressions.py └── test_type_decl.py └── p21 ├── test_p21_api.py ├── test_p21_factory.py ├── test_p21_lexer.py ├── test_p21_parser.py └── test_strings.py /.github/workflows/testrunner.yml: -------------------------------------------------------------------------------- 1 | name: Testrunner 2 | on: 3 | push: 4 | branches: [ master ] 5 | pull_request: 6 | branches: [ master ] 7 | jobs: 8 | build: 9 | runs-on: ${{ matrix.os }} 10 | strategy: 11 | matrix: 12 | os: [ubuntu-latest, windows-latest] 13 | python-version: ['3.7', '3.8', '3.9', '3.10', 'pypy-3.7'] 14 | steps: 15 | - uses: actions/checkout@v2 16 | - name: Set up Python ${{ matrix.python-version }} 17 | uses: actions/setup-python@v2 18 | with: 19 | python-version: ${{ matrix.python-version }} 20 | - name: Install requirements 21 | run: | 22 | python -m pip install -U pip 23 | python -m pip install wheel pytest 24 | - name: Install package 25 | run: | 26 | python -m pip install . 27 | - name: Run pytest 28 | run: | 29 | python -m pytest 30 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .tox 2 | .cache 3 | .eggs 4 | .pytest_cache 5 | .vscode 6 | build/* 7 | dist/* 8 | .idea/* 9 | /src/steputils.egg-info/* 10 | data/p21examples/*/*.stp 11 | data/p21examples/*/*.ifc 12 | docs/build/* 13 | __pycache__ 14 | *.bat 15 | tox.ini 16 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019, Manfred Moitzi 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a 6 | copy of this software and associated documentation files (the 7 | "Software"), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included 14 | in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 17 | OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 20 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 21 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 22 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include NEWS.md README.md LICENSE.txt 2 | include requirements.txt 3 | recursive-include examples *.py 4 | recursive-include tests *.py 5 | -------------------------------------------------------------------------------- /NEWS.md: -------------------------------------------------------------------------------- 1 | 2 | News 3 | ==== 4 | 5 | Version 0.1b0 6 | ------------- 7 | 8 | - BUGFIX: [#3](https://github.com/mozman/steputils/pull/3) multiline string support 9 | 10 | Version 0.1a5 11 | ------------- 12 | 13 | - EXPRESS parser implemented with antlr4 14 | 15 | Version 0.1a2 16 | ------------- 17 | 18 | - optimized STEP-file loader and serializer, speed gain about 7x 19 | - renamed module ``stepfile`` into ``p21`` and implemented factory functions at module level 20 | 21 | Version 0.1a1 22 | ------------- 23 | 24 | - pre-alpha state 25 | - STEP-file loader and serializer works 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | STEPutils 3 | ========= 4 | 5 | THIS PROJECT IS IN PLANNING STATE! 6 | ---------------------------------- 7 | 8 | Abstract 9 | -------- 10 | 11 | STEPutils is a Python package to manage STEP model data. 12 | 13 | The intention of this package is to build a simple document object model (DOM) for STEP model data like 14 | `xml.etree.ElementTree` for XML data. STEPutils could be used as import/export layer for CAD like application. 15 | The DOM has methods to traverse, create and delete object nodes but no further CAD-like functionality like translating, 16 | scaling or rotating objects, if you need that - you are looking for a CAD application like 17 | [FreeCAD](https://www.freecadweb.org/). 18 | 19 | For more information about the STEP (ISO 10303) standard read this 20 | [Wikipedia](https://en.wikipedia.org/w/index.php?title=ISO_10303) article. 21 | 22 | Quick-Info 23 | ---------- 24 | 25 | - Python package to manage a simple document object model (DOM) for STEP model data 26 | - the intended audience are developers 27 | - requires at least Python 3.7 28 | - requires `antlr4-python3-runtime<4.10` !!! 29 | - OS independent 30 | - tested with GitHub actions for windows-latest and linux-latest 31 | - MIT-License 32 | 33 | Installation 34 | ------------ 35 | 36 | Install with pip for Python 3.7 and later: 37 | 38 | pip install steputils 39 | 40 | Install latest development version with pip from GitHub: 41 | 42 | pip install git+https://github.com/mozman/steputils.git@master 43 | 44 | or from source: 45 | 46 | python setup.py install 47 | 48 | Documentation 49 | ------------- 50 | 51 | https://steputils.readthedocs.io 52 | 53 | Contribution 54 | ------------ 55 | 56 | The source code of STEPutils can be found at __GitHub__, target your pull requests to the `master` branch: 57 | 58 | http://github.com/mozman/steputils 59 | -------------------------------------------------------------------------------- /TODO.md: -------------------------------------------------------------------------------- 1 | Project Goals 2 | ============= 3 | 4 | The main goal is to build a simple document object model (DOM) for [STEP] model data. 5 | The DOM can be used as an import/export layer for CAD like application or applications which need access to 6 | STEP model data. STEP defines various applications, one example application is [IFC4] data 7 | (Industry Foundation Classes [1]), this also will be the first application with explict support, because it is related 8 | to my profession and because there is a free documentation for [IFC4] provided by 9 | [buildingSMART International](https://www.buildingsmart.org/). Other applications of the ISO 10303 standard may not have 10 | such free resources, and I will not purchase ISO standards for a hobby project and because of the complexity it is 11 | not possible for me to support all application in an extensive way beyond a generic support. 12 | 13 | The DOM should provide methods to travers the model and create or delete object nodes like `xml.etree.ElementTree` for 14 | XML data. 15 | 16 | Loading and Storing Data 17 | ------------------------ 18 | 19 | Many file formats of the [STEP] standard should be supported, as plain text file and also as 20 | compressed zip file: 21 | 22 | 1. [STEP] - Standard for the Exchange of Product model data - `p21.readfile()` already works and is fast enough. 23 | 2. [XML] - Extensible Markup Language, fast to read from Python with `xml.etree.ElementTree` 24 | 3. [JSON] - JavaScript Object Notation, no examples available yet 25 | 26 | ### EXPRESS Schema Parser 27 | 28 | A Python data model generator has to be written for EXPRESS definition files, the solution I found online called 29 | [STEPcode], is a starting point, but the generated Python code is ugly and does not correspond to PEP8, generated 30 | code is included in folder `doc/stepcode`. 31 | 32 | - EXPRESS data specification language, EXPRESS is a standard data modeling language for product data. 33 | EXPRESS is formalized in the ISO Standard for the Exchange of Product model STEP (ISO 10303), and standardized 34 | as [ISO 10303-11]. 35 | - `iso-10303-11--2004.bnf`: Backus-Naur-Form for EXPRESS 36 | - EXPRESS parser implemented by antlr4 works but is very slow. Generating a Python data model from EXPRESS schema 37 | is a done once and therefore hasn't to be very fast, but it is a pain in the development and testing phase 38 | (Caching AST!). 39 | - The pyparsing implementation does not work but is promising for speed, so I will not abandon this implementation 40 | complete - but for now I go the antlr4 route. 41 | 42 | #### Abstract Syntax Tree for EXPRESS 43 | 44 | It is required to create an AST from the parse tree, (EXP|XSD) -> AST -> Python Data Model. 45 | 46 | Caching the AST could speed up developing and testing phase for the slow antlr4 parser! 47 | 48 | ### XML Requirements & Resources 49 | 50 | Import/Export by standard Python module `xml.etree.ElementTree` and the ability to use an faster alternative 51 | implementation like [lxml]. 52 | 53 | ### JSON Requirements & Resources 54 | 55 | Import/Export by the standard Python module `json` and the ability to use an faster alternative implementation 56 | like [orjson]. 57 | 58 | Internal Data Model 59 | ------------------- 60 | 61 | ### Classes 62 | 63 | 1. Static: create class declarations from EXPRESS file as a Python .py file 64 | - (+) manually modifications are possible 65 | - (-) modifications are lost at recreation process 66 | - (-) big code base 67 | 68 | 2. Dynamic: use Python meta programming to create classes on the fly 69 | - (-) no modifications are possible by default, maybe extensible by mixins 70 | - (+) small code base 71 | 72 | ### Instances 73 | 74 | Instantiation by factory! `args` is a list of positional arguments and `kwargs` are keyword arguments as a dict. 75 | 76 | ifc4 = steputils.model('IFC4') 77 | root = ifc4.get_root() 78 | entity = ifc4.entity('IfcRoot', IfcGloballyUniqueId=guid()) 79 | 80 | ### DOM Interface 81 | 82 | Create new model: 83 | 84 | ifc4 = steputils.model('IFC4') 85 | 86 | Get root node: 87 | 88 | root = ifc4.get_root() 89 | 90 | Create new node: 91 | 92 | entity = ifc4.entity('IfcRoot', IfcGloballyUniqueId=guid()) 93 | 94 | Adding new node to parent: 95 | 96 | # add one child node 97 | root.append(entity) 98 | 99 | # add multiple child nodes 100 | root.extend([entity, entity2, ...]) 101 | 102 | # insert at a specified position 103 | root.insert(index, entity) 104 | 105 | Iterate child nodes: 106 | 107 | for entity in root: 108 | pass 109 | 110 | Delete child nodes: 111 | 112 | root.remove(entity) 113 | 114 | ### Query Language - First Draft 115 | 116 | Query entities in a XPath like way: 117 | 118 | `tag` Selects all child elements with the given tag. For example, `spam` selects all child elements named spam, 119 | and `spam/egg` selects all grandchildren named egg in all children named spam. 120 | 121 | `.` Selects the current node. This is mostly useful at the beginning of the path, to indicate that it’s a relative path. 122 | 123 | `*` Selects all child elements. For example, `*/egg` selects all grandchildren named egg 124 | 125 | `//` Selects all subelements, on all levels beneath the current element. For example, `.//egg` selects all egg 126 | elements in the entire tree. 127 | 128 | `..` Selects the parent element. 129 | 130 | `[@attrib]` Selects all elements that have the given attribute. 131 | 132 | `[@attrib='value']` Selects all elements for which the given attribute has the given value. The value cannot contain quotes. 133 | 134 | `[tag]` Selects all elements that have a child named tag. Only immediate children are supported. 135 | 136 | `[tag='text']` Selects all elements that have a child named tag whose complete text content, including descendants, 137 | equals the given text. 138 | 139 | `[position]` Selects all elements that are located at the given position. The position is an integer: 1 is the first 140 | position, -1 for the last position like Python list indices. 141 | 142 | root.findall('spam') # finds all matching child elements named spam 143 | root.find('spam') # find first matching child element named spam 144 | 145 | 146 | [IFC4]: https://technical.buildingsmart.org/ 147 | [STEP]: https://en.wikipedia.org/wiki/ISO_10303-21 148 | [XML]: https://en.wikipedia.org/wiki/XML 149 | [JSON]: https://en.wikipedia.org/wiki/JSON 150 | [STEPcode]: https://stepcode.github.io/ 151 | [orjson]: https://pypi.org/project/orjson/ 152 | [lxml]: https://pypi.org/project/lxml/ 153 | [BNF]: https://en.wikipedia.org/wiki/Backus%E2%80%93Naur_form 154 | [ISO 10303-21]: https://en.wikipedia.org/wiki/ISO_10303-21 155 | [ISO 10303-11]: https://en.wikipedia.org/wiki/EXPRESS_(data_modeling_language) 156 | [pyparsing]: https://pypi.org/project/pyparsing/ 157 | 158 | [1]: https://en.wikipedia.org/wiki/Industry_Foundation_Classes 159 | -------------------------------------------------------------------------------- /data/allplan-ifc-examples/README.md: -------------------------------------------------------------------------------- 1 | Example File 2 | ============ 3 | 4 | These example files are created by myself and licenced by: 5 | Creative Commons [CC BY](https://creativecommons.org/licenses/by-sa/4.0/). 6 | -------------------------------------------------------------------------------- /data/formal/iso-10303-11--2004.bnf: -------------------------------------------------------------------------------- 1 | ;; iso-10303-11:2004 2 | 3 | 0 ABS = ’abs’ . 4 | 1 ABSTRACT = ’abstract’ . 5 | 2 ACOS = ’acos’ . 6 | 3 AGGREGATE = ’aggregate’ . 7 | 4 ALIAS = ’alias’ . 8 | 5 AND = ’and’ . 9 | 6 ANDOR = ’andor’ . 10 | 7 ARRAY = ’array’ . 11 | 8 AS = ’as’ . 12 | 9 ASIN = ’asin’ . 13 | 10 ATAN = ’atan’ . 14 | 11 BAG = ’bag’ . 15 | 12 BASED_ON = ’based_on’ . 16 | 13 BEGIN = ’begin’ . 17 | 14 BINARY = ’binary’ . 18 | 15 BLENGTH = ’blength’ . 19 | 16 BOOLEAN = ’boolean’ . 20 | 17 BY = ’by’ . 21 | 18 CASE = ’case’ . 22 | 19 CONSTANT = ’constant’ . 23 | 20 CONST_E = ’const_e’ . 24 | 21 COS = ’cos’ . 25 | 22 DERIVE = ’derive’ . 26 | 23 DIV = ’div’ . 27 | 24 ELSE = ’else’ . 28 | 25 END = ’end’ . 29 | 26 END_ALIAS = ’end_alias’ . 30 | 27 END_CASE = ’end_case’ . 31 | 28 END_CONSTANT = ’end_constant’ . 32 | 29 END_ENTITY = ’end_entity’ . 33 | 30 END_FUNCTION = ’end_function’ . 34 | 31 END_IF = ’end_if’ . 35 | 32 END_LOCAL = ’end_local’ . 36 | 33 END_PROCEDURE = ’end_procedure’ . 37 | 34 END_REPEAT = ’end_repeat’ . 38 | 35 END_RULE = ’end_rule’ . 39 | 36 END_SCHEMA = ’end_schema’ . 40 | 37 END_SUBTYPE_CONSTRAINT = ’end_subtype_constraint’ . 41 | 38 END_TYPE = ’end_type’ . 42 | 39 ENTITY = ’entity’ . 43 | 40 ENUMERATION = ’enumeration’ . 44 | 41 ESCAPE = ’escape’ . 45 | 42 EXISTS = ’exists’ . 46 | 43 EXTENSIBLE = ’extensible’ . 47 | 44 EXP = ’exp’ . 48 | 45 FALSE = ’false’ . 49 | 46 FIXED = ’fixed’ . 50 | 47 FOR = ’for’ . 51 | 48 FORMAT = ’format’ . 52 | 49 FROM = ’from’ . 53 | 50 FUNCTION = ’function’ . 54 | 51 GENERIC = ’generic’ . 55 | 52 GENERIC_ENTITY = ’generic_entity’ . 56 | 53 HIBOUND = ’hibound’ . 57 | 54 HIINDEX = ’hiindex’ . 58 | 55 IF = ’if’ . 59 | 56 IN = ’in’ . 60 | 57 INSERT = ’insert’ . 61 | 58 INTEGER = ’integer’ . 62 | 59 INVERSE = ’inverse’ . 63 | 60 LENGTH = ’length’ . 64 | 61 LIKE = ’like’ . 65 | 62 LIST = ’list’ . 66 | 63 LOBOUND = ’lobound’ . 67 | 64 LOCAL = ’local’ . 68 | 65 LOG = ’log’ . 69 | 66 LOG10 = ’log10’ . 70 | 67 LOG2 = ’log2’ . 71 | 68 LOGICAL = ’logical’ . 72 | 69 LOINDEX = ’loindex’ . 73 | 70 MOD = ’mod’ . 74 | 71 NOT = ’not’ . 75 | 72 NUMBER = ’number’ . 76 | 73 NVL = ’nvl’ . 77 | 74 ODD = ’odd’ . 78 | 75 OF = ’of’ . 79 | 76 ONEOF = ’oneof’ . 80 | 77 OPTIONAL = ’optional’ . 81 | 78 OR = ’or’ . 82 | 79 OTHERWISE = ’otherwise’ . 83 | 80 PI = ’pi’ . 84 | 81 PROCEDURE = ’procedure’ . 85 | 82 QUERY = ’query’ . 86 | 83 REAL = ’real’ . 87 | 84 REFERENCE = ’reference’ . 88 | 85 REMOVE = ’remove’ . 89 | 86 RENAMED = ’renamed’ . 90 | 87 REPEAT = ’repeat’ . 91 | 88 RETURN = ’return’ . 92 | 89 ROLESOF = ’rolesof’ . 93 | 90 RULE = ’rule’ . 94 | 91 SCHEMA = ’schema’ . 95 | 92 SELECT = ’select’ . 96 | 93 SELF = ’self’ . 97 | 94 SET = ’set’ . 98 | 95 SIN = ’sin’ . 99 | 96 SIZEOF = ’sizeof’ . 100 | 97 SKIP = ’skip’ . 101 | 98 SQRT = ’sqrt’ . 102 | 99 STRING = ’string’ . 103 | 100 SUBTYPE = ’subtype’ . 104 | 101 SUBTYPE_CONSTRAINT = ’subtype_constraint’ . 105 | 102 SUPERTYPE = ’supertype’ . 106 | 103 TAN = ’tan’ . 107 | 104 THEN = ’then’ . 108 | 105 TO = ’to’ . 109 | 106 TOTAL_OVER = ’total_over’ . 110 | 107 TRUE = ’true’ . 111 | 108 TYPE = ’type’ . 112 | 109 TYPEOF = ’typeof’ . 113 | 110 UNIQUE = ’unique’ . 114 | 111 UNKNOWN = ’unknown’ . 115 | 112 UNTIL = ’until’ . 116 | 113 USE = ’use’ . 117 | 114 USEDIN = ’usedin’ . 118 | 115 VALUE = ’value’ . 119 | 116 VALUE_IN = ’value_in’ . 120 | 117 VALUE_UNIQUE = ’value_unique’ . 121 | 118 VAR = ’var’ . 122 | 119 WHERE = ’where’ . 123 | 120 WHILE = ’while’ . 124 | 121 WITH = ’with’ . 125 | 122 XOR = ’xor’ . 126 | 123 bit = ’0’ | ’1’ . 127 | 124 digit = ’0’ | ’1’ | ’2’ | ’3’ | ’4’ | ’5’ | ’6’ | ’7’ | ’8’ | ’9’ . 128 | 125 digits = digit { digit } . 129 | 126 encoded_character = octet octet octet octet . 130 | 127 hex_digit = digit | ’a’ | ’b’ | ’c’ | ’d’ | ’e’ | ’f’ . 131 | 128 letter = ’a’ | ’b’ | ’c’ | ’d’ | ’e’ | ’f’ | ’g’ | ’h’ | ’i’ | ’j’ | ’k’ | ’l’ | ’m’ | ’n’ | ’o’ | ’p’ | ’q’ | ’r’ | ’s’ | ’t’ | ’u’ | ’v’ | ’w’ | ’x’ | ’y’ | ’z’ . 132 | 129 lparen_then_not_lparen_star = ’(’ { ’(’ } not_lparen_star { not_lparen_star } . 133 | 130 not_lparen_star = not_paren_star | ’)’ . 134 | 131 not_paren_star = letter | digit | not_paren_star_special . 135 | 132 not_paren_star_quote_special = ’!’ | ’"’ | ’#’ | ’$’ | ’%’ | ’&’ | ’+’ | ’,’ | ’-’ | ’.’ | ’/’ | ’:’ | ’;’ | ’<’ | ’=’ | ’>’ | ’?’ | ’@’ | ’[’ | ’\’ | ’]’ | ’^’ | ’_’ | ’‘’ | ’{’ | ’|’ | ’}’ | ’~’ . 136 | 133 not_paren_star_special = not_paren_star_quote_special | ’’’’ . 137 | 134 not_quote = not_paren_star_quote_special | letter | digit | ’(’ | ’)’ | ’*’ . 138 | 135 not_rparen_star = not_paren_star | ’(’ . 139 | 136 octet = hex_digit hex_digit . 140 | 137 special = not_paren_star_quote_special | ’(’ | ’)’ | ’*’ | ’’’’ . 141 | 138 not_rparen_star_then_rparen = not_rparen_star { not_rparen_star } ’)’ { ’)’ } . 142 | 139 binary_literal = ’%’ bit { bit } . 143 | 140 encoded_string_literal = ’"’ encoded_character { encoded_character } ’"’ . 144 | 141 integer_literal = digits . 145 | 142 real_literal = integer_literal | ( digits ’.’ [ digits ] [ ’e’ [ sign ] digits ] ) . 146 | 143 simple_id = letter { letter | digit | ’_’ } . 147 | 144 simple_string_literal = \q { ( \q \q ) | not_quote | \s | \x8 | \x9 | \xA | \xB | \xC | \xD } \q . 148 | 145 embedded_remark = ’(*’ [ remark_tag ] { ( not_paren_star { not_paren_star } ) | lparen_then_not_lparen_star | ( ’*’ { ’*’ } ) | not_rparen_star_then_rparen | embedded_remark } ’*)’ . 149 | 146 remark = embedded_remark | tail_remark . 150 | 147 remark_tag = ’"’ remark_ref { ’.’ remark_ref } ’"’ . 151 | 148 remark_ref = attribute_ref | constant_ref | entity_ref | enumeration_ref | function_ref | parameter_ref | procedure_ref | rule_label_ref | rule_ref | schema_ref | subtype_constraint_ref | type_label_ref | type_ref | variable_ref . 152 | 149 tail_remark = ’--’ [ remark_tag ] { \a | \s | \x8 | \x9 | \xA | \xB | \xC | \xD } \n . 153 | 150 attribute_ref = attribute_id . 154 | 151 constant_ref = constant_id . 155 | 152 entity_ref = entity_id . 156 | 153 enumeration_ref = enumeration_id . 157 | 154 function_ref = function_id . 158 | 155 parameter_ref = parameter_id . 159 | 156 procedure_ref = procedure_id . 160 | 157 rule_label_ref = rule_label_id . 161 | 158 rule_ref = rule_id . 162 | 159 schema_ref = schema_id . 163 | 160 subtype_constraint_ref = subtype_constraint_id . 164 | 161 type_label_ref = type_label_id . 165 | 162 type_ref = type_id . 166 | 163 variable_ref = variable_id . 167 | 164 abstract_entity_declaration = ABSTRACT . 168 | 165 abstract_supertype = ABSTRACT SUPERTYPE ’;’ . 169 | 166 abstract_supertype_declaration = ABSTRACT SUPERTYPE [ subtype_constraint ] . 170 | 167 actual_parameter_list = ’(’ parameter { ’,’ parameter } ’)’ . 171 | 168 add_like_op = ’+’ | ’-’ | OR | XOR . 172 | 169 aggregate_initializer = ’[’ [ element { ’,’ element } ] ’]’ . 173 | 170 aggregate_source = simple_expression . 174 | 171 aggregate_type = AGGREGATE [ ’:’ type_label ] OF parameter_type . 175 | 172 aggregation_types = array_type | bag_type | list_type | set_type . 176 | 173 algorithm_head = { declaration } [ constant_decl ] [ local_decl ] . 177 | 174 alias_stmt = ALIAS variable_id FOR general_ref { qualifier } ’;’ stmt { stmt } END_ALIAS ’;’ . 178 | 175 array_type = ARRAY bound_spec OF [ OPTIONAL ] [ UNIQUE ] instantiable_type . 179 | 176 assignment_stmt = general_ref { qualifier } ’:=’ expression ’;’ . 180 | 177 attribute_decl = attribute_id | redeclared_attribute . 181 | 178 attribute_id = simple_id . 182 | 179 attribute_qualifier = ’.’ attribute_ref . 183 | 180 bag_type = BAG [ bound_spec ] OF instantiable_type . 184 | 181 binary_type = BINARY [ width_spec ] . 185 | 182 boolean_type = BOOLEAN . 186 | 183 bound_1 = numeric_expression . 187 | 184 bound_2 = numeric_expression . 188 | 185 bound_spec = ’[’ bound_1 ’:’ bound_2 ’]’ . 189 | 186 built_in_constant = CONST_E | PI | SELF | ’?’ . 190 | 187 built_in_function = ABS | ACOS | ASIN | ATAN | BLENGTH | COS | EXISTS | EXP | FORMAT | HIBOUND | HIINDEX | LENGTH | LOBOUND | LOINDEX | LOG | LOG2 | LOG10 | NVL | ODD | ROLESOF | SIN | SIZEOF | SQRT | TAN | TYPEOF | USEDIN | VALUE | VALUE_IN | VALUE_UNIQUE . 191 | 188 built_in_procedure = INSERT | REMOVE . 192 | 189 case_action = case_label { ’,’ case_label } ’:’ stmt . 193 | 190 case_label = expression . 194 | 191 case_stmt = CASE selector OF { case_action } [ OTHERWISE ’:’ stmt ] END_CASE ’;’ . 195 | 192 compound_stmt = BEGIN stmt { stmt } END ’;’ . 196 | 193 concrete_types = aggregation_types | simple_types | type_ref . 197 | 194 constant_body = constant_id ’:’ instantiable_type ’:=’ expression ’;’ . 198 | 195 constant_decl = CONSTANT constant_body { constant_body } END_CONSTANT ’;’ . 199 | 196 constant_factor = built_in_constant | constant_ref . 200 | 197 constant_id = simple_id . 201 | 198 constructed_types = enumeration_type | select_type . 202 | 199 declaration = entity_decl | function_decl | procedure_decl | subtype_constraint_decl | type_decl . 203 | 200 derived_attr = attribute_decl ’:’ parameter_type ’:=’ expression ’;’ . 204 | 201 derive_clause = DERIVE derived_attr { derived_attr } . 205 | 202 domain_rule = [ rule_label_id ’:’ ] expression . 206 | 203 element = expression [ ’:’ repetition ] . 207 | 204 entity_body = { explicit_attr } [ derive_clause ] [ inverse_clause ] [ unique_clause ] [ where_clause ] . 208 | 205 entity_constructor = entity_ref ’(’ [ expression { ’,’ expression } ] ’)’ . 209 | 206 entity_decl = entity_head entity_body END_ENTITY ’;’ . 210 | 207 entity_head = ENTITY entity_id subsuper ’;’ . 211 | 208 entity_id = simple_id . 212 | 209 enumeration_extension = BASED_ON type_ref [ WITH enumeration_items ] . 213 | 210 enumeration_id = simple_id . 214 | 211 enumeration_items = ’(’ enumeration_id { ’,’ enumeration_id } ’)’ . 215 | 212 enumeration_reference = [ type_ref ’.’ ] enumeration_ref . 216 | 213 enumeration_type = [ EXTENSIBLE ] ENUMERATION [ ( OF enumeration_items ) | enumeration_extension ] . 217 | 214 escape_stmt = ESCAPE ’;’ . 218 | 215 explicit_attr = attribute_decl { ’,’ attribute_decl } ’:’ [ OPTIONAL ] parameter_type ’;’ . 219 | 216 expression = simple_expression [ rel_op_extended simple_expression ] . 220 | 217 factor = simple_factor [ ’**’ simple_factor ] . 221 | 218 formal_parameter = parameter_id { ’,’ parameter_id } ’:’ parameter_type . 222 | 219 function_call = ( built_in_function | function_ref ) [ actual_parameter_list ] . 223 | 220 function_decl = function_head algorithm_head stmt { stmt } END_FUNCTION ’;’ . 224 | 221 function_head = FUNCTION function_id [ ’(’ formal_parameter { ’;’ formal_parameter } ’)’ ] ’:’ parameter_type ’;’ . 225 | 222 function_id = simple_id . 226 | 223 generalized_types = aggregate_type | general_aggregation_types | generic_entity_type | generic_type . 227 | 224 general_aggregation_types = general_array_type | general_bag_type | general_list_type | general_set_type . 228 | 225 general_array_type = ARRAY [ bound_spec ] OF [ OPTIONAL ] [ UNIQUE ] parameter_type . 229 | 226 general_bag_type = BAG [ bound_spec ] OF parameter_type . 230 | 227 general_list_type = LIST [ bound_spec ] OF [ UNIQUE ] parameter_type . 231 | 228 general_ref = parameter_ref | variable_ref . 232 | 229 general_set_type = SET [ bound_spec ] OF parameter_type . 233 | 230 generic_entity_type = GENERIC_ENTITY [ ’:’ type_label ] . 234 | 231 generic_type = GENERIC [ ’:’ type_label ] . 235 | 232 group_qualifier = ’\’ entity_ref . 236 | 233 if_stmt = IF logical_expression THEN stmt { stmt } [ ELSE stmt { stmt } ] END_IF ’;’ . 237 | 234 increment = numeric_expression . 238 | 235 increment_control = variable_id ’:=’ bound_1 TO bound_2 [ BY increment ] . 239 | 236 index = numeric_expression . 240 | 237 index_1 = index . 241 | 238 index_2 = index . 242 | 239 index_qualifier = ’[’ index_1 [ ’:’ index_2 ] ’]’ . 243 | 240 instantiable_type = concrete_types | entity_ref . 244 | 241 integer_type = INTEGER . 245 | 242 interface_specification = reference_clause | use_clause . 246 | 243 interval = ’{’ interval_low interval_op interval_item interval_op interval_high ’}’ . 247 | 244 interval_high = simple_expression . 248 | 245 interval_item = simple_expression . 249 | 246 interval_low = simple_expression . 250 | 247 interval_op = ’<’ | ’<=’ . 251 | 248 inverse_attr = attribute_decl ’:’ [ ( SET | BAG ) [ bound_spec ] OF ] entity_ref FOR [ entity_ref ’.’ ] attribute_ref ’;’ . 252 | 249 inverse_clause = INVERSE inverse_attr { inverse_attr } . 253 | 250 list_type = LIST [ bound_spec ] OF [ UNIQUE ] instantiable_type . 254 | 251 literal = binary_literal | logical_literal | real_literal | string_literal . 255 | 252 local_decl = LOCAL local_variable { local_variable } END_LOCAL ’;’ . 256 | 253 local_variable = variable_id { ’,’ variable_id } ’:’ parameter_type [ ’:=’ expression ] ’;’ . 257 | 254 logical_expression = expression . 258 | 255 logical_literal = FALSE | TRUE | UNKNOWN . 259 | 256 logical_type = LOGICAL . 260 | 257 multiplication_like_op = ’*’ | ’/’ | DIV | MOD | AND | ’||’ . 261 | 258 named_types = entity_ref | type_ref . 262 | 259 named_type_or_rename = named_types [ AS ( entity_id | type_id ) ] . 263 | 260 null_stmt = ’;’ . 264 | 261 number_type = NUMBER . 265 | 262 numeric_expression = simple_expression . 266 | 263 one_of = ONEOF ’(’ supertype_expression { ’,’ supertype_expression } ’)’ . 267 | 264 parameter = expression . 268 | 265 parameter_id = simple_id . 269 | 266 parameter_type = generalized_types | named_types | simple_types . 270 | 267 population = entity_ref . 271 | 268 precision_spec = numeric_expression . 272 | 269 primary = literal | ( qualifiable_factor { qualifier } ) . 273 | 270 procedure_call_stmt = ( built_in_procedure | procedure_ref ) [ actual_parameter_list ] ’;’ . 274 | 271 procedure_decl = procedure_head algorithm_head { stmt } END_PROCEDURE ’;’ . 275 | 272 procedure_head = PROCEDURE procedure_id [ ’(’ [ VAR ] formal_parameter { ’;’ [ VAR ] formal_parameter } ’)’ ] ’;’ . 276 | 273 procedure_id = simple_id . 277 | 274 qualifiable_factor = attribute_ref | constant_factor | function_call | general_ref | population . 278 | 275 qualified_attribute = SELF group_qualifier attribute_qualifier . 279 | 276 qualifier = attribute_qualifier | group_qualifier | index_qualifier . 280 | 277 query_expression = QUERY ’(’ variable_id ’<*’ aggregate_source ’|’ logical_expression ’)’ . 281 | 278 real_type = REAL [ ’(’ precision_spec ’)’ ] . 282 | 279 redeclared_attribute = qualified_attribute [ RENAMED attribute_id ] . 283 | 280 referenced_attribute = attribute_ref | qualified_attribute . 284 | 281 reference_clause = REFERENCE FROM schema_ref [ ’(’ resource_or_rename { ’,’ resource_or_rename } ’)’ ] ’;’ . 285 | 282 rel_op = ’<’ | ’>’ | ’<=’ | ’>=’ | ’<>’ | ’=’ | ’:<>:’ | ’:=:’ . 286 | 283 rel_op_extended = rel_op | IN | LIKE . 287 | 284 rename_id = constant_id | entity_id | function_id | procedure_id | type_id . 288 | 285 repeat_control = [ increment_control ] [ while_control ] [ until_control ] . 289 | 286 repeat_stmt = REPEAT repeat_control ’;’ stmt { stmt } END_REPEAT ’;’ . 290 | 287 repetition = numeric_expression . 291 | 288 resource_or_rename = resource_ref [ AS rename_id ] . 292 | 289 resource_ref = constant_ref | entity_ref | function_ref | procedure_ref | type_ref . 293 | 290 return_stmt = RETURN [ ’(’ expression ’)’ ] ’;’ . 294 | 291 rule_decl = rule_head algorithm_head { stmt } where_clause END_RULE ’;’ . 295 | 292 rule_head = RULE rule_id FOR ’(’ entity_ref { ’,’ entity_ref } ’)’ ’;’ . 296 | 293 rule_id = simple_id . 297 | 294 rule_label_id = simple_id . 298 | 295 schema_body = { interface_specification } [ constant_decl ] { declaration | rule_decl } . 299 | 296 schema_decl = SCHEMA schema_id [ schema_version_id ] ’;’ schema_body END_SCHEMA ’;’ . 300 | 297 schema_id = simple_id . 301 | 298 schema_version_id = string_literal . 302 | 299 selector = expression . 303 | 300 select_extension = BASED_ON type_ref [ WITH select_list ] . 304 | 301 select_list = ’(’ named_types { ’,’ named_types } ’)’ . 305 | 302 select_type = [ EXTENSIBLE [ GENERIC_ENTITY ] ] SELECT [ select_list | select_extension ] . 306 | 303 set_type = SET [ bound_spec ] OF instantiable_type . 307 | 304 sign = ’+’ | ’-’ . 308 | 305 simple_expression = term { add_like_op term } . 309 | 306 simple_factor = aggregate_initializer | entity_constructor | enumeration_reference | interval | query_expression | ( [ unary_op ] ( ’(’ expression ’)’ | primary ) ) . 310 | 307 simple_types = binary_type | boolean_type | integer_type | logical_type | number_type | real_type | string_type . 311 | 308 skip_stmt = SKIP ’;’ . 312 | 309 stmt = alias_stmt | assignment_stmt | case_stmt | compound_stmt | escape_stmt | if_stmt | null_stmt | procedure_call_stmt | repeat_stmt | return_stmt | skip_stmt . 313 | 310 string_literal = simple_string_literal | encoded_string_literal . 314 | 311 string_type = STRING [ width_spec ] . 315 | 312 subsuper = [ supertype_constraint ] [ subtype_declaration ] . 316 | 313 subtype_constraint = OF ’(’ supertype_expression ’)’ . 317 | 314 subtype_constraint_body = [ abstract_supertype ] [ total_over ] [ supertype_expression ’;’ ] . 318 | 315 subtype_constraint_decl = subtype_constraint_head subtype_constraint_body END_SUBTYPE_CONSTRAINT ’;’ . 319 | 316 subtype_constraint_head = SUBTYPE_CONSTRAINT subtype_constraint_id FOR entity_ref ’;’ . 320 | 317 subtype_constraint_id = simple_id . 321 | 318 subtype_declaration = SUBTYPE OF ’(’ entity_ref { ’,’ entity_ref } ’)’ . 322 | 319 supertype_constraint = abstract_entity_declaration | abstract_supertype_declaration | supertype_rule . 323 | 320 supertype_expression = supertype_factor { ANDOR supertype_factor } . 324 | 321 supertype_factor = supertype_term { AND supertype_term } . 325 | 322 supertype_rule = SUPERTYPE subtype_constraint . 326 | 323 supertype_term = entity_ref | one_of | ’(’ supertype_expression ’)’ . 327 | 324 syntax = schema_decl { schema_decl } . 328 | 325 term = factor { multiplication_like_op factor } . 329 | 326 total_over = TOTAL_OVER ’(’ entity_ref { ’,’ entity_ref } ’)’ ’;’ . 330 | 327 type_decl = TYPE type_id ’=’ underlying_type ’;’ [ where_clause ] END_TYPE ’;’ . 331 | 328 type_id = simple_id . 332 | 329 type_label = type_label_id | type_label_ref . 333 | 330 type_label_id = simple_id . 334 | 331 unary_op = ’+’ | ’-’ | NOT . 335 | 332 underlying_type = concrete_types | constructed_types . 336 | 333 unique_clause = UNIQUE unique_rule ’;’ { unique_rule ’;’ } . 337 | 334 unique_rule = [ rule_label_id ’:’ ] referenced_attribute { ’,’ referenced_attribute } . 338 | 335 until_control = UNTIL logical_expression . 339 | 336 use_clause = USE FROM schema_ref [ ’(’ named_type_or_rename { ’,’ named_type_or_rename } ’)’ ] ’;’ . 340 | 337 variable_id = simple_id . 341 | 338 where_clause = WHERE domain_rule ’;’ { domain_rule ’;’ } . 342 | 339 while_control = WHILE logical_expression . 343 | 340 width = numeric_expression . 344 | 341 width_spec = ’(’ width ’)’ [ FIXED ] . 345 | -------------------------------------------------------------------------------- /data/formal/iso-10303-11--2004.ebnf: -------------------------------------------------------------------------------- 1 | (* iso-10303-11:2004 *) 2 | 3 | ABS = ’abs’ ; 4 | ABSTRACT = ’abstract’ ; 5 | ACOS = ’acos’ ; 6 | AGGREGATE = ’aggregate’ ; 7 | ALIAS = ’alias’ ; 8 | AND = ’and’ ; 9 | ANDOR = ’andor’ ; 10 | ARRAY = ’array’ ; 11 | AS = ’as’ ; 12 | ASIN = ’asin’ ; 13 | ATAN = ’atan’ ; 14 | BAG = ’bag’ ; 15 | BASED_ON = ’based_on’ ; 16 | BEGIN = ’begin’ ; 17 | BINARY = ’binary’ ; 18 | BLENGTH = ’blength’ ; 19 | BOOLEAN = ’boolean’ ; 20 | BY = ’by’ ; 21 | CASE = ’case’ ; 22 | CONSTANT = ’constant’ ; 23 | CONST_E = ’const_e’ ; 24 | COS = ’cos’ ; 25 | DERIVE = ’derive’ ; 26 | DIV = ’div’ ; 27 | ELSE = ’else’ ; 28 | END = ’end’ ; 29 | END_ALIAS = ’end_alias’ ; 30 | END_CASE = ’end_case’ ; 31 | END_CONSTANT = ’end_constant’ ; 32 | END_ENTITY = ’end_entity’ ; 33 | END_FUNCTION = ’end_function’ ; 34 | END_IF = ’end_if’ ; 35 | END_LOCAL = ’end_local’ ; 36 | END_PROCEDURE = ’end_procedure’ ; 37 | END_REPEAT = ’end_repeat’ ; 38 | END_RULE = ’end_rule’ ; 39 | END_SCHEMA = ’end_schema’ ; 40 | END_SUBTYPE_CONSTRAINT = ’end_subtype_constraint’ ; 41 | END_TYPE = ’end_type’ ; 42 | ENTITY = ’entity’ ; 43 | ENUMERATION = ’enumeration’ ; 44 | ESCAPE = ’escape’ ; 45 | EXISTS = ’exists’ ; 46 | EXTENSIBLE = ’extensible’ ; 47 | EXP = ’exp’ ; 48 | FALSE = ’false’ ; 49 | FIXED = ’fixed’ ; 50 | FOR = ’for’ ; 51 | FORMAT = ’format’ ; 52 | FROM = ’from’ ; 53 | FUNCTION = ’function’ ; 54 | GENERIC = ’generic’ ; 55 | GENERIC_ENTITY = ’generic_entity’ ; 56 | HIBOUND = ’hibound’ ; 57 | HIINDEX = ’hiindex’ ; 58 | IF = ’if’ ; 59 | IN = ’in’ ; 60 | INSERT = ’insert’ ; 61 | INTEGER = ’integer’ ; 62 | INVERSE = ’inverse’ ; 63 | LENGTH = ’length’ ; 64 | LIKE = ’like’ ; 65 | LIST = ’list’ ; 66 | LOBOUND = ’lobound’ ; 67 | LOCAL = ’local’ ; 68 | LOG = ’log’ ; 69 | LOG10 = ’log10’ ; 70 | LOG2 = ’log2’ ; 71 | LOGICAL = ’logical’ ; 72 | LOINDEX = ’loindex’ ; 73 | MOD = ’mod’ ; 74 | NOT = ’not’ ; 75 | NUMBER = ’number’ ; 76 | NVL = ’nvl’ ; 77 | ODD = ’odd’ ; 78 | OF = ’of’ ; 79 | ONEOF = ’oneof’ ; 80 | OPTIONAL = ’optional’ ; 81 | OR = ’or’ ; 82 | OTHERWISE = ’otherwise’ ; 83 | PI = ’pi’ ; 84 | PROCEDURE = ’procedure’ ; 85 | QUERY = ’query’ ; 86 | REAL = ’real’ ; 87 | REFERENCE = ’reference’ ; 88 | REMOVE = ’remove’ ; 89 | RENAMED = ’renamed’ ; 90 | REPEAT = ’repeat’ ; 91 | RETURN = ’return’ ; 92 | ROLESOF = ’rolesof’ ; 93 | RULE = ’rule’ ; 94 | SCHEMA = ’schema’ ; 95 | SELECT = ’select’ ; 96 | SELF = ’self’ ; 97 | SET = ’set’ ; 98 | SIN = ’sin’ ; 99 | SIZEOF = ’sizeof’ ; 100 | SKIP = ’skip’ ; 101 | SQRT = ’sqrt’ ; 102 | STRING = ’string’ ; 103 | SUBTYPE = ’subtype’ ; 104 | SUBTYPE_CONSTRAINT = ’subtype_constraint’ ; 105 | SUPERTYPE = ’supertype’ ; 106 | TAN = ’tan’ ; 107 | THEN = ’then’ ; 108 | TO = ’to’ ; 109 | TOTAL_OVER = ’total_over’ ; 110 | TRUE = ’true’ ; 111 | TYPE = ’type’ ; 112 | TYPEOF = ’typeof’ ; 113 | UNIQUE = ’unique’ ; 114 | UNKNOWN = ’unknown’ ; 115 | UNTIL = ’until’ ; 116 | USE = ’use’ ; 117 | USEDIN = ’usedin’ ; 118 | VALUE = ’value’ ; 119 | VALUE_IN = ’value_in’ ; 120 | VALUE_UNIQUE = ’value_unique’ ; 121 | VAR = ’var’ ; 122 | WHERE = ’where’ ; 123 | WHILE = ’while’ ; 124 | WITH = ’with’ ; 125 | XOR = ’xor’ ; 126 | bit = ’0’ | ’1’ ; 127 | digit = ’0’ | ’1’ | ’2’ | ’3’ | ’4’ | ’5’ | ’6’ | ’7’ | ’8’ | ’9’ ; 128 | digits = digit { digit } ; 129 | encoded_character = octet octet octet octet ; 130 | hex_digit = digit | ’a’ | ’b’ | ’c’ | ’d’ | ’e’ | ’f’ ; 131 | letter = ’a’ | ’b’ | ’c’ | ’d’ | ’e’ | ’f’ | ’g’ | ’h’ | ’i’ | ’j’ | ’k’ | ’l’ | ’m’ | ’n’ | ’o’ | ’p’ | ’q’ | ’r’ | ’s’ | ’t’ | ’u’ | ’v’ | ’w’ | ’x’ | ’y’ | ’z’ ; 132 | lparen_then_not_lparen_star = ’(’ { ’(’ } not_lparen_star { not_lparen_star } ; 133 | not_lparen_star = not_paren_star | ’)’ ; 134 | not_paren_star = letter | digit | not_paren_star_special ; 135 | not_paren_star_quote_special = ’!’ | ’"’ | ’#’ | ’$’ | ’%’ | ’&’ | ’+’ | ’,’ | ’-’ | ’.’ | ’/’ | ’:’ | ’;’ | ’<’ | ’=’ | ’>’ | ’?’ | ’@’ | ’[’ | ’\’ | ’]’ | ’^’ | ’_’ | ’‘’ | ’{’ | ’|’ | ’}’ | ’~’ ; 136 | not_paren_star_special = not_paren_star_quote_special | ’’’’ ; 137 | not_quote = not_paren_star_quote_special | letter | digit | ’(’ | ’)’ | ’*’ ; 138 | not_rparen_star = not_paren_star | ’(’ ; 139 | octet = hex_digit hex_digit ; 140 | special = not_paren_star_quote_special | ’(’ | ’)’ | ’*’ | ’’’’ ; 141 | not_rparen_star_then_rparen = not_rparen_star { not_rparen_star } ’)’ { ’)’ } ; 142 | binary_literal = ’%’ bit { bit } ; 143 | encoded_string_literal = ’"’ encoded_character { encoded_character } ’"’ ; 144 | integer_literal = digits ; 145 | real_literal = integer_literal | ( digits ’.’ [ digits ] [ ’e’ [ sign ] digits ] ) ; 146 | simple_id = letter { letter | digit | ’_’ } ; 147 | simple_string_literal = \q { ( \q \q ) | not_quote | \s | \x8 | \x9 | \xA | \xB | \xC | \xD } \q ; 148 | embedded_remark = ’(*’ [ remark_tag ] { ( not_paren_star { not_paren_star } ) | lparen_then_not_lparen_star | ( ’*’ { ’*’ } ) | not_rparen_star_then_rparen | embedded_remark } ’*)’ ; 149 | remark = embedded_remark | tail_remark ; 150 | remark_tag = ’"’ remark_ref { ’.’ remark_ref } ’"’ ; 151 | remark_ref = attribute_ref | constant_ref | entity_ref | enumeration_ref | function_ref | parameter_ref | procedure_ref | rule_label_ref | rule_ref | schema_ref | subtype_constraint_ref | type_label_ref | type_ref | variable_ref ; 152 | tail_remark = ’--’ [ remark_tag ] { \a | \s | \x8 | \x9 | \xA | \xB | \xC | \xD } \n ; 153 | attribute_ref = attribute_id ; 154 | constant_ref = constant_id ; 155 | entity_ref = entity_id ; 156 | enumeration_ref = enumeration_id ; 157 | function_ref = function_id ; 158 | parameter_ref = parameter_id ; 159 | procedure_ref = procedure_id ; 160 | rule_label_ref = rule_label_id ; 161 | rule_ref = rule_id ; 162 | schema_ref = schema_id ; 163 | subtype_constraint_ref = subtype_constraint_id ; 164 | type_label_ref = type_label_id ; 165 | type_ref = type_id ; 166 | variable_ref = variable_id ; 167 | abstract_entity_declaration = ABSTRACT ; 168 | abstract_supertype = ABSTRACT SUPERTYPE ’;’ ; 169 | abstract_supertype_declaration = ABSTRACT SUPERTYPE [ subtype_constraint ] ; 170 | actual_parameter_list = ’(’ parameter { ’,’ parameter } ’)’ ; 171 | add_like_op = ’+’ | ’-’ | OR | XOR ; 172 | aggregate_initializer = ’[’ [ element { ’,’ element } ] ’]’ ; 173 | aggregate_source = simple_expression ; 174 | aggregate_type = AGGREGATE [ ’:’ type_label ] OF parameter_type ; 175 | aggregation_types = array_type | bag_type | list_type | set_type ; 176 | algorithm_head = { declaration } [ constant_decl ] [ local_decl ] ; 177 | alias_stmt = ALIAS variable_id FOR general_ref { qualifier } ’;’ stmt { stmt } END_ALIAS ’;’ ; 178 | array_type = ARRAY bound_spec OF [ OPTIONAL ] [ UNIQUE ] instantiable_type ; 179 | assignment_stmt = general_ref { qualifier } ’:=’ expression ’;’ ; 180 | attribute_decl = attribute_id | redeclared_attribute ; 181 | attribute_id = simple_id ; 182 | attribute_qualifier = ’.’ attribute_ref ; 183 | bag_type = BAG [ bound_spec ] OF instantiable_type ; 184 | binary_type = BINARY [ width_spec ] ; 185 | boolean_type = BOOLEAN ; 186 | bound_1 = numeric_expression ; 187 | bound_2 = numeric_expression ; 188 | bound_spec = ’[’ bound_1 ’:’ bound_2 ’]’ ; 189 | built_in_constant = CONST_E | PI | SELF | ’?’ ; 190 | built_in_function = ABS | ACOS | ASIN | ATAN | BLENGTH | COS | EXISTS | EXP | FORMAT | HIBOUND | HIINDEX | LENGTH | LOBOUND | LOINDEX | LOG | LOG2 | LOG10 | NVL | ODD | ROLESOF | SIN | SIZEOF | SQRT | TAN | TYPEOF | USEDIN | VALUE | VALUE_IN | VALUE_UNIQUE ; 191 | built_in_procedure = INSERT | REMOVE ; 192 | case_action = case_label { ’,’ case_label } ’:’ stmt ; 193 | case_label = expression ; 194 | case_stmt = CASE selector OF { case_action } [ OTHERWISE ’:’ stmt ] END_CASE ’;’ ; 195 | compound_stmt = BEGIN stmt { stmt } END ’;’ ; 196 | concrete_types = aggregation_types | simple_types | type_ref ; 197 | constant_body = constant_id ’:’ instantiable_type ’:=’ expression ’;’ ; 198 | constant_decl = CONSTANT constant_body { constant_body } END_CONSTANT ’;’ ; 199 | constant_factor = built_in_constant | constant_ref ; 200 | constant_id = simple_id ; 201 | constructed_types = enumeration_type | select_type ; 202 | declaration = entity_decl | function_decl | procedure_decl | subtype_constraint_decl | type_decl ; 203 | derived_attr = attribute_decl ’:’ parameter_type ’:=’ expression ’;’ ; 204 | derive_clause = DERIVE derived_attr { derived_attr } ; 205 | domain_rule = [ rule_label_id ’:’ ] expression ; 206 | element = expression [ ’:’ repetition ] ; 207 | entity_body = { explicit_attr } [ derive_clause ] [ inverse_clause ] [ unique_clause ] [ where_clause ] ; 208 | entity_constructor = entity_ref ’(’ [ expression { ’,’ expression } ] ’)’ ; 209 | entity_decl = entity_head entity_body END_ENTITY ’;’ ; 210 | entity_head = ENTITY entity_id subsuper ’;’ ; 211 | entity_id = simple_id ; 212 | enumeration_extension = BASED_ON type_ref [ WITH enumeration_items ] ; 213 | enumeration_id = simple_id ; 214 | enumeration_items = ’(’ enumeration_id { ’,’ enumeration_id } ’)’ ; 215 | enumeration_reference = [ type_ref ’.’ ] enumeration_ref ; 216 | enumeration_type = [ EXTENSIBLE ] ENUMERATION [ ( OF enumeration_items ) | enumeration_extension ] ; 217 | escape_stmt = ESCAPE ’;’ ; 218 | explicit_attr = attribute_decl { ’,’ attribute_decl } ’:’ [ OPTIONAL ] parameter_type ’;’ ; 219 | expression = simple_expression [ rel_op_extended simple_expression ] ; 220 | factor = simple_factor [ ’**’ simple_factor ] ; 221 | formal_parameter = parameter_id { ’,’ parameter_id } ’:’ parameter_type ; 222 | function_call = ( built_in_function | function_ref ) [ actual_parameter_list ] ; 223 | function_decl = function_head algorithm_head stmt { stmt } END_FUNCTION ’;’ ; 224 | function_head = FUNCTION function_id [ ’(’ formal_parameter { ’;’ formal_parameter } ’)’ ] ’:’ parameter_type ’;’ ; 225 | function_id = simple_id ; 226 | generalized_types = aggregate_type | general_aggregation_types | generic_entity_type | generic_type ; 227 | general_aggregation_types = general_array_type | general_bag_type | general_list_type | general_set_type ; 228 | general_array_type = ARRAY [ bound_spec ] OF [ OPTIONAL ] [ UNIQUE ] parameter_type ; 229 | general_bag_type = BAG [ bound_spec ] OF parameter_type ; 230 | general_list_type = LIST [ bound_spec ] OF [ UNIQUE ] parameter_type ; 231 | general_ref = parameter_ref | variable_ref ; 232 | general_set_type = SET [ bound_spec ] OF parameter_type ; 233 | generic_entity_type = GENERIC_ENTITY [ ’:’ type_label ] ; 234 | generic_type = GENERIC [ ’:’ type_label ] ; 235 | group_qualifier = ’\’ entity_ref ; 236 | if_stmt = IF logical_expression THEN stmt { stmt } [ ELSE stmt { stmt } ] END_IF ’;’ ; 237 | increment = numeric_expression ; 238 | increment_control = variable_id ’:=’ bound_1 TO bound_2 [ BY increment ] ; 239 | index = numeric_expression ; 240 | index_1 = index ; 241 | index_2 = index ; 242 | index_qualifier = ’[’ index_1 [ ’:’ index_2 ] ’]’ ; 243 | instantiable_type = concrete_types | entity_ref ; 244 | integer_type = INTEGER ; 245 | interface_specification = reference_clause | use_clause ; 246 | interval = ’{’ interval_low interval_op interval_item interval_op interval_high ’}’ ; 247 | interval_high = simple_expression ; 248 | interval_item = simple_expression ; 249 | interval_low = simple_expression ; 250 | interval_op = ’<’ | ’<=’ ; 251 | inverse_attr = attribute_decl ’:’ [ ( SET | BAG ) [ bound_spec ] OF ] entity_ref FOR [ entity_ref ’.’ ] attribute_ref ’;’ ; 252 | inverse_clause = INVERSE inverse_attr { inverse_attr } ; 253 | list_type = LIST [ bound_spec ] OF [ UNIQUE ] instantiable_type ; 254 | literal = binary_literal | logical_literal | real_literal | string_literal ; 255 | local_decl = LOCAL local_variable { local_variable } END_LOCAL ’;’ ; 256 | local_variable = variable_id { ’,’ variable_id } ’:’ parameter_type [ ’:=’ expression ] ’;’ ; 257 | logical_expression = expression ; 258 | logical_literal = FALSE | TRUE | UNKNOWN ; 259 | logical_type = LOGICAL ; 260 | multiplication_like_op = ’*’ | ’/’ | DIV | MOD | AND | ’||’ ; 261 | named_types = entity_ref | type_ref ; 262 | named_type_or_rename = named_types [ AS ( entity_id | type_id ) ] ; 263 | null_stmt = ’;’ ; 264 | number_type = NUMBER ; 265 | numeric_expression = simple_expression ; 266 | one_of = ONEOF ’(’ supertype_expression { ’,’ supertype_expression } ’)’ ; 267 | parameter = expression ; 268 | parameter_id = simple_id ; 269 | parameter_type = generalized_types | named_types | simple_types ; 270 | population = entity_ref ; 271 | precision_spec = numeric_expression ; 272 | primary = literal | ( qualifiable_factor { qualifier } ) ; 273 | procedure_call_stmt = ( built_in_procedure | procedure_ref ) [ actual_parameter_list ] ’;’ ; 274 | procedure_decl = procedure_head algorithm_head { stmt } END_PROCEDURE ’;’ ; 275 | procedure_head = PROCEDURE procedure_id [ ’(’ [ VAR ] formal_parameter { ’;’ [ VAR ] formal_parameter } ’)’ ] ’;’ ; 276 | procedure_id = simple_id ; 277 | qualifiable_factor = attribute_ref | constant_factor | function_call | general_ref | population ; 278 | qualified_attribute = SELF group_qualifier attribute_qualifier ; 279 | qualifier = attribute_qualifier | group_qualifier | index_qualifier ; 280 | query_expression = QUERY ’(’ variable_id ’<*’ aggregate_source ’|’ logical_expression ’)’ ; 281 | real_type = REAL [ ’(’ precision_spec ’)’ ] ; 282 | redeclared_attribute = qualified_attribute [ RENAMED attribute_id ] ; 283 | referenced_attribute = attribute_ref | qualified_attribute ; 284 | reference_clause = REFERENCE FROM schema_ref [ ’(’ resource_or_rename { ’,’ resource_or_rename } ’)’ ] ’;’ ; 285 | rel_op = ’<’ | ’>’ | ’<=’ | ’>=’ | ’<>’ | ’=’ | ’:<>:’ | ’:=:’ ; 286 | rel_op_extended = rel_op | IN | LIKE ; 287 | rename_id = constant_id | entity_id | function_id | procedure_id | type_id ; 288 | repeat_control = [ increment_control ] [ while_control ] [ until_control ] ; 289 | repeat_stmt = REPEAT repeat_control ’;’ stmt { stmt } END_REPEAT ’;’ ; 290 | repetition = numeric_expression ; 291 | resource_or_rename = resource_ref [ AS rename_id ] ; 292 | resource_ref = constant_ref | entity_ref | function_ref | procedure_ref | type_ref ; 293 | return_stmt = RETURN [ ’(’ expression ’)’ ] ’;’ ; 294 | rule_decl = rule_head algorithm_head { stmt } where_clause END_RULE ’;’ ; 295 | rule_head = RULE rule_id FOR ’(’ entity_ref { ’,’ entity_ref } ’)’ ’;’ ; 296 | rule_id = simple_id ; 297 | rule_label_id = simple_id ; 298 | schema_body = { interface_specification } [ constant_decl ] { declaration | rule_decl } ; 299 | schema_decl = SCHEMA schema_id [ schema_version_id ] ’;’ schema_body END_SCHEMA ’;’ ; 300 | schema_id = simple_id ; 301 | schema_version_id = string_literal ; 302 | selector = expression ; 303 | select_extension = BASED_ON type_ref [ WITH select_list ] ; 304 | select_list = ’(’ named_types { ’,’ named_types } ’)’ ; 305 | select_type = [ EXTENSIBLE [ GENERIC_ENTITY ] ] SELECT [ select_list | select_extension ] ; 306 | set_type = SET [ bound_spec ] OF instantiable_type ; 307 | sign = ’+’ | ’-’ ; 308 | simple_expression = term { add_like_op term } ; 309 | simple_factor = aggregate_initializer | entity_constructor | enumeration_reference | interval | query_expression | ( [ unary_op ] ( ’(’ expression ’)’ | primary ) ) ; 310 | simple_types = binary_type | boolean_type | integer_type | logical_type | number_type | real_type | string_type ; 311 | skip_stmt = SKIP ’;’ ; 312 | stmt = alias_stmt | assignment_stmt | case_stmt | compound_stmt | escape_stmt | if_stmt | null_stmt | procedure_call_stmt | repeat_stmt | return_stmt | skip_stmt ; 313 | string_literal = simple_string_literal | encoded_string_literal ; 314 | string_type = STRING [ width_spec ] ; 315 | subsuper = [ supertype_constraint ] [ subtype_declaration ] ; 316 | subtype_constraint = OF ’(’ supertype_expression ’)’ ; 317 | subtype_constraint_body = [ abstract_supertype ] [ total_over ] [ supertype_expression ’;’ ] ; 318 | subtype_constraint_decl = subtype_constraint_head subtype_constraint_body END_SUBTYPE_CONSTRAINT ’;’ ; 319 | subtype_constraint_head = SUBTYPE_CONSTRAINT subtype_constraint_id FOR entity_ref ’;’ ; 320 | subtype_constraint_id = simple_id ; 321 | subtype_declaration = SUBTYPE OF ’(’ entity_ref { ’,’ entity_ref } ’)’ ; 322 | supertype_constraint = abstract_entity_declaration | abstract_supertype_declaration | supertype_rule ; 323 | supertype_expression = supertype_factor { ANDOR supertype_factor } ; 324 | supertype_factor = supertype_term { AND supertype_term } ; 325 | supertype_rule = SUPERTYPE subtype_constraint ; 326 | supertype_term = entity_ref | one_of | ’(’ supertype_expression ’)’ ; 327 | syntax = schema_decl { schema_decl } ; 328 | term = factor { multiplication_like_op factor } ; 329 | total_over = TOTAL_OVER ’(’ entity_ref { ’,’ entity_ref } ’)’ ’;’ ; 330 | type_decl = TYPE type_id ’=’ underlying_type ’;’ [ where_clause ] END_TYPE ’;’ ; 331 | type_id = simple_id ; 332 | type_label = type_label_id | type_label_ref ; 333 | type_label_id = simple_id ; 334 | unary_op = ’+’ | ’-’ | NOT ; 335 | underlying_type = concrete_types | constructed_types ; 336 | unique_clause = UNIQUE unique_rule ’;’ { unique_rule ’;’ } ; 337 | unique_rule = [ rule_label_id ’:’ ] referenced_attribute { ’,’ referenced_attribute } ; 338 | until_control = UNTIL logical_expression ; 339 | use_clause = USE FROM schema_ref [ ’(’ named_type_or_rename { ’,’ named_type_or_rename } ’)’ ] ’;’ ; 340 | variable_id = simple_id ; 341 | where_clause = WHERE domain_rule ’;’ { domain_rule ’;’ } ; 342 | while_control = WHILE logical_expression ; 343 | width = numeric_expression ; 344 | width_spec = ’(’ width ’)’ [ FIXED ] ; 345 | -------------------------------------------------------------------------------- /data/formal/iso-10303-21--2002.bnf: -------------------------------------------------------------------------------- 1 | ; ISO 10303-21:2002 2 | 3 | alphabet = reverse_solidus 'P' upper reverse_solidus . 4 | apostrophe = '''' . 5 | arbitrary = reverse_solidus 'X' reverse_solidus hex_one . 6 | binary = '"' ( '0' | '1' | '2' | '3' ) { hex } '"' . 7 | character = space | digit | lower | upper | special | reverse_solidus | apostrophe . 8 | complex_entity_instance = entity_instance_name '=' subsuper_record ';' . 9 | control_directive = page | alphabet | extended2 | extended4 | arbitrary . 10 | data_section = 'DATA' [ '(' parameter_list ')' ] ';' entity_instance_list 'ENDSEC;' . 11 | digit = '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' . 12 | end_extended = reverse_solidus 'X0' reverse_solidus . 13 | entity_instance = simple_entity_instance | complex_entity_instance . 14 | entity_instance_list = { entity_instance } . 15 | entity_instance_name = '#' digit { digit } . 16 | enumeration = '.' upper { upper | digit } '.' . 17 | exchange_file = 'ISO-10303-21;' header_section data_section { data_section } 'END-ISO-10303-21;' . 18 | extended2 = reverse_solidus 'X2' reverse_solidus hex_two { hex_two } end_extended . 19 | extended4 = reverse_solidus 'X4' reverse_solidus hex_four { hex_four } end_extended . 20 | header_entity = keyword '(' [ parameter_list ] ')' ';' . 21 | header_entity_list = header_entity { header_entity } . 22 | header_section = 'HEADER;' header_entity header_entity header_entity [header_entity_list] 'ENDSEC;' . 23 | hex = '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' | 'A' | 'B' | 'C' | 'D' | 'E' | 'F' . 24 | hex_four = hex_two hex_two . 25 | hex_one = hex hex . 26 | hex_two = hex_one hex_one . 27 | integer = [ sign ] digit { digit } . 28 | keyword = user_defined_keyword | standard_keyword . 29 | list = '(' [ parameter { ',' parameter } ] ')' . 30 | lower = 'a' | 'b' | 'c' | 'd' | 'e' | 'f' | 'g' | 'h' | 'i' | 'j' | 'k' | 'l' | 'm' | 'n' | 'o' | 'p' | 'q' | 'r' | 's' | 't' | 'u' | 'v' | 'w' | 'x' | 'y' | 'z' . 31 | non_q_char = special | digit | space | lower | upper . 32 | omitted_parameter = '*' . 33 | page = reverse_solidus 'S' reverse_solidus character . 34 | parameter = typed_parameter | untyped_parameter | omitted_parameter . 35 | parameter_list = parameter { ',' parameter } . 36 | real = [ sign ] digit { digit } '.' { digit } [ 'E' [ sign ] digit { digit } ] . 37 | reverse_solidus = '\' . 38 | sign = '+' | '-' . 39 | simple_entity_instance = entity_instance_name '=' simple_record ';' . 40 | simple_record = keyword '(' [ parameter_list ] ')' . 41 | simple_record_list = simple_record { simple_record } . 42 | space = ' ' . 43 | special = '!' | '"' | '*' | '$' | '%' | '&' | '.' | '#' | '+' | ',' | '-' | '(' | ')' | '?' | '/' | ':' | ';' | '<' | '=' | '>' | '@' | '[' | ']' | '{' | '|' | '}' | '^' | '`' | '~' . 44 | standard_keyword = upper { upper | digit } . 45 | string = '''' { non_q_char | apostrophe apostrophe | reverse_solidus reverse_solidus | control_directive } '''' . 46 | subsuper_record = '(' simple_record_list ')' . 47 | typed_parameter = keyword '(' parameter ')' . 48 | untyped_parameter = '$' | integer | real | string | entity_instance_name | enumeration | binary | list . 49 | upper = 'A' | 'B' | 'C' | 'D' | 'E' | 'F' | 'G' | 'H' | 'I' | 'J' | 'K' | 'L' | 'M' | 'N' | 'O' | 'P' | 'Q' | 'R' | 'S' | 'T' | 'U' | 'V' | 'W' | 'X' | 'Y' | 'Z' | '_' . 50 | user_defined_keyword = '!' upper { upper | digit } . 51 | -------------------------------------------------------------------------------- /data/formal/iso-10303-21--2016.ebnf: -------------------------------------------------------------------------------- 1 | (* ISO 10303-21:2016 *) 2 | 3 | (* Table 1 — WSN defining subsets of the basic alphabet *) 4 | 5 | SPACE = " " ; 6 | 7 | DIGIT = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" 8 | | "8" | "9" ; 9 | 10 | LOWER = "a" | "b" | "c" | "d" | "e" | "f" | "g" | "h" 11 | | "i" | "j" | "k" | "l" | "m" | "n" | "o" | "p" 12 | | "q" | "r" | "s" | "t" | "u" | "v" | "w" | "x" 13 | | "y" | "z" ; 14 | 15 | UPPER = "A" | "B" | "C" | "D" | "E" | "F" | "G" | "H" 16 | | "I" | "J" | "K" | "L" | "M" | "N" | "O" | "P" 17 | | "Q" | "R" | "S" | "T" | "U" | "V" | "W" | "X" 18 | | "Y" | "Z" | "_" ; 19 | 20 | SPECIAL = "!" | """" | "*" | "$" | "%" | "&" | "." | "#" 21 | | "+" | "," | "-" | "(" | ")" | "?" | "/" | ":" 22 | | ";" | "<" | "=" | ">" | "@" | "[" | "]" | "{" 23 | | "|" | "}" | "^" | "`" | "~" ; 24 | 25 | HEX = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | 26 | "8" | "9" | "A" | "B" | "C" | "D" | "E" | "F" ; 27 | 28 | REVERSE_SOLIDUS = "\" ; 29 | 30 | APOSTROPHE = "'" ; 31 | 32 | LATIN_CODEPOINT = SPACE | DIGIT | LOWER | UPPER | SPECIAL 33 | | REVERSE_SOLIDUS | APOSTROPHE 34 | 35 | 36 | HIGH_CODEPOINT = (U+0080 to U+10FFFF) 37 | 38 | (* PRINT CONTROL DIRECTIVES: 39 | 40 | "\N\" new line 41 | "\F\" form feed 42 | 43 | *) 44 | 45 | (* Table 2 — WSN of token definitions *) 46 | 47 | KEYWORD = USER_DEFINED_KEYWORD | STANDARD_KEYWORD ; 48 | 49 | USER_DEFINED_KEYWORD = "!" UPPER { UPPER | DIGIT } ; 50 | 51 | STANDARD_KEYWORD = UPPER { UPPER | DIGIT } ; 52 | 53 | SIGN = "+" | "-" ; 54 | 55 | INTEGER = [ SIGN ] DIGIT { DIGIT } ; 56 | 57 | REAL = [ SIGN ] DIGIT { DIGIT } "." { DIGIT } 58 | [ "E" [ SIGN ] DIGIT { DIGIT } ] ; 59 | 60 | HEX_ONE = HEX HEX ; 61 | 62 | HEX_TWO = HEX_ONE HEX_ONE ; 63 | 64 | HEX_FOUR = HEX_TWO HEX_TWO ; 65 | 66 | PAGE = REVERSE_SOLIDUS "S" REVERSE_SOLIDUS LATIN_CODEPOINT ; 67 | 68 | ALPHABET = REVERSE_SOLIDUS "P" UPPER REVERSE_SOLIDUS ; 69 | 70 | END_EXTENDED = REVERSE_SOLIDUS "X0" REVERSE_SOLIDUS ; 71 | 72 | EXTENDED2 = REVERSE_SOLIDUS "X2" REVERSE_SOLIDUS HEX_TWO { HEX_TWO } END_EXTENDED ; 73 | 74 | EXTENDED4 = REVERSE_SOLIDUS "X4" REVERSE_SOLIDUS HEX_FOUR { HEX_FOUR } END_EXTENDED ; 75 | 76 | ARBITRARY = REVERSE_SOLIDUS "X" REVERSE_SOLIDUS HEX_ONE ; 77 | 78 | CONTROL_DIRECTIVE = PAGE | ALPHABET | EXTENDED2 | EXTENDED4 | ARBITRARY ; 79 | 80 | STRING = "'" { SPECIAL | DIGIT | SPACE | LOWER | UPPER | 81 | HIGH_CODEPOINT | 82 | APOSTROPHE APOSTROPHE | 83 | REVERSE_SOLIDUS REVERSE_SOLIDUS | 84 | CONTROL_DIRECTIVE } "'" ; 85 | 86 | ENTITY_INSTANCE_NAME = "#" ( DIGIT ) { DIGIT } ; 87 | 88 | VALUE_INSTANCE_NAME = "@" ( DIGIT ) { DIGIT } ; 89 | 90 | CONSTANT_ENTITY_NAME = "#" ( UPPER ) { UPPER | DIGIT } ; 91 | 92 | CONSTANT_VALUE_NAME = "@" ( UPPER ) { UPPER | DIGIT } ; 93 | 94 | LHS_OCCURRENCE_NAME = ( ENTITY_INSTANCE_NAME | VALUE_INSTANCE_NAME ) ; 95 | 96 | RHS_OCCURRENCE_NAME = ( ENTITY_INSTANCE_NAME | VALUE_INSTANCE_NAME | 97 | CONSTANT_ENTITY_NAME | CONSTANT_VALUE_NAME) ; 98 | 99 | ANCHOR_NAME = "<" URI_FRAGMENT_IDENTIFIER ">" ; 100 | 101 | TAG_NAME = ( UPPER | LOWER) { UPPER | LOWER | DIGIT } ; 102 | 103 | RESOURCE = "<" UNIVERSAL_RESOURCE_IDENTIFIER ">" ; 104 | 105 | ENUMERATION = "." UPPER { UPPER | DIGIT } "." ; 106 | 107 | BINARY = """" ( "0" | "1" | "2" | "3" ) { HEX } """" ; 108 | 109 | SIGNATURE_CONTENT = BASE64 ; 110 | 111 | (* Table 3 — WSN of token definitions *) 112 | 113 | EXCHANGE_FILE = "ISO-10303-21;" 114 | HEADER_SECTION [ ANCHOR_SECTION ] 115 | [ REFERENCE_SECTION ] { DATA_SECTION } 116 | "END-ISO-10303-21;" { SIGNATURE_SECTION } ; 117 | 118 | HEADER_SECTION = "HEADER;" 119 | HEADER_ENTITY HEADER_ENTITY HEADER_ENTITY 120 | [HEADER_ENTITY_LIST] 121 | "ENDSEC;" ; 122 | HEADER_ENTITY_LIST = HEADER_ENTITY { HEADER_ENTITY } ; 123 | HEADER_ENTITY = KEYWORD "(" [ PARAMETER_LIST ] ")" ";" ; 124 | 125 | PARAMETER_LIST = PARAMETER { "," PARAMETER } ; 126 | PARAMETER = TYPED_PARAMETER | 127 | UNTYPED_PARAMETER | OMITTED_PARAMETER ; 128 | TYPED_PARAMETER = KEYWORD "(" PARAMETER ")" ; 129 | UNTYPED_PARAMETER = "$" | INTEGER | REAL | STRING | RHS_OCCURENCE_NAME 130 | | ENUMERATION | BINARY | LIST ; 131 | OMITTED_PARAMETER = "*" ; 132 | LIST = "(" [ PARAMETER { "," PARAMETER } ] ")" ; 133 | 134 | ANCHOR_SECTION = "ANCHOR;" ANCHOR_LIST "ENDSEC;" ; 135 | ANCHOR_LIST = { ANCHOR } ; 136 | ANCHOR = ANCHOR_NAME "=" ANCHOR_ITEM { ANCHOR_TAG } ";" ; 137 | ANCHOR_ITEM = "$" | INTEGER | REAL | STRING | ENUMERATION | BINARY 138 | | RHS_OCCURRENCE_NAME | RESOURCE | ANCHOR_ITEM_LIST ; 139 | ANCHOR_ITEM_LIST = "(" [ ANCHOR_ITEM { "," ANCHOR_ITEM } ] ")" ; 140 | ANCHOR_TAG = "{" TAG_NAME ":" ANCHOR_ITEM "}" ; 141 | 142 | REFERENCE_SECTION = "REFERENCE;" REFERENCE_LIST "ENDSEC;" ; 143 | REFERENCE_LIST = { REFERENCE } ; 144 | REFERENCE = LHS_OCCURRENCE_NAME "=" RESOURCE ";" ; 145 | 146 | DATA_SECTION = "DATA" [ "(" PARAMETER_LIST ")" ] ";" 147 | ENTITY_INSTANCE_LIST "ENDSEC;" ; 148 | ENTITY_INSTANCE_LIST = { ENTITY_INSTANCE } ; 149 | ENTITY_INSTANCE = SIMPLE_ENTITY_INSTANCE | COMPLEX_ENTITY_INSTANCE ; 150 | SIMPLE_ENTITY_INSTANCE = ENTITY_INSTANCE_NAME "=" SIMPLE_RECORD ";" ; 151 | COMPLEX_ENTITY_INSTANCE = ENTITY_INSTANCE_NAME "=" SUBSUPER_RECORD ";" ; 152 | SIMPLE_RECORD = KEYWORD "(" [ PARAMETER_LIST ] ")" ; 153 | SUBSUPER_RECORD = "(" SIMPLE_RECORD_LIST ")" ; 154 | SIMPLE_RECORD_LIST = SIMPLE_RECORD { SIMPLE_RECORD } ; 155 | 156 | SIGNATURE_SECTION = "SIGNATURE" SIGNATURE_CONTENT "ENDSEC;" ; 157 | -------------------------------------------------------------------------------- /data/p21examples/README.md: -------------------------------------------------------------------------------- 1 | Example File 2 | ============ 3 | 4 | These example files are not included in the public repository, because they are not explicit free to use 5 | and may be copyrighted. -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Minimal makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line, and also 5 | # from the environment for the first two. 6 | SPHINXOPTS ?= 7 | SPHINXBUILD ?= sphinx-build 8 | SOURCEDIR = source 9 | BUILDDIR = build 10 | 11 | # Put it first so that "make" without argument is like "make help". 12 | help: 13 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 14 | 15 | .PHONY: help Makefile 16 | 17 | # Catch-all target: route all unknown targets to Sphinx using the new 18 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). 19 | %: Makefile 20 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 21 | -------------------------------------------------------------------------------- /docs/make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | pushd %~dp0 4 | 5 | REM Command file for Sphinx documentation 6 | 7 | if "%SPHINXBUILD%" == "" ( 8 | set SPHINXBUILD=sphinx-build 9 | ) 10 | set SOURCEDIR=source 11 | set BUILDDIR=build 12 | 13 | if "%1" == "" goto help 14 | 15 | %SPHINXBUILD% >NUL 2>NUL 16 | if errorlevel 9009 ( 17 | echo. 18 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx 19 | echo.installed, then set the SPHINXBUILD environment variable to point 20 | echo.to the full path of the 'sphinx-build' executable. Alternatively you 21 | echo.may add the Sphinx directory to PATH. 22 | echo. 23 | echo.If you don't have Sphinx installed, grab it from 24 | echo.http://sphinx-doc.org/ 25 | exit /b 1 26 | ) 27 | 28 | %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% 29 | goto end 30 | 31 | :help 32 | %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% 33 | 34 | :end 35 | popd 36 | -------------------------------------------------------------------------------- /docs/source/conf.py: -------------------------------------------------------------------------------- 1 | # Configuration file for the Sphinx documentation builder. 2 | # 3 | # This file only contains a selection of the most common options. For a full 4 | # list see the documentation: 5 | # https://www.sphinx-doc.org/en/master/usage/configuration.html 6 | 7 | # -- Path setup -------------------------------------------------------------- 8 | 9 | # If extensions (or modules to document with autodoc) are in another directory, 10 | # add these directories to sys.path here. If the directory is relative to the 11 | # documentation root, use os.path.abspath to make it absolute, like shown here. 12 | import sys 13 | import os 14 | sys.path.insert(0, os.path.abspath("../../src")) 15 | import steputils 16 | 17 | # -- Project information ----------------------------------------------------- 18 | 19 | 20 | project = 'STEPutils' 21 | copyright = '2019-2022, Manfred Moitzi' 22 | author = steputils.__author__ 23 | 24 | # The short X.Y version. 25 | version = '{}.{}.{}'.format(*steputils.version) 26 | # The full version, including alpha/beta/rc tags 27 | release = steputils.__version__ 28 | 29 | # -- General configuration --------------------------------------------------- 30 | 31 | # Add any Sphinx extension module names here, as strings. They can be 32 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom 33 | # ones. 34 | extensions = [ 35 | 'sphinx.ext.autodoc', 36 | 'sphinx.ext.napoleon', 37 | ] 38 | 39 | autodoc_typehints = 'signature' 40 | 41 | # Add any paths that contain templates here, relative to this directory. 42 | templates_path = ['_templates'] 43 | 44 | # List of patterns, relative to source directory, that match files and 45 | # directories to ignore when looking for source files. 46 | # This pattern also affects html_static_path and html_extra_path. 47 | exclude_patterns = [] 48 | 49 | # -- Options for HTML output ------------------------------------------------- 50 | 51 | # The theme to use for HTML and HTML Help pages. See the documentation for 52 | # a list of builtin themes. 53 | # 54 | html_theme = "sphinx_rtd_theme" 55 | 56 | import sphinx_rtd_theme 57 | html_theme_path = [sphinx_rtd_theme.get_html_theme_path()] 58 | 59 | 60 | # Add any paths that contain custom static files (such as style sheets) here, 61 | # relative to this directory. They are copied after the builtin static files, 62 | # so a file named "default.css" will overwrite the builtin "default.css". 63 | html_static_path = ['_static'] 64 | 65 | master_doc = 'index' 66 | html_show_sourcelink = False 67 | -------------------------------------------------------------------------------- /docs/source/express.rst: -------------------------------------------------------------------------------- 1 | 2 | .. module:: steputils.express 3 | 4 | ================================ 5 | EXPRESS - Data Modeling Language 6 | ================================ 7 | 8 | EXPRESS data modeling language, ISO 10303-11 9 | 10 | List of resources: 11 | 12 | - Wikipedia: https://en.wikipedia.org/wiki/EXPRESS_(data_modeling_language) 13 | - Library of Congress: https://www.loc.gov/preservation/digital/formats/fdd/fdd000449.shtml 14 | - XML and EXPRESS as schema definition languages: http://xml.coverpages.org/rivers-moore-k0603.html -------------------------------------------------------------------------------- /docs/source/index.rst: -------------------------------------------------------------------------------- 1 | .. STEPutils documentation master file, created by 2 | sphinx-quickstart on Sun Dec 29 08:21:12 2019. 3 | You can adapt this file completely to your liking, but it should at least 4 | contain the root `toctree` directive. 5 | 6 | Welcome! This is the documentation for STEPutils release |release|, last updated |today|. 7 | 8 | Quick-Info 9 | ========== 10 | 11 | - Python package to manage a simple document object model (DOM) for STEP model data 12 | - the intended audience are developers 13 | - requires at least Python 3.6 14 | - OS independent 15 | - tested with CPython & PyPy on Windows 10 & Manjaro Linux 16 | - MIT-License 17 | 18 | 19 | .. toctree:: 20 | :maxdepth: 2 21 | :caption: Contents: 22 | 23 | p21 24 | express 25 | 26 | .. toctree:: 27 | :maxdepth: 1 28 | :caption: References: 29 | 30 | step-file-loc 31 | 32 | Indices and tables 33 | ================== 34 | 35 | * :ref:`genindex` 36 | * :ref:`modindex` 37 | * :ref:`search` 38 | -------------------------------------------------------------------------------- /docs/source/p21.rst: -------------------------------------------------------------------------------- 1 | .. method signatures added, because sphinx always use fully qualified type hints, which is not very 2 | pretty nor readable 3 | 4 | .. module:: steputils.p21 5 | 6 | =============== 7 | p21 - STEP-file 8 | =============== 9 | 10 | STEP physical file representation (STEP-file) specified by the ISO 10303-21:2002 standard. 11 | 12 | STEP-File is data exchange form of STEP. ISO 10303 can represent 3D objects in Computer-aided 13 | design (CAD) and related information. Due to its ASCII structure, a STEP-file is easy to read, with typically one 14 | instance per line. The format of a STEP-File is defined in ISO 10303-21 Clear Text Encoding of the Exchange Structure. 15 | 16 | ISO 10303-21:2002 defines the encoding mechanism for representing data conforming to a particular schema in the 17 | EXPRESS data modeling language specified in ISO 10303-11. A STEP-File is also called p21-File and STEP Physical 18 | File. The file extensions .stp and .step indicate that the file contains data conforming to STEP Application 19 | Protocols while the extension .p21 should be used for all other purposes. 20 | 21 | Source: https://en.wikipedia.org/wiki/ISO_10303-21 22 | 23 | A summery of ISO 10303-21 can be viewed online at the `Library of Congress`_. The final draft of `ISO 10303-21;2016`_ 24 | is online available at `STEP Tools, Inc.`_. 25 | 26 | `ISO 10303-21;2016`_ is **not** supported yet! 27 | 28 | The intended usage is to import the :mod:`p21` module and create new objects by factory functions: 29 | 30 | .. code-block:: python 31 | 32 | from steputils import p21 33 | 34 | FNAME = "example.p21" 35 | 36 | # Create a new STEP-file: 37 | stepfile = p21.new_step_file() 38 | 39 | # Create a new data section: 40 | data = stepfile.new_data_section() 41 | 42 | # Add entity instances to data section: 43 | data.add(p21.simple_instance('#1', name='APPLICATION', params=('MyApp', 'v1.0'))) 44 | 45 | # Set required header entities: 46 | stepfile.header.set_file_description(('Example STEP file', 'v1.0')) 47 | stepfile.header.set_file_name(name=FNAME, organization=('me', 'myself'), authorization='me') 48 | # A file schema has to be defined explicit, list all used file schemas. 49 | stepfile.header.set_file_schema(('NONE',)) 50 | 51 | # Write STEP-file to file system: 52 | stepfile.save(FNAME) 53 | 54 | # Read an existing file from file system: 55 | try: 56 | stepfile = p21.readfile(FNAME) 57 | except IOError as e: 58 | print(str(e)) 59 | except p21.ParseError as e: 60 | # Invalid STEP-file or unsupported version 61 | print(str(e)) 62 | else: 63 | print(f'File {FNAME} is a valid STEP-file (ISO 10303-21;2002).') 64 | 65 | Loader Functions 66 | ---------------- 67 | 68 | .. autofunction:: readfile(filename: str) -> StepFile 69 | 70 | .. autofunction:: load(fp: TextIO) -> StepFile 71 | 72 | .. autofunction:: loads(s: str) -> StepFile 73 | 74 | Factory Functions 75 | ----------------- 76 | 77 | .. autofunction:: new_step_file() -> StepFile 78 | 79 | .. autofunction:: simple_instance(ref: str, name: str, params) -> SimpleEntityInstance 80 | 81 | .. autofunction:: simple_entity_instance(ref: str, entity:Entity) -> SimpleEntityInstance 82 | 83 | .. autofunction:: complex_entity_instance(ref: str, entities: List[Entity]) -> ComplexEntityInstance 84 | 85 | .. autofunction:: entity(name: str, params) -> Entity 86 | 87 | .. autofunction:: keyword(name: str) -> Keyword 88 | 89 | .. autofunction:: reference(ref: str) -> Reference 90 | 91 | .. autofunction:: parameter_list(*args) -> ParameterList 92 | 93 | .. autofunction:: unset_parameter(char: str) -> UnsetParameter 94 | 95 | .. autofunction:: typed_parameter(type_name: str, param) -> TypedParameter 96 | 97 | .. autofunction:: enum(enum: str) -> Enumeration 98 | 99 | .. autofunction:: binary(value: int, unset: int = 0) -> Binary 100 | 101 | .. autofunction:: timestamp 102 | 103 | Type Checker Functions 104 | ---------------------- 105 | 106 | .. autofunction:: is_string 107 | 108 | .. autofunction:: is_integer 109 | 110 | .. autofunction:: is_real 111 | 112 | .. autofunction:: is_binary 113 | 114 | .. autofunction:: is_reference 115 | 116 | .. autofunction:: is_keyword 117 | 118 | .. autofunction:: is_enum 119 | 120 | .. autofunction:: is_unset_parameter 121 | 122 | .. autofunction:: is_typed_parameter 123 | 124 | .. autofunction:: is_parameter_list 125 | 126 | Note: It is a single parameter if it's not a :class:`ParameterList` 127 | 128 | .. autofunction:: is_entity 129 | 130 | .. autofunction:: is_simple_entity_instance 131 | 132 | .. autofunction:: is_complex_entity_instance 133 | 134 | 135 | Classes 136 | ------- 137 | 138 | Create new instances by the associated factory function! 139 | 140 | StepFile 141 | ~~~~~~~~ 142 | 143 | .. class:: StepFile 144 | 145 | STEP physical file representation (STEP-file). Create new STEP-files by factory function :func:`new_step_file`. 146 | 147 | A STEP-File has one :class:`HeaderSection`, and at least one :class:`DataSection`. 148 | 149 | .. attribute:: header 150 | 151 | Header section as :class:`HeaderSection` object. 152 | 153 | .. attribute:: data 154 | 155 | List of data sections as :class:`DataSection` objects 156 | 157 | .. automethod:: __getitem__(ref: Reference) -> EntityInstance 158 | 159 | .. automethod:: __delitem__(ref: Reference) -> None 160 | 161 | .. automethod:: __iter__() -> Iterable[EntityInstance] 162 | 163 | .. automethod:: __len__ 164 | 165 | .. automethod:: __str__ 166 | 167 | .. automethod:: get(ref: Reference) -> Optional[EntityInstance] 168 | 169 | .. automethod:: new_data_section(params: Iterable = None) -> DataSection 170 | 171 | .. automethod:: append(data: DataSection) -> None 172 | 173 | .. automethod:: save 174 | 175 | .. automethod:: write 176 | 177 | .. automethod:: has_reference 178 | 179 | 180 | HeaderSection 181 | ~~~~~~~~~~~~~ 182 | 183 | The HEADER section has a fixed structure consisting of 3 to 6 groups in the given order. Except for the data fields 184 | time_stamp and FILE_SCHEMA all fields may contain empty strings. 185 | 186 | :code:`FILE_DESCRIPTION(description: ParameterList, implementation_level: str)` 187 | 188 | - ``description`` 189 | - ``implementation_level``: The version and conformance option of this file. Possible versions are "1" for the 190 | original standard back in 1994, ``'2'`` for the technical corrigendum in 1995 and "3" for the second edition. 191 | The conformance option is either ``'1'`` for internal and ``'2'`` for external mapping of complex entity instances. 192 | Often, one will find here the value ``'2;1'``. The value ``'2;2'`` enforcing external mapping is also possible 193 | but only very rarely used. The values ``'3;1'`` and ``'3;2'`` indicate extended STEP-Files as defined in the 194 | 2001 standard with several DATA sections, multiple schemas and ``FILE_POPULATION`` support. 195 | 196 | 197 | :code:`FILE_NAME(name: str, time_stamp: str, author: str, organization: ParameterList,` 198 | :code:`preprocessor_version: ParameterList, originating_system: str, authorization: str)` 199 | 200 | - ``name`` of this exchange structure. It may correspond to the name of the file in a file system or reflect 201 | data in this file. There is no strict rule how to use this field. 202 | - ``time_stamp`` indicates the time when this file was created. The time is given in the international data time 203 | format ISO 8601, e.g. 2003-12-27T11:57:53 for 27 of December 2003, 2 minutes to noon time. 204 | - ``author`` the name and mailing address of the person creating this exchange structure 205 | - ``organization`` the organization to whom the person belongs to 206 | - ``preprocessor_version`` the name of the system and its version which produces this STEP-file 207 | - ``originating_system`` the name of the system and its version which originally created the information 208 | contained in this STEP-file. 209 | - ``authorization`` the name and mailing address of the person who authorized this file. 210 | 211 | :code:`FILE_SCHEMA(schema: ParameterList)` 212 | 213 | - Specifies one or several Express schema governing the information in the data section(s). For first edition 214 | files, only one EXPRESS schema together with an optional ASN.1 object identifier of the schema version can 215 | be listed here. Second edition files may specify several EXPRESS schema. 216 | - If data sections have names and schema defined, `FILE_SCHEMA` will be created automatically from the data 217 | sections attributes, if data sections do not have `schema` attribute, define a general file schema by 218 | :meth:`HeaderSection.set_file_schema`. 219 | 220 | The last three header groups are only valid in second edition files. 221 | 222 | :code:`FILE_POPULATION(governing_schema: str, determination_method: str, governed_sections: ParameterList)` (?) 223 | 224 | Indicating a valid population (set of entity instances) which conforms 225 | to an EXPRESS schemas. This is done by collecting data from several data_sections and referenced instances from 226 | other data sections. 227 | 228 | - ``governing_schema``, the EXPRESS schema to which the indicated population belongs to and by which it can 229 | be validated. 230 | - ``determination_method`` to figure out which instances belong to the population. Three methods are predefined: 231 | ``'SECTION_BOUNDARY'``, ``'INCLUDE_ALL_COMPATIBLE'``, and ``'INCLUDE_REFERENCED'``. 232 | - ``governed_sections``, the data sections whose entity instances fully belongs to the population. 233 | 234 | The concept of FILE_POPULATION is very close to schema_instance of SDAI. Unfortunately, during the 235 | Standardization process, it was not possible to come to an agreement to merge these concepts. Therefore, 236 | JSDAI adds further attributes to FILE_POPULATION as intelligent comments to cover all missing information 237 | from schema_instance. This is supported for both import and export. 238 | 239 | :code:`SECTION_LANGUAGE(language: str)` (?) 240 | 241 | HeaderSection['SECTION_LANGUAGE'] allows assignment of a default language for either all or a specific 242 | data section. This is needed for those Express schemas that do not provide the capability to specify in which 243 | language string attributes of entities such as name and description are given. 244 | 245 | :code:`SECTION_CONTEXT(context: ParameterList)` (?) 246 | 247 | Provide the capability to specify additional context information for all 248 | or single data sections. This can be used e.g. for STEP-APs to indicate which conformance class is covered by 249 | a particular data section. 250 | 251 | .. class:: HeaderSection 252 | 253 | A :class:`StepFile` has always one header section as attribute :attr:`StepFile.header`. 254 | 255 | .. attribute:: entities 256 | 257 | Ordered dict of all header entities as :class:`Entity` objects. 258 | 259 | .. automethod:: __getitem__(name: str) -> Entity 260 | 261 | .. automethod:: __contains__(name: str) -> bool 262 | 263 | .. automethod:: get(name: str) -> Optional[Entity] 264 | 265 | .. automethod:: add(self, entity: Entity) -> None 266 | 267 | .. automethod:: set_file_description(description: Tuple = None, level: str = '2;1') -> None 268 | 269 | .. automethod:: set_file_name(name: str, time_stamp: str = None, author: str = '', organization: Tuple = None, preprocessor_version: Tuple = None, organization_system: str = '', autorization: str = '') -> None 270 | 271 | .. automethod:: set_file_schema(schema: Iterable) -> None 272 | 273 | 274 | 275 | DataSection 276 | ~~~~~~~~~~~ 277 | 278 | The DATA section contains application data according to one specific express schema. The encoding of this data 279 | follows some simple principles. 280 | 281 | Source of Documentation: https://en.wikipedia.org/wiki/ISO_10303-21 282 | 283 | Every entity instance in the exchange structure is given a unique name in the form ``'#1234'``. 284 | The instance name must consist of a positive number (>0) and is typically smaller than 263. The instance name 285 | is only valid locally within the STEP-file. If the same content is exported again from a system the instance 286 | names may be different for the same instances. The instance name is also used to reference other entity 287 | instances through attribute values or aggregate members. The referenced instance may be defined before or 288 | after the current instance. 289 | 290 | Instances of single entity data types are represented by writing the name of the entity in capital letters and then 291 | followed by the attribute values in the defined order within parentheses. See e.g. ``#16=PRODUCT(...)`` above. 292 | 293 | Instances of complex entity data types are represented in the STEP file by using either the internal mapping or 294 | the external mapping. 295 | External mapping has always to be used if the complex entity instance consist of more than one leaf entity. 296 | In this case all the single entity instance values are given independently from each other in alphabetical 297 | order as defined above with all entity values grouped together in parentheses. 298 | Internal mapping is used by default for conformance option 1 when the complex entity instance consists of 299 | only one leaf entity. The encoding is similar to the one of a single entity instance with the additional order 300 | given by the subtype definition. 301 | 302 | Mapping of attribute values: 303 | 304 | Only explicit attributes get mapped. Inverse, Derived and re-declared attributes are not listed since their 305 | values can be deduced from the other ones. Unset attribute values are given as ``$``. 306 | Explicit attributes which got re-declared as derived in a subtype are encoded as ``*`` in the position of the 307 | supertype attribute. 308 | 309 | Mapping of other data types: 310 | 311 | Enumeration, boolean and logical values are given in capital letters with a leading and trailing dot such as 312 | ``.TRUE.``. 313 | String values are given in ``'``. For characters with a code greater than 126 a special encoding is used. The 314 | character sets as defined in ISO 8859 and 10646 are supported. Note that typical 8 (e.g. west European) or 315 | 16 (Unicode) bit character sets cannot directly be taken for STEP-file strings. They have to be decoded 316 | in a very special way. 317 | Integers and real values are used identical to typical programming languages 318 | Binary values (bit sequences) are encoded as hexadecimal and surrounded by double quotes, with a leading 319 | character indicating the number of unused bits (0, 1, 2, or 3) followed by uppercase hexadecimal encoding of 320 | data. It is important to note that the entire binary value is encoded as a single hexadecimal number, with 321 | the highest order bits in the first hex character and the lowest order bits in the last one. 322 | The elements of aggregates (SET, BAG, LIST, ARRAY) are given in parentheses, separated by ``,``. 323 | Care has to be taken for select data types based on defined data types. Here the name of the defined data 324 | type gets mapped too. 325 | 326 | .. class:: DataSection 327 | 328 | A :class:`StepFile` has a ``list`` of data sections as attribute :attr:`StepFile.data`. A new STEP-file 329 | has no data sections, create at least one by factory function :meth:`StepFile.new_data_section`. 330 | 331 | .. attribute:: parameter 332 | 333 | Each data section can have associated parameters stored in attribute :attr:`parameter`. 334 | 335 | .. attribute:: instances 336 | 337 | Ordered dict of all entity instances of this data section. Key is the name of instance as a string 338 | (e.g. ``'#100'``), values are :class:`EntityInstance` objects. 339 | 340 | .. automethod:: __getitem__(ref: str) -> EntityInstance 341 | 342 | .. automethod:: __iter__ 343 | 344 | .. automethod:: get(ref: str) -> Optional[EntityInstance] 345 | 346 | .. automethod:: __len__() -> int 347 | 348 | .. automethod:: references() -> Iterable[Reference] 349 | 350 | .. automethod:: add(instance: EntityInstance) -> None 351 | 352 | Helper Classes 353 | ~~~~~~~~~~~~~~ 354 | 355 | .. autoclass:: ParameterList 356 | 357 | .. autoclass:: Reference 358 | 359 | .. autoclass:: Keyword 360 | 361 | .. autoclass:: Enumeration 362 | 363 | .. autoclass:: Binary 364 | 365 | .. attribute:: value 366 | 367 | Value as ``int``. 368 | 369 | .. attribute:: unset 370 | 371 | Count of unset bits in the range from 0 to 3. 372 | 373 | .. autoclass:: UnsetParameter 374 | 375 | .. autoclass:: TypedParameter 376 | 377 | .. attribute:: type_name 378 | 379 | Type name as :class:`Keyword` 380 | 381 | .. attribute:: param 382 | 383 | .. autoclass:: Entity 384 | 385 | .. attribute:: name 386 | 387 | Entity name as :class:`Keyword` 388 | 389 | .. attribute:: params 390 | 391 | Entity parameters as :class:`ParameterList`. 392 | 393 | .. autoclass:: SimpleEntityInstance 394 | 395 | .. attribute:: ref 396 | 397 | Instance name as :class:`Reference` 398 | 399 | .. attribute:: entity 400 | 401 | Instance entity as :class:`Entity`. 402 | 403 | .. autoclass:: ComplexEntityInstance 404 | 405 | .. attribute:: ref 406 | 407 | Instance name as :class:`Reference` 408 | 409 | .. attribute:: entities 410 | 411 | Instance entities as list of :class:`Entity` objects. 412 | 413 | .. _Library of Congress: https://www.loc.gov/preservation/digital/formats/fdd/fdd000448.shtml 414 | 415 | .. _ISO 10303-21;2016: http://www.steptools.com/stds/step/IS_final_p21e3.html 416 | 417 | .. _STEP Tools, Inc.: http://www.steptools.com/ -------------------------------------------------------------------------------- /docs/source/step-file-loc.rst: -------------------------------------------------------------------------------- 1 | ===================== 2 | STEP-file description 3 | ===================== 4 | 5 | This is just an excerpt of the important parts, the full document can be viewed online at the `Library of Congress`_. 6 | The final draft of `ISO 10303-21;2016`_ is online available at `STEP Tools, Inc.`_. 7 | 8 | ISO 10303-21 specifies an exchange format, often known as a STEP-file, that allows product data conforming to a schema 9 | in the EXPRESS data modeling language (ISO 10303-11) to be transferred among computer systems. The content of a 10 | STEP-file is termed an "exchange structure." See Notes below for more on the relationship between ISO 10303-21 11 | (STEP-file) and ISO 10303-11 (EXPRESS). This description covers the 1994-6, 2002, and 2016 versions of ISO 10303-21. 12 | 13 | The plain text format for a STEP-file has the following characteristics: 14 | 15 | - it consists of a sequence of records (lines of characters) 16 | - the character set for the exchange structure is defined as the code points U+0020 to U+007E and U+0080 to U+10FFFF of 17 | ISO 10646 (Unicode). The first range includes: digits, upper and lower case "latin" letters, and common special 18 | characters (roughly equivalent to ASCII). The 2016 version of ISO 10303 extended the permitted "alphabet" to include 19 | "high" codepoints U+0080 to U+10FFFF, using UTF-8 encoding. For compatibility with the 2002 version, high codepoint 20 | characters can be encoded/escaped within "control directives" (``/X2/``, ``/X4/``, and ``/X0/``) 21 | - the first characters in the first record are ``"ISO-10303-21;"`` 22 | - in STEP-files conforming to the 2002 version of ISO 10303-21, the last record contains ``"END-ISO-10303-21;"`` 23 | as a terminator. A STEP-file conforming to the 2016 version may have one or more digital signatures following the 24 | "END-ISO-10303-21;" terminator 25 | - text between ``"/*"`` and ``"*/"`` is a comment 26 | - print control directives ``"\N\"`` or ``"\F\"`` can be included to indicate line-breaks or page-breaks respectively 27 | for use when printing the exchange structure. 28 | 29 | The STEP-file is divided into sections. Section labels are reserved terms (termed "special tokens" in the specification) 30 | and sections must be in the order shown below. All sections end with a record ``"ENDSEC;"``. Many STEP-file 31 | instances have only two sections, the mandatory HEADER section and a single DATA section. 32 | 33 | - HEADER; Mandatory, non-repeatable section. 34 | 35 | Must contain one instance of each of the following entities, and they shall appear in that order: 36 | 37 | - file_description 38 | - file_name 39 | - file_schema 40 | 41 | Optional entities include: 42 | 43 | - schema_population 44 | - file_population 45 | - section_language 46 | - section_context 47 | 48 | - ANCHOR; Optional, non-repeatable section. Introduced in 2016 version. The anchor section defines external names for 49 | instances in the exchange structure so that they can be referenced. Anchors can be associated with entities, values, 50 | and other items in the exchange structure. If an anchor name is associated with an item in the REFERENCE section, 51 | the anchor is associated with an item in a different exchange structure. 52 | 53 | - REFERENCE; Optional, non-repeatable section. Introduced in 2016 version. Each entry in the reference section shall 54 | associate an entity instance name (e.g., ``#10``) or value instance name (e.g., ``@70``) with an entity or a value, 55 | which may be in an external file identified by a URI. The declared references can be used in the DATA sections 56 | of the exchange structure. 57 | 58 | - DATA; Optional, repeatable section. The DATA sections contain the core content of the model instance. If an exchange 59 | structure contains more than one DATA section, each ``"DATA"`` keyword shall be followed by a parenthesized list 60 | containing a name for this DATA section and a single schema name that governs the content of this section. 61 | Following these parameters come a sequence of entity instances, with each entity having a unique name in the form of 62 | ``"#"`` followed by a sequence of digits. The entities correspond to Entities and Types in the governing EXPRESS schema. 63 | So-called "user-defined" entities (i.e., entities not derived directly from the EXPRESS schema) are permitted but 64 | discouraged in the 2016 version of ISO 10303-21, which recommends (in clause 11.3) the creation of an additional 65 | EXPRESS schema and a separate DATA section. 66 | 67 | - SIGNATURE; Optional, repeatable section. Introduced in 2016 version. Holds a digital signature to permit verification 68 | that the file content before the SIGNATURE section has not been corrupted and to validate the credentials of the 69 | signer. Follows the ``"END-ISO-10303-21;"`` terminator. 70 | 71 | Also described in ISO 10303-21 is a mechanism for a multi-file exchange structure to be compressed and packaged using 72 | ZIP. The ZIP configuration must correspond to PKZIP 2.04g, which limits compression to the Deflate algorithm. Such a 73 | ZIP file must have a file named ``ISO10303.p21`` which serves as the "root" of the archive and contains at least the 74 | Header of the STEP-file. The special file name ``ISO-10303.p21`` denotes the root of a multi-file exchange structure 75 | whether stored in a ZIP archive or a directory hierarchy. This format description, apart from this paragraph, does not 76 | cover the ZIP-based variant specified in ISO 10303-21. 77 | 78 | .. _Library of Congress: https://www.loc.gov/preservation/digital/formats/fdd/fdd000448.shtml 79 | 80 | .. _ISO 10303-21;2016: http://www.steptools.com/stds/step/IS_final_p21e3.html 81 | 82 | .. _STEP Tools, Inc.: http://www.steptools.com/ -------------------------------------------------------------------------------- /docs/stepcode/README.md: -------------------------------------------------------------------------------- 1 | This code is just for documentation included: 2 | 3 | - `ifc4x2.py` is generated by STEPcode and modified by myself. 4 | - folder `SCL` contains the support files for the autogenerated files also modified by myself. 5 | -------------------------------------------------------------------------------- /docs/stepcode/SCL/BaseType.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2011, Thomas Paviot (tpaviot@gmail.com) 2 | # All rights reserved. 3 | 4 | # This file is part of the StepClassLibrary (SCL). 5 | # 6 | # Redistribution and use in source and binary forms, with or without 7 | # modification, are permitted provided that the following conditions are met: 8 | # 9 | # Redistributions of source code must retain the above copyright notice, 10 | # this list of conditions and the following disclaimer. 11 | # 12 | # Redistributions in binary form must reproduce the above copyright notice, 13 | # this list of conditions and the following disclaimer in the documentation 14 | # and/or other materials provided with the distribution. 15 | # 16 | # Neither the name of the nor the names of its contributors may 17 | # be used to endorse or promote products derived from this software without 18 | # specific prior written permission. 19 | 20 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 | # ARE DISCLAIMED. 24 | # IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY 25 | # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 26 | # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 27 | # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 28 | # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 30 | # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | 32 | 33 | class Type: 34 | """ 35 | A type can be defined from its name and scope 36 | Looking into the scope dict returns the python type class. 37 | This is the base class for aggregated data types or constructed data types 38 | """ 39 | 40 | def __init__(self, typedef, scope): 41 | self._scope = scope 42 | self._typedef = typedef 43 | 44 | def get_scope(self): 45 | return self._scope 46 | 47 | def get_type(self): 48 | if isinstance(self._typedef, str): 49 | if self._scope == None: 50 | raise AssertionError('No scope defined for this type') 51 | elif self._typedef in vars(self._scope): 52 | return vars(self._scope)[self._typedef] 53 | else: 54 | raise TypeError("Type '%s' is not defined in given scope" % self._typedef) 55 | else: 56 | return self._typedef 57 | 58 | 59 | class Aggregate: 60 | """ 61 | This is an abstract class. ARRAY, LIST, SET and BAG inherit from this class 62 | """ 63 | pass 64 | 65 | 66 | if __name__ == "__main__": 67 | import sys 68 | 69 | scp = sys.modules[__name__] 70 | 71 | 72 | class line: 73 | pass 74 | 75 | 76 | new_type = Type('lie', scp) 77 | print(new_type.get_type()) 78 | -------------------------------------------------------------------------------- /docs/stepcode/SCL/ConstructedDataTypes.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2011, Thomas Paviot (tpaviot@gmail.com) 2 | # All rights reserved. 3 | 4 | # This file is part of the StepClassLibrary (SCL). 5 | # 6 | # Redistribution and use in source and binary forms, with or without 7 | # modification, are permitted provided that the following conditions are met: 8 | # 9 | # Redistributions of source code must retain the above copyright notice, 10 | # this list of conditions and the following disclaimer. 11 | # 12 | # Redistributions in binary form must reproduce the above copyright notice, 13 | # this list of conditions and the following disclaimer in the documentation 14 | # and/or other materials provided with the distribution. 15 | # 16 | # Neither the name of the nor the names of its contributors may 17 | # be used to endorse or promote products derived from this software without 18 | # specific prior written permission. 19 | 20 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 | # ARE DISCLAIMED. 24 | # IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY 25 | # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 26 | # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 27 | # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 28 | # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 30 | # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | 32 | import sys 33 | from enum import Enum 34 | from . import BaseType 35 | 36 | 37 | class ENUMERATION(Enum): 38 | """ 39 | EXPRESS definition: 40 | =================== 41 | An ENUMERATION data type has as its domain an ordered set of names. The names represent 42 | values of the enumeration data type. 43 | 44 | Python implementation: 45 | ====================== 46 | An enumeration is initialized from strings defining the types. 47 | For instance, some EXPRESS definition: 48 | TYPE ahead_or_behind = ENUMERATION OF 49 | (ahead, 50 | behind); 51 | END_TYPE; -- ahead_or_behind 52 | 53 | Scoping and visibility of ENUMERATIONS is similar in EXPRESS and Python 54 | 55 | Enum implemented as per Standard Library / PEP 435 56 | >>> ahead_or_behind = ENUMERATION('ahead_or_behind', 'ahead behind') 57 | >>> race_position = ahead_or_behind.ahead 58 | >>> if race_position == ahead_or_behind.ahead: 59 | ... # do stuff! 60 | """ 61 | pass 62 | 63 | 64 | class SELECT: 65 | """ A select data type has as its domain the union of the domains of the named data types in 66 | its select list. The select data type is a generalization of each of the named data types in its 67 | select list. 68 | """ 69 | 70 | def __init__(self, *kargs, **args): 71 | # first defining the scope 72 | if 'scope' in args: 73 | self._scope = args['scope'] 74 | else: 75 | self._scope = None 76 | # create the types from the list of arguments 77 | self._base_types = [] 78 | for types in list(kargs): 79 | new_type = BaseType.Type(types, self._scope) 80 | self._base_types.append(new_type) 81 | 82 | def get_allowed_types(self): 83 | _auth_types = [] 84 | for types in self._base_types: 85 | _auth_types.append(types.get_type()) 86 | return _auth_types 87 | 88 | def get_allowed_basic_types(self): 89 | ''' if a select contains some subselect, goes down through the different 90 | sublayers until there is no more ''' 91 | b = [] 92 | _auth_types = self.get_allowed_types() 93 | for _auth_type in _auth_types: 94 | if isinstance(_auth_type, SELECT) or isinstance(_auth_type, ENUMERATION): 95 | h = _auth_type.get_allowed_types() 96 | b.extend(h) 97 | else: 98 | b = _auth_types 99 | return b 100 | -------------------------------------------------------------------------------- /docs/stepcode/SCL/Model.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2011-2012, Thomas Paviot (tpaviot@gmail.com) 2 | # All rights reserved. 3 | 4 | # This file is part of the StepClassLibrary (SCL). 5 | # 6 | # Redistribution and use in source and binary forms, with or without 7 | # modification, are permitted provided that the following conditions are met: 8 | # 9 | # Redistributions of source code must retain the above copyright notice, 10 | # this list of conditions and the following disclaimer. 11 | # 12 | # Redistributions in binary form must reproduce the above copyright notice, 13 | # this list of conditions and the following disclaimer in the documentation 14 | # and/or other materials provided with the distribution. 15 | # 16 | # Neither the name of the nor the names of its contributors may 17 | # be used to endorse or promote products derived from this software without 18 | # specific prior written permission. 19 | 20 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 | # ARE DISCLAIMED. 24 | # IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY 25 | # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 26 | # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 27 | # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 28 | # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 30 | # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | 32 | 33 | class Model: 34 | """ The container for entity instances 35 | """ 36 | 37 | def __init_(self): 38 | print("Model initialized") 39 | self._instances = [] 40 | 41 | def add_instance(self, entity_instance): 42 | self._instances.append(entity_instance) 43 | 44 | def remove_instance(self, entity_instance): 45 | self._instances.remove(entity_instance) 46 | 47 | def get_instances(self): 48 | return self._instances 49 | 50 | def export_to_p21file(self, filename): 51 | raise AssertionError("Not implemented") 52 | 53 | def export_to_p28file(self, filename): 54 | raise AssertionError("Not implemented") 55 | -------------------------------------------------------------------------------- /docs/stepcode/SCL/Rules.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2011-2012, Thomas Paviot (tpaviot@gmail.com) 2 | # All rights reserved. 3 | 4 | # This file is part of the StepClassLibrary (SCL). 5 | # 6 | # Redistribution and use in source and binary forms, with or without 7 | # modification, are permitted provided that the following conditions are met: 8 | # 9 | # Redistributions of source code must retain the above copyright notice, 10 | # this list of conditions and the following disclaimer. 11 | # 12 | # Redistributions in binary form must reproduce the above copyright notice, 13 | # this list of conditions and the following disclaimer in the documentation 14 | # and/or other materials provided with the distribution. 15 | # 16 | # Neither the name of the nor the names of its contributors may 17 | # be used to endorse or promote products derived from this software without 18 | # specific prior written permission. 19 | 20 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 | # ARE DISCLAIMED. 24 | # IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY 25 | # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 26 | # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 27 | # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 28 | # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 30 | # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | 32 | __doc__ = "This module defines EXPRESS rules" 33 | 34 | 35 | class Rule: 36 | """ 37 | This class describes a RULE 38 | @TODO: to be implemented 39 | """ 40 | pass 41 | -------------------------------------------------------------------------------- /docs/stepcode/SCL/SCLBase.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2011, Thomas Paviot (tpaviot@gmail.com) 2 | # All rights reserved. 3 | 4 | # This file is part of the StepClassLibrary (SCL). 5 | # 6 | # Redistribution and use in source and binary forms, with or without 7 | # modification, are permitted provided that the following conditions are met: 8 | # 9 | # Redistributions of source code must retain the above copyright notice, 10 | # this list of conditions and the following disclaimer. 11 | # 12 | # Redistributions in binary form must reproduce the above copyright notice, 13 | # this list of conditions and the following disclaimer in the documentation 14 | # and/or other materials provided with the distribution. 15 | # 16 | # Neither the name of the nor the names of its contributors may 17 | # be used to endorse or promote products derived from this software without 18 | # specific prior written permission. 19 | 20 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 | # ARE DISCLAIMED. 24 | # IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY 25 | # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 26 | # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 27 | # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 28 | # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 30 | # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | 32 | 33 | class BaseEntityClass: 34 | """ A class that allows advanced __repr__ features for entity instances 35 | """ 36 | 37 | def __repr__(self): 38 | """ Displays attribute with their values 39 | """ 40 | doc_string = "# %s class description:\n%s\n# Instance attributes:\n" % (self.__class__, self.__doc__) 41 | # write each argument with its value 42 | properties = dir(self) 43 | for elem in properties: 44 | if not elem.startswith("_"): 45 | doc_string += "\t%s:%s\n" % (elem, self.__getattribute__(elem)) 46 | return doc_string 47 | -------------------------------------------------------------------------------- /docs/stepcode/SCL/SimpleDataTypes.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2011, Thomas Paviot (tpaviot@gmail.com) 2 | # All rights reserved. 3 | 4 | # This file is part of the StepClassLibrary (SCL). 5 | # 6 | # Redistribution and use in source and binary forms, with or without 7 | # modification, are permitted provided that the following conditions are met: 8 | # 9 | # Redistributions of source code must retain the above copyright notice, 10 | # this list of conditions and the following disclaimer. 11 | # 12 | # Redistributions in binary form must reproduce the above copyright notice, 13 | # this list of conditions and the following disclaimer in the documentation 14 | # and/or other materials provided with the distribution. 15 | # 16 | # Neither the name of the nor the names of its contributors may 17 | # be used to endorse or promote products derived from this software without 18 | # specific prior written permission. 19 | 20 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 | # ARE DISCLAIMED. 24 | # IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY 25 | # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 26 | # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 27 | # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 28 | # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 30 | # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | 32 | """ 33 | Docstrings are courtesy of ISO 10303-11:1994(E) 34 | """ 35 | from __future__ import print_function 36 | 37 | 38 | class Number: 39 | """ 40 | EXPRESS definition: 41 | =================== 42 | The number data type has as its domain all numeric values in the language. The number data 43 | type shall be used when a more specific numeric representation is not important. 44 | Syntax: 45 | 248 number_type = NUMBER . 46 | EXAMPLE 15 - Since we may not know the context of size we do not know how to correctly 47 | represent it, e.g. the size of the crowd at a football game would be an integer, whereas the area 48 | of the pitch would be a real. 49 | size : NUMBER ; 50 | 51 | Python definition: 52 | ================== 53 | class NUMBER is an abstract class, aimed at being specialized. 54 | """ 55 | pass 56 | 57 | 58 | class Real(float, Number): 59 | """ 60 | EXPRESS definition: 61 | =================== 62 | The real data type has as its domain all rational, irrational and scientific real numbers. It is 63 | a specialization of the number data type. 64 | Syntax: 65 | 265 real_type = REAL [ '(' precision_spec ')' ] . 66 | 255 precision_spec = numeric_expression . 67 | Rational and irrational numbers have infnite resolution and are exact. Scientific numbers rep- 68 | resent quantities which are known only to a specified precision. The precision_spec is stated 69 | in terms of significant digits. 70 | A real number literal is represented by a mantissa and optional exponent. The number of digits 71 | making up the mantissa when all leading zeros have been removed is the number of significant 72 | digits. The known precision of a value is the number of leading digits that are necessary to the 73 | application. 74 | Rules and restrictions: 75 | a) The precision_spec gives the minimum number of digits of resolution that are re- 76 | quired. This expression shall evaluate to a positive integer value. 77 | b) When no resolution specification is given the precision of the real number is uncon- 78 | strained. 79 | 80 | Note 9.2.6: 81 | integer and real are both specializations of number; 82 | 83 | Python definition: 84 | ================== 85 | REAL both inherits from float and NUMBER 86 | """ 87 | pass 88 | 89 | 90 | class Integer(int, Number): 91 | """ 92 | EXPRESS definition: 93 | =================== 94 | The integer data type has as its domain all integer numbers. It is a specialization of the real 95 | data type. 96 | Syntax: 97 | 227 integer_type = INTEGER . 98 | EXAMPLE 16 - This example uses an integer data type to represent an attribute named nodes. 99 | The domain of this attribute is all integers, with no further constraint. 100 | ENTITY foo; 101 | nodes : INTEGER; 102 | END_ENTITY; 103 | 104 | Note 9.2.6: integer and real are both specializations of number; 105 | 106 | Python definition: 107 | ================== 108 | INTEGER both inherits from int and NUMBER 109 | 110 | @TODO: note 9.2.6 tells that integer is a specialization of real 111 | """ 112 | pass 113 | 114 | 115 | class String(str): 116 | """ 117 | The string data type has as its domain sequences of characters. The characters which are 118 | permitted to form part of a string value are defined in ISO 10646. 119 | Syntax: 120 | 293 string_type = STRING [ width_spec ] . 121 | 318 width_spec = '(' width ')' [ FIXED ] . 122 | 317 width = numeric_expression . 123 | A string data type may be defined as either fixed or varying width (number of characters). If 124 | it is not specfically defined as fixed width (by using the fixed reserved word in the dfinition) 125 | the string has varying width. 126 | 127 | The domain of a fixed width string data type is the set of all character sequences of exactly 128 | the width specified in the type definition. 129 | The domain of a varying width string data type is the set of all character sequences of width 130 | less than or equal to the maximum width specified in the type definition. 131 | If no width is specified, the domain is the set of all character sequences, with no constraint on 132 | the width of these sequences. 133 | Substrings and individual characters may be addressed using subscripts as described in 12.5. 134 | The case (upper or lower) of letters within a string is significant. 135 | 136 | Python mapping: INTEGER is mapped the 'str' type. An additional width_spec parameter can be passed 137 | to handle the FIXED length constraint 138 | """ 139 | pass 140 | 141 | 142 | class Logical: 143 | """ 144 | The logical data type has as its domain the three literals true, false and unknown. 145 | Syntax: 146 | 243 logical_type = LOGICAL . 147 | The following ordering holds for the values of the logical data type: false < unknown < 148 | true. The logical data type is compatible with the boolean data type, except that the value 149 | unknown cannot be assigned to a boolean variable. 150 | """ 151 | pass 152 | 153 | 154 | Unknown = Logical() 155 | 156 | # 157 | # The boolean data type has as its domain the two literals true and false. The boolean data 158 | # type is a specialization of the logical data type. 159 | # 160 | # Python mapping: BOOLEAN is mapped to 'bool' type 161 | # 162 | # The bool data type can't however be subclassed in Python (see 163 | # See http://mail.python.org/pipermail/python-dev/2002-March/020822.html) 164 | # so it is just set to bool 165 | Boolean = bool 166 | 167 | 168 | class Binary: 169 | """ 170 | The binary data type has as its domain sequences of bits, each bit being represented by 0 or 1. 171 | Syntax: 172 | 172 binary_type = BINARY [ width_spec ] . 173 | 318 width_spec = '(' width ')' [ FIXED ] . 174 | 317 width = numeric_expression . 175 | A binary data type may be defined as either fixed or varying width (number of bits). If it is 176 | not specifically defined as fixed width (by using the fixed reserved word in the definition) the 177 | binary data type has varying width. 178 | The domain of a fixed width binary data type is the set of all bit sequences of exactly the width 179 | specified in the type definition. 180 | The domain of a varying width binary data type is the set of all bit sequences of width less 181 | than or equal to the maximum width specified in the type definition. If no width is specified, 182 | the domain is the set of all bit sequences, with no constraint on the width of these sequences. 183 | Subbinaries and individual bits may be addressed using subscripts as described in 12.3. 184 | 185 | Python mapping: BINARY is mapped to the 'str' type. A check is performed to validate it is a binary 186 | string representing a number. 187 | """ 188 | 189 | def __init__(self, value, width=None, fixed=False): 190 | try: 191 | self._value = int(value, 2) 192 | except ValueError: 193 | raise ValueError("%s is not a binary" % value) 194 | 195 | self._specified_width = width 196 | self._fixed = fixed 197 | # Check implicit width 198 | if (width is not None) and not fixed: 199 | raise ValueError( 200 | "The 'width' parameter is passed but 'fixed' is still false. Please explicitly set 'fixed' to True to avoid implicit declaration") 201 | # First check the string length if 'fixed' is set to True 202 | if fixed: 203 | if len(value) != width: 204 | raise ValueError("The BINARY width {}} is not consistent with the 'width' declaration({}})" .format(len(value), width)) 205 | 206 | def __str__(self): 207 | return bin(self._value)[2:] 208 | 209 | def __repr__(self): 210 | return f"{self.__class__.__name__}('{self.__str__()}')" 211 | 212 | 213 | if __name__ == "__main__": 214 | print("Creating REAL from float value") 215 | a = Real(1.5) 216 | print(a * 2) 217 | print("Creating REAL from string value") 218 | a = Real("1.2") 219 | print(a * 3) 220 | print("Creating INTEGER from int value") 221 | b = Integer(2) 222 | c = Integer(3) 223 | print(b + c) 224 | print("Creating INTEGER from string value") 225 | e = Integer("5") 226 | f = Integer("8") 227 | print(e * f) 228 | print(repr(Binary('1001'))) 229 | -------------------------------------------------------------------------------- /docs/stepcode/SCL/TypeChecker.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2011, Thomas Paviot (tpaviot@gmail.com) 2 | # All rights reserved. 3 | 4 | # This file is part of the StepClassLibrary (SCL). 5 | # 6 | # Redistribution and use in source and binary forms, with or without 7 | # modification, are permitted provided that the following conditions are met: 8 | # 9 | # Redistributions of source code must retain the above copyright notice, 10 | # this list of conditions and the following disclaimer. 11 | # 12 | # Redistributions in binary form must reproduce the above copyright notice, 13 | # this list of conditions and the following disclaimer in the documentation 14 | # and/or other materials provided with the distribution. 15 | # 16 | # Neither the name of the nor the names of its contributors may 17 | # be used to endorse or promote products derived from this software without 18 | # specific prior written permission. 19 | 20 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 | # ARE DISCLAIMED. 24 | # IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY 25 | # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 26 | # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 27 | # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 28 | # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 30 | # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | 32 | from .ConstructedDataTypes import ENUMERATION, SELECT 33 | from . import BaseType 34 | 35 | RAISE_EXCEPTION_IF_TYPE_DOES_NOT_MATCH = True 36 | DEBUG = False 37 | 38 | 39 | def cast_python_object_to_aggregate(obj, aggregate): 40 | """ This function casts a python object to an aggregate type. For instance: 41 | [1.,2.,3.]-> ARRAY(1,3,REAL)""" 42 | aggregate_lower_bound = aggregate.bound_1() 43 | aggregate_upper_bound = aggregate.bound_2() 44 | if isinstance(obj, list): 45 | for idx in range(aggregate_lower_bound, aggregate_upper_bound + 1): 46 | aggregate[idx] = obj[idx - aggregate_lower_bound] 47 | return aggregate 48 | 49 | 50 | def check_type(instance, expected_type): 51 | """ This function checks whether an object is an instance of a given class 52 | returns False or True 53 | """ 54 | type_match = False # by default, will be set to True if any match 55 | if DEBUG: 56 | print("===") 57 | print("Instance passed: ", instance) 58 | print("Expected type: ", expected_type) 59 | # in the case of an enumeration, we have to check if the instance is in the list 60 | if (isinstance(expected_type, ENUMERATION)): 61 | allowed_ids = expected_type.get_enum_ids() 62 | if instance in allowed_ids: 63 | type_match = True 64 | else: 65 | raise TypeError('Enumeration ids must be %s ( passed %s)' % (allowed_ids, type(instance))) 66 | elif (isinstance(expected_type, SELECT)): 67 | # we check if the instance is of the type of any of the types that are in the SELECT 68 | allowed_types = expected_type.get_allowed_basic_types() 69 | for allowed_type in allowed_types: 70 | if isinstance(instance, allowed_type): 71 | type_match = True 72 | if not type_match: 73 | if RAISE_EXCEPTION_IF_TYPE_DOES_NOT_MATCH: 74 | raise TypeError('Argument type must be %s (you passed %s)' % (allowed_types, type(instance))) 75 | else: 76 | print("WARNING: expected '%s' but passed a '%s', casting from python value to EXPRESS type" % ( 77 | allowed_types, type(instance))) 78 | return False 79 | elif (isinstance(expected_type, BaseType.Aggregate)): 80 | # first check that they are instance of the same class 81 | if not (isinstance(instance, type(expected_type))): 82 | raise TypeError('Expected %s but passed %s' % (type(expected_type), type(instance))) 83 | # then check that the base type is the same 84 | elif not (instance.get_type() == expected_type.get_type()): 85 | # print instance.get_type() 86 | # print expected_type.get_type() 87 | raise TypeError('Expected %s:%s base type but passed %s:%s base type' % ( 88 | type(expected_type), expected_type.get_type(), type(instance), instance.get_type())) 89 | # check optional and unique attributes 90 | # elif not (instance._unique == expected_type._unique): 91 | # raise TypeError('Aggregate expects UNIQUE:%s property but passed UNIQUE:%s'%(expected_type._unique, instance._unique)) 92 | # elif not (instance._optional == expected_type._optional): 93 | # raise TypeError('Aggregate expects OPTIONAL:%s property but passed OPTIONAL:%s'%(expected_type._optional, instance._optional)) 94 | # @TODO: check aggregate bounds 95 | else: 96 | type_match = True 97 | else: # simple data types 98 | type_match = isinstance(instance, expected_type) 99 | if not type_match: 100 | if RAISE_EXCEPTION_IF_TYPE_DOES_NOT_MATCH: 101 | raise TypeError('Argument type must be %s (you passed %s)' % (expected_type, type(instance))) 102 | else: 103 | print("WARNING: expected '%s' but passed a '%s', casting from python value to EXPRESS type" % ( 104 | expected_type, type(instance))) 105 | return False 106 | return True 107 | -------------------------------------------------------------------------------- /docs/stepcode/SCL/Utils.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2011, Thomas Paviot (tpaviot@gmail.com) 2 | # All rights reserved. 3 | 4 | # This file is part of the StepClassLibrary (SCL). 5 | # 6 | # Redistribution and use in source and binary forms, with or without 7 | # modification, are permitted provided that the following conditions are met: 8 | # 9 | # Redistributions of source code must retain the above copyright notice, 10 | # this list of conditions and the following disclaimer. 11 | # 12 | # Redistributions in binary form must reproduce the above copyright notice, 13 | # this list of conditions and the following disclaimer in the documentation 14 | # and/or other materials provided with the distribution. 15 | # 16 | # Neither the name of the nor the names of its contributors may 17 | # be used to endorse or promote products derived from this software without 18 | # specific prior written permission. 19 | 20 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 | # ARE DISCLAIMED. 24 | # IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY 25 | # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 26 | # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 27 | # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 28 | # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 30 | # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | """ This module provide string utils""" 32 | 33 | 34 | def process_nested_parent_str(attr_str, idx=0): 35 | """ 36 | The first letter should be a parenthesis 37 | input string: "(1,4,(5,6),7)" 38 | output: ['1','4',['5','6'],'7'] 39 | """ 40 | params = [] 41 | current_param = '' 42 | k = 0 43 | while k < len(attr_str): 44 | ch = attr_str[k] 45 | k += 1 46 | if ch == ',': 47 | params.append(current_param) 48 | current_param = '' 49 | elif ch == '(': 50 | nv = attr_str[k:] 51 | current_param, progress = process_nested_parent_str(nv) 52 | params.append(current_param) 53 | current_param = '' 54 | k += progress + 1 55 | elif ch == ')': 56 | params.append(current_param) 57 | return params, k 58 | else: 59 | current_param += ch 60 | params.append(current_param) 61 | return params, k 62 | 63 | 64 | if __name__ == "__main__": 65 | print(process_nested_parent_str("'A'")[0]) 66 | print(process_nested_parent_str("30.0,0.0,5.0")[0]) 67 | print(process_nested_parent_str("1,2,(3,4,5),6,7,8")[0]) 68 | print(process_nested_parent_str("(#9149,#9166),#9142,.T.")[0]) 69 | -------------------------------------------------------------------------------- /docs/stepcode/SCL/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mozman/steputils/547860b349a36cf24c564d6c87ffd8f60484f6fb/docs/stepcode/SCL/__init__.py -------------------------------------------------------------------------------- /docs/stepcode/SCL/essa_par.py: -------------------------------------------------------------------------------- 1 | def process_nested_parent_str(attr_str): 2 | """ 3 | The first letter should be a parenthesis 4 | input string: "(1,4,(5,6),7)" 5 | output: tuple (1,4,(4,6),7) 6 | """ 7 | params = [] 8 | agg_scope_level = 0 9 | current_param = '' 10 | for i, ch in enumerate(attr_str): 11 | if ch == ',': 12 | params.append(current_param) 13 | current_param = '' 14 | elif ch == '(': 15 | agg_scope_level += 1 16 | elif ch == ')': 17 | agg_scope_level = 0 18 | elif agg_scope_level == 0: 19 | current_param += ch 20 | return params 21 | 22 | 23 | def process_nested_parent_str2(attr_str, idx=0): 24 | """ 25 | The first letter should be a parenthesis 26 | input string: "(1,4,(5,6),7)" 27 | output: ['1','4',['5','6'],'7'] 28 | """ 29 | # print 'Entering function with string %s'%(attr_str) 30 | params = [] 31 | current_param = '' 32 | k = 0 33 | while (k < len(attr_str)): 34 | # print 'k in this function:%i'%k 35 | ch = attr_str[k] 36 | k += 1 37 | if ch == ',': 38 | # print "Add param:",current_param 39 | params.append(current_param) 40 | current_param = '' 41 | elif ch == '(': 42 | nv = attr_str[k:] 43 | # print "Up one level parenthesis:%s"%(nv) 44 | current_param, progress = process_nested_parent_str2(nv) 45 | # print "Adding the list returned from nested",current_param 46 | params.append(current_param) 47 | current_param = '' 48 | k += progress + 1 49 | elif ch == ')': 50 | # print "Down one level parenthesis: %i characters parsed"%k 51 | params.append(current_param) 52 | # print "Current params:",params#k -= acc-2 53 | return params, k 54 | else: 55 | current_param += ch 56 | # print "Ch:",ch 57 | # print "k:",k 58 | 59 | # raw_input("") 60 | # idx += 1 61 | 62 | params.append(current_param) 63 | return params, k 64 | 65 | 66 | # print process_nested_parent_str2('1,2,3,4,5,6') 67 | # idx=0 68 | # print process_nested_parent_str2("'A','B','C'") 69 | print(process_nested_parent_str2("'A'")[0]) 70 | print(process_nested_parent_str2("30.0,0.0,5.0")[0]) 71 | print(process_nested_parent_str2("(Thomas)")[0]) 72 | print(process_nested_parent_str2("Thomas, Paviot, ouais")[0]) 73 | print(process_nested_parent_str2("1,2,(3,4,5),6,7,8")[0]) 74 | print(process_nested_parent_str2("(#9149,#9166),#9142,.T.")[0]) 75 | -------------------------------------------------------------------------------- /examples/create_simple_p21_file.py: -------------------------------------------------------------------------------- 1 | from steputils import p21 2 | 3 | OUTDIR = "C:\\Users\\mozman\\Desktop\\Outbox\\" 4 | FNAME = "example.p21" 5 | 6 | # Create a new STEP-file: 7 | stepfile = p21.new_step_file() 8 | 9 | # Create new data section: 10 | data = stepfile.new_data_section() 11 | 12 | # Add entity instances to data section: 13 | data.add(p21.simple_instance("#1", name="APPLICATION", params=("MyApp", "v1.0"))) 14 | 15 | # Set required header entities: 16 | stepfile.header.set_file_description(("Example STEP file", "v1.0")) 17 | stepfile.header.set_file_name( 18 | name=FNAME, organization=("me", "myself"), authorization="me" 19 | ) 20 | stepfile.header.set_file_schema(("NONE",)) 21 | 22 | # Write STEP-file to file system: 23 | stepfile.save(OUTDIR + FNAME) 24 | 25 | # Read an existing file from file system: 26 | try: 27 | stepfile = p21.readfile(OUTDIR + FNAME) 28 | except IOError as e: 29 | print(str(e)) 30 | except p21.ParseError as e: 31 | # Invalid STEP-file 32 | print(str(e)) 33 | else: 34 | print(f"File {FNAME} is a valid STEP-file") 35 | -------------------------------------------------------------------------------- /profiling/express_antlr4.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2020 Manfred Moitzi 2 | # License: MIT License 3 | 4 | from pathlib import Path 5 | import time 6 | from steputils.express import Parser 7 | 8 | DATAPATH = Path(r'..\data\schema').resolve() 9 | 10 | 11 | class LoadResult: 12 | def __init__(self, filename: Path, content, loading_time: float, parsing_time: float = 0): 13 | self.filename = filename 14 | self.content = content 15 | self.loading_time = loading_time 16 | self.parsing_time = parsing_time 17 | 18 | def print(self): 19 | if self.content is None: 20 | print(f"{self.filename.name} is not an EXPRESS schema file.") 21 | else: 22 | size = self.filename.stat().st_size / 1024. 23 | overall = self.loading_time + self.parsing_time 24 | print( 25 | f"File: {self.filename.name}; " 26 | f" Size: {size:.2f} kB; " 27 | f"Parsing: {self.parsing_time:.2f}s; " 28 | ) 29 | 30 | 31 | def scan_express_schemas(p: Path): 32 | print(f"\nEntering Folder <{p.stem}>") 33 | start_time = time.perf_counter() 34 | for file in p.iterdir(): 35 | if file.is_dir(): 36 | scan_express_schemas(file) 37 | else: 38 | result = load_schema(file) 39 | result.print() 40 | run_time = time.perf_counter() - start_time 41 | print('-' * 79) 42 | print(f"Folder <{p.stem}> Runtime: {run_time:.2f} sec.") 43 | 44 | 45 | def load_schema(fn: Path) -> LoadResult: 46 | if fn.suffix.lower() != '.exp': 47 | return LoadResult(fn, None, 0) 48 | content = None 49 | start_time = time.perf_counter() 50 | loaded = time.perf_counter() 51 | try: 52 | data = open(str(fn), mode='rt').read() 53 | except IOError as e: 54 | print(e) 55 | else: 56 | loaded = time.perf_counter() 57 | print(f'parsing {fn.stem} ...') 58 | parser = Parser(data) 59 | content = parser.schema() 60 | end_time = time.perf_counter() 61 | loading_time = loaded - start_time 62 | parsing_time = end_time - loaded 63 | return LoadResult(fn, content, loading_time, parsing_time) 64 | 65 | 66 | if __name__ == '__main__': 67 | start_time = time.perf_counter() 68 | scan_express_schemas(DATAPATH) 69 | run_time = time.perf_counter() - start_time 70 | print(f"Overall Runtime: {run_time:.2f} sec.") 71 | -------------------------------------------------------------------------------- /profiling/express_pyparser.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2020 Manfred Moitzi 2 | # License: MIT License 3 | 4 | from pathlib import Path 5 | import time 6 | from steputils.express.pyparser import syntax 7 | from pyparsing import ParseException 8 | 9 | DATAPATH = Path(r'..\data\schema').resolve() 10 | 11 | 12 | class LoadResult: 13 | def __init__(self, filename: Path, content, loading_time: float, parsing_time: float = 0): 14 | self.filename = filename 15 | self.content = content 16 | self.loading_time = loading_time 17 | self.parsing_time = parsing_time 18 | 19 | def print(self): 20 | if self.content is None: 21 | print(f"{self.filename.name} is not an EXPRESS schema file.") 22 | else: 23 | print(f"File: {self.filename.name}") 24 | if isinstance(self.content, ParseException): 25 | print(' ParseException: ' + str(self.content)) 26 | size = self.filename.stat().st_size / 1024. 27 | overall = self.loading_time + self.parsing_time 28 | print( 29 | f" Size: {size:.2f} kB; " 30 | f"Parsing: {self.parsing_time:.2f}s; " 31 | ) 32 | 33 | 34 | def scan_express_schemas(p: Path): 35 | print(f"\nEntering Folder <{p.stem}>") 36 | start_time = time.perf_counter() 37 | for file in p.iterdir(): 38 | if file.is_dir(): 39 | scan_express_schemas(file) 40 | else: 41 | result = load_schema(file) 42 | result.print() 43 | run_time = time.perf_counter() - start_time 44 | print('-' * 79) 45 | print(f"Folder <{p.stem}> Runtime: {run_time:.2f} sec.") 46 | 47 | 48 | def load_schema(fn: Path) -> LoadResult: 49 | if fn.suffix.lower() != '.exp': 50 | return LoadResult(fn, None, 0) 51 | content = None 52 | start_time = time.perf_counter() 53 | loaded = time.perf_counter() 54 | try: 55 | data = open(str(fn), mode='rt').read() 56 | except IOError as e: 57 | print(e) 58 | else: 59 | loaded = time.perf_counter() 60 | try: 61 | content = syntax.parseString(data) 62 | except ParseException as e: 63 | content = e 64 | 65 | end_time = time.perf_counter() 66 | loading_time = loaded - start_time 67 | parsing_time = end_time - loaded 68 | return LoadResult(fn, content, loading_time, parsing_time) 69 | 70 | 71 | if __name__ == '__main__': 72 | start_time = time.perf_counter() 73 | scan_express_schemas(DATAPATH) 74 | run_time = time.perf_counter() - start_time 75 | print(f"Overall Runtime: {run_time:.2f} sec.") 76 | -------------------------------------------------------------------------------- /profiling/p21_loader.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2020 Manfred Moitzi 2 | # License: MIT License 3 | 4 | from pathlib import Path 5 | import time 6 | from steputils import p21 7 | 8 | # p21 example files not included in public repository, because legal status of files is not clear 9 | DIR = Path(r'..\data\p21examples').resolve() 10 | 11 | 12 | class LoadResult: 13 | def __init__(self, filename: Path, content, loading_time: float, parsing_time: float = 0): 14 | self.filename = filename 15 | self.content = content 16 | self.loading_time = loading_time 17 | self.parsing_time = parsing_time 18 | 19 | def print(self): 20 | if self.content is None: 21 | print(f"{self.filename.name} is not a STEP-file.") 22 | else: 23 | size = self.filename.stat().st_size / 1024. 24 | overall = self.loading_time + self.parsing_time 25 | print( 26 | f"File: {self.filename.name}; " 27 | f" Size: {size:.2f} kB; " 28 | f"IO: {self.loading_time:.2f}s; " 29 | f"Parsing: {self.parsing_time:.2f}s; " 30 | f"Sum: {overall:.2f}s; " 31 | ) 32 | 33 | 34 | def scan_p21_files(p: Path): 35 | print(f"\nEntering Folder <{p.stem}>") 36 | start_time = time.perf_counter() 37 | for file in p.iterdir(): 38 | if file.is_dir(): 39 | scan_p21_files(file) 40 | else: 41 | result = load_p21(file) 42 | result.print() 43 | run_time = time.perf_counter() - start_time 44 | print('-' * 79) 45 | print(f"Folder <{p.stem}> Runtime: {run_time:.2f} sec.") 46 | 47 | 48 | def load_p21(fn: Path) -> LoadResult: 49 | if fn.suffix.lower() not in ('.p21', '.ifc', '.stp'): 50 | return LoadResult(fn, None, 0) 51 | content = None 52 | start_time = time.perf_counter() 53 | loaded = time.perf_counter() 54 | try: 55 | data = open(str(fn), mode='rt', encoding=p21.STEP_FILE_ENCODING).read() 56 | loaded = time.perf_counter() 57 | content = p21.loads(data) 58 | except IOError as e: 59 | print(e) 60 | except p21.ParseError as e: 61 | print(e) 62 | end_time = time.perf_counter() 63 | loading_time = loaded - start_time 64 | parsing_time = end_time - loaded 65 | return LoadResult(fn, content, loading_time, parsing_time) 66 | 67 | 68 | if __name__ == '__main__': 69 | start_time = time.perf_counter() 70 | scan_p21_files(DIR) 71 | run_time = time.perf_counter() - start_time 72 | print(f"Overall Runtime: {run_time:.2f} sec.") 73 | -------------------------------------------------------------------------------- /profiling/result-express-antlr4-cpython.txt: -------------------------------------------------------------------------------- 1 | 2 | Entering Folder 3 | parsing ap203 ... 4 | File: ap203.exp; Size: 208.84 kB; IO: 0.00s; Parsing: 14.08s; Sum: 14.08s; 5 | parsing IFC4x2 ... 6 | File: IFC4x2.exp; Size: 377.34 kB; IO: 0.00s; Parsing: 19.53s; Sum: 19.53s; 7 | IFC4x2.xsd is not an EXPRESS schema file. 8 | ------------------------------------------------------------------------------- 9 | Folder Runtime: 33.61 sec. 10 | Overall Runtime: 33.61 sec. 11 | -------------------------------------------------------------------------------- /profiling/result-express-antlr4-pypy3.txt: -------------------------------------------------------------------------------- 1 | Entering Folder 2 | parsing ap203 ... 3 | File: ap203.exp; Size: 208.84 kB; IO: 0.03s; Parsing: 9.74s; Sum: 9.77s; 4 | parsing IFC4x2 ... 5 | File: IFC4x2.exp; Size: 377.34 kB; IO: 0.03s; Parsing: 7.77s; Sum: 7.80s; 6 | IFC4x2.xsd is not an EXPRESS schema file. 7 | ------------------------------------------------------------------------------- 8 | Folder Runtime: 17.57 sec. 9 | Overall Runtime: 17.57 sec. 10 | -------------------------------------------------------------------------------- /profiling/result-express-pyparser-cpython.txt: -------------------------------------------------------------------------------- 1 | 2 | Entering Folder 3 | File: ap203.exp 4 | ParseException: Expected "END_SCHEMA", found '-' (at char 12027), (line:501, col:15) 5 | Size: 208.84 kB; Parsing: 0.24s; 6 | File: IFC4x2.exp 7 | ParseException: Expected "END_SCHEMA", found 'E' (at char 72625), (line:3398, col:1) 8 | Size: 377.34 kB; Parsing: 0.54s; 9 | IFC4x2.xsd is not an EXPRESS schema file. 10 | ------------------------------------------------------------------------------- 11 | Folder Runtime: 0.78 sec. 12 | Overall Runtime: 0.78 sec. 13 | -------------------------------------------------------------------------------- /profiling/result-p21-fast-loader-cpython.txt: -------------------------------------------------------------------------------- 1 | 2 | Entering Folder 3 | 4 | Entering Folder 5 | File: ATS1-out.stp; Size: 18.12 kB; IO: 0.00s; Parsing: 0.02s; Sum: 0.02s; 6 | File: ATS10-out.stp; Size: 113.92 kB; IO: 0.00s; Parsing: 0.15s; Sum: 0.15s; 7 | File: ATS2-out.stp; Size: 33.95 kB; IO: 0.00s; Parsing: 0.04s; Sum: 0.04s; 8 | File: ATS3-out.stp; Size: 59.17 kB; IO: 0.00s; Parsing: 0.08s; Sum: 0.08s; 9 | File: ATS4-out.stp; Size: 109.92 kB; IO: 0.00s; Parsing: 0.14s; Sum: 0.14s; 10 | File: ATS7-out.stp; Size: 127.90 kB; IO: 0.00s; Parsing: 0.17s; Sum: 0.17s; 11 | File: ATS8-out.stp; Size: 263.56 kB; IO: 0.00s; Parsing: 0.36s; Sum: 0.36s; 12 | ------------------------------------------------------------------------------- 13 | Folder Runtime: 0.97 sec. 14 | 15 | Entering Folder 16 | File: as1-oc-214.stp; Size: 431.61 kB; IO: 0.00s; Parsing: 0.62s; Sum: 0.62s; 17 | File: dm1-id-214.stp; Size: 85.51 kB; IO: 0.00s; Parsing: 0.13s; Sum: 0.13s; 18 | File: FOOT.stp; Size: 6.59 kB; IO: 0.00s; Parsing: 0.01s; Sum: 0.01s; 19 | File: FOOT_BACK_000.stp; Size: 26.07 kB; IO: 0.00s; Parsing: 0.04s; Sum: 0.04s; 20 | File: FOOT_FRONT_000.stp; Size: 26.01 kB; IO: 0.00s; Parsing: 0.04s; Sum: 0.04s; 21 | File: HEAD.stp; Size: 6.58 kB; IO: 0.00s; Parsing: 0.01s; Sum: 0.01s; 22 | File: HEAD_BACK.stp; Size: 45.76 kB; IO: 0.00s; Parsing: 0.06s; Sum: 0.06s; 23 | File: HEAD_FRONT.stp; Size: 15.56 kB; IO: 0.00s; Parsing: 0.02s; Sum: 0.02s; 24 | File: io1-cm-214.stp; Size: 41.71 kB; IO: 0.00s; Parsing: 0.07s; Sum: 0.07s; 25 | File: MAINBODY.stp; Size: 6.63 kB; IO: 0.00s; Parsing: 0.01s; Sum: 0.01s; 26 | File: MAINBODY_BACK.stp; Size: 112.62 kB; IO: 0.00s; Parsing: 0.15s; Sum: 0.15s; 27 | File: MAINBODY_FRONT.stp; Size: 85.46 kB; IO: 0.00s; Parsing: 0.12s; Sum: 0.12s; 28 | File: s1-c5-214.stp; Size: 12.11 kB; IO: 0.00s; Parsing: 0.02s; Sum: 0.02s; 29 | File: sg1-c5-214.stp; Size: 23.27 kB; IO: 0.00s; Parsing: 0.03s; Sum: 0.03s; 30 | File: TAIL.stp; Size: 7.41 kB; IO: 0.00s; Parsing: 0.01s; Sum: 0.01s; 31 | File: TAIL_MIDDLE_PART.stp; Size: 37.85 kB; IO: 0.00s; Parsing: 0.06s; Sum: 0.06s; 32 | File: TAIL_TURBINE.stp; Size: 39.50 kB; IO: 0.00s; Parsing: 0.06s; Sum: 0.06s; 33 | ------------------------------------------------------------------------------- 34 | Folder Runtime: 1.46 sec. 35 | 36 | Entering Folder 37 | File: Bien-Zenker_Jasmin-Sun-AC14-V2.ifc; Size: 4697.76 kB; IO: 0.01s; Parsing: 7.23s; Sum: 7.25s; 38 | ------------------------------------------------------------------------------- 39 | Folder Runtime: 7.25 sec. 40 | README.md is not as STEP-file. 41 | ------------------------------------------------------------------------------- 42 | Folder Runtime: 9.74 sec. 43 | Overall Runtime: 9.74 sec. 44 | -------------------------------------------------------------------------------- /profiling/result-p21-fast-loader-pypy3.txt: -------------------------------------------------------------------------------- 1 | 2 | Entering Folder 3 | 4 | Entering Folder 5 | File: ATS1-out.stp; Size: 18.12 kB; IO: 0.00s; Parsing: 0.09s; Sum: 0.09s; 6 | File: ATS10-out.stp; Size: 113.92 kB; IO: 0.00s; Parsing: 0.25s; Sum: 0.25s; 7 | File: ATS2-out.stp; Size: 33.95 kB; IO: 0.00s; Parsing: 0.04s; Sum: 0.04s; 8 | File: ATS3-out.stp; Size: 59.17 kB; IO: 0.00s; Parsing: 0.05s; Sum: 0.05s; 9 | File: ATS4-out.stp; Size: 109.92 kB; IO: 0.00s; Parsing: 0.04s; Sum: 0.04s; 10 | File: ATS7-out.stp; Size: 127.90 kB; IO: 0.00s; Parsing: 0.09s; Sum: 0.10s; 11 | File: ATS8-out.stp; Size: 263.56 kB; IO: 0.00s; Parsing: 0.05s; Sum: 0.06s; 12 | ------------------------------------------------------------------------------- 13 | Folder Runtime: 0.63 sec. 14 | 15 | Entering Folder 16 | File: as1-oc-214.stp; Size: 431.61 kB; IO: 0.00s; Parsing: 0.30s; Sum: 0.30s; 17 | File: dm1-id-214.stp; Size: 85.51 kB; IO: 0.00s; Parsing: 0.05s; Sum: 0.05s; 18 | File: FOOT.stp; Size: 6.59 kB; IO: 0.00s; Parsing: 0.01s; Sum: 0.01s; 19 | File: FOOT_BACK_000.stp; Size: 26.07 kB; IO: 0.00s; Parsing: 0.01s; Sum: 0.01s; 20 | File: FOOT_FRONT_000.stp; Size: 26.01 kB; IO: 0.00s; Parsing: 0.01s; Sum: 0.01s; 21 | File: HEAD.stp; Size: 6.58 kB; IO: 0.00s; Parsing: 0.01s; Sum: 0.01s; 22 | File: HEAD_BACK.stp; Size: 45.76 kB; IO: 0.00s; Parsing: 0.01s; Sum: 0.01s; 23 | File: HEAD_FRONT.stp; Size: 15.56 kB; IO: 0.00s; Parsing: 0.00s; Sum: 0.01s; 24 | File: io1-cm-214.stp; Size: 41.71 kB; IO: 0.00s; Parsing: 0.01s; Sum: 0.02s; 25 | File: MAINBODY.stp; Size: 6.63 kB; IO: 0.00s; Parsing: 0.00s; Sum: 0.00s; 26 | File: MAINBODY_BACK.stp; Size: 112.62 kB; IO: 0.00s; Parsing: 0.02s; Sum: 0.03s; 27 | File: MAINBODY_FRONT.stp; Size: 85.46 kB; IO: 0.00s; Parsing: 0.02s; Sum: 0.02s; 28 | File: s1-c5-214.stp; Size: 12.11 kB; IO: 0.00s; Parsing: 0.01s; Sum: 0.01s; 29 | File: sg1-c5-214.stp; Size: 23.27 kB; IO: 0.00s; Parsing: 0.00s; Sum: 0.01s; 30 | File: TAIL.stp; Size: 7.41 kB; IO: 0.00s; Parsing: 0.01s; Sum: 0.01s; 31 | File: TAIL_MIDDLE_PART.stp; Size: 37.85 kB; IO: 0.00s; Parsing: 0.01s; Sum: 0.01s; 32 | File: TAIL_TURBINE.stp; Size: 39.50 kB; IO: 0.00s; Parsing: 0.01s; Sum: 0.01s; 33 | ------------------------------------------------------------------------------- 34 | Folder Runtime: 0.51 sec. 35 | 36 | Entering Folder 37 | File: Bien-Zenker_Jasmin-Sun-AC14-V2.ifc; Size: 4697.76 kB; IO: 0.05s; Parsing: 1.06s; Sum: 1.11s; 38 | ------------------------------------------------------------------------------- 39 | Folder Runtime: 1.11 sec. 40 | README.md is not as STEP-file. 41 | ------------------------------------------------------------------------------- 42 | Folder Runtime: 2.26 sec. 43 | Overall Runtime: 2.26 sec. 44 | -------------------------------------------------------------------------------- /profiling/result-p21-ply-cpython.txt: -------------------------------------------------------------------------------- 1 | 2 | Entering Folder 3 | 4 | Entering Folder 5 | File: ATS1-out.stp; Size: 18.12 kB; Loading Time: 0.04 sec. 6 | File: ATS10-out.stp; Size: 113.92 kB; Loading Time: 0.21 sec. 7 | File: ATS2-out.stp; Size: 33.95 kB; Loading Time: 0.06 sec. 8 | File: ATS3-out.stp; Size: 59.17 kB; Loading Time: 0.10 sec. 9 | File: ATS4-out.stp; Size: 109.92 kB; Loading Time: 0.21 sec. 10 | File: ATS7-out.stp; Size: 127.90 kB; Loading Time: 0.23 sec. 11 | File: ATS8-out.stp; Size: 263.56 kB; Loading Time: 0.55 sec. 12 | ------------------------------------------------------------------------------- 13 | Folder Runtime: 1.40 sec. 14 | 15 | Entering Folder 16 | File: as1-oc-214.stp; Size: 431.61 kB; Loading Time: 1.13 sec. 17 | File: dm1-id-214.stp; Size: 85.51 kB; Loading Time: 0.19 sec. 18 | File: FOOT.stp; Size: 6.59 kB; Loading Time: 0.02 sec. 19 | File: FOOT_BACK_000.stp; Size: 26.07 kB; Loading Time: 0.06 sec. 20 | File: FOOT_FRONT_000.stp; Size: 26.01 kB; Loading Time: 0.06 sec. 21 | File: HEAD.stp; Size: 6.58 kB; Loading Time: 0.02 sec. 22 | File: HEAD_BACK.stp; Size: 45.76 kB; Loading Time: 0.10 sec. 23 | File: HEAD_FRONT.stp; Size: 15.56 kB; Loading Time: 0.03 sec. 24 | File: io1-cm-214.stp; Size: 41.71 kB; Loading Time: 0.12 sec. 25 | File: MAINBODY.stp; Size: 6.63 kB; Loading Time: 0.02 sec. 26 | File: MAINBODY_BACK.stp; Size: 112.62 kB; Loading Time: 0.24 sec. 27 | File: MAINBODY_FRONT.stp; Size: 85.46 kB; Loading Time: 0.18 sec. 28 | File: s1-c5-214.stp; Size: 12.11 kB; Loading Time: 0.03 sec. 29 | File: sg1-c5-214.stp; Size: 23.27 kB; Loading Time: 0.06 sec. 30 | File: TAIL.stp; Size: 7.41 kB; Loading Time: 0.02 sec. 31 | File: TAIL_MIDDLE_PART.stp; Size: 37.85 kB; Loading Time: 0.09 sec. 32 | File: TAIL_TURBINE.stp; Size: 39.50 kB; Loading Time: 0.10 sec. 33 | ------------------------------------------------------------------------------- 34 | Folder Runtime: 2.46 sec. 35 | 36 | Entering Folder 37 | File: Bien-Zenker_Jasmin-Sun-AC14-V2.ifc; Size: 4697.76 kB; Loading Time: 46.79 sec. 38 | ------------------------------------------------------------------------------- 39 | Folder Runtime: 46.79 sec. 40 | README.md is not as STEP-file. 41 | ------------------------------------------------------------------------------- 42 | Folder Runtime: 50.66 sec. 43 | Overall Runtime: 50.66 sec. 44 | -------------------------------------------------------------------------------- /profiling/result-p21-ply-pypy3.txt: -------------------------------------------------------------------------------- 1 | 2 | Entering Folder 3 | 4 | Entering Folder 5 | File: ATS1-out.stp; Size: 18.12 kB; Loading Time: 0.26 sec. 6 | File: ATS10-out.stp; Size: 113.92 kB; Loading Time: 0.34 sec. 7 | File: ATS2-out.stp; Size: 33.95 kB; Loading Time: 0.03 sec. 8 | File: ATS3-out.stp; Size: 59.17 kB; Loading Time: 0.06 sec. 9 | File: ATS4-out.stp; Size: 109.92 kB; Loading Time: 0.04 sec. 10 | File: ATS7-out.stp; Size: 127.90 kB; Loading Time: 0.08 sec. 11 | File: ATS8-out.stp; Size: 263.56 kB; Loading Time: 0.08 sec. 12 | ------------------------------------------------------------------------------- 13 | Folder Runtime: 0.88 sec. 14 | 15 | Entering Folder 16 | File: as1-oc-214.stp; Size: 431.61 kB; Loading Time: 0.35 sec. 17 | File: dm1-id-214.stp; Size: 85.51 kB; Loading Time: 0.04 sec. 18 | File: FOOT.stp; Size: 6.59 kB; Loading Time: 0.01 sec. 19 | File: FOOT_BACK_000.stp; Size: 26.07 kB; Loading Time: 0.01 sec. 20 | File: FOOT_FRONT_000.stp; Size: 26.01 kB; Loading Time: 0.01 sec. 21 | File: HEAD.stp; Size: 6.58 kB; Loading Time: 0.01 sec. 22 | File: HEAD_BACK.stp; Size: 45.76 kB; Loading Time: 0.02 sec. 23 | File: HEAD_FRONT.stp; Size: 15.56 kB; Loading Time: 0.01 sec. 24 | File: io1-cm-214.stp; Size: 41.71 kB; Loading Time: 0.02 sec. 25 | File: MAINBODY.stp; Size: 6.63 kB; Loading Time: 0.01 sec. 26 | File: MAINBODY_BACK.stp; Size: 112.62 kB; Loading Time: 0.04 sec. 27 | File: MAINBODY_FRONT.stp; Size: 85.46 kB; Loading Time: 0.03 sec. 28 | File: s1-c5-214.stp; Size: 12.11 kB; Loading Time: 0.01 sec. 29 | File: sg1-c5-214.stp; Size: 23.27 kB; Loading Time: 0.01 sec. 30 | File: TAIL.stp; Size: 7.41 kB; Loading Time: 0.01 sec. 31 | File: TAIL_MIDDLE_PART.stp; Size: 37.85 kB; Loading Time: 0.01 sec. 32 | File: TAIL_TURBINE.stp; Size: 39.50 kB; Loading Time: 0.02 sec. 33 | ------------------------------------------------------------------------------- 34 | Folder Runtime: 0.63 sec. 35 | 36 | Entering Folder 37 | File: Bien-Zenker_Jasmin-Sun-AC14-V2.ifc; Size: 4697.76 kB; Loading Time: 16.29 sec. 38 | ------------------------------------------------------------------------------- 39 | Folder Runtime: 16.29 sec. 40 | README.md is not as STEP-file. 41 | ------------------------------------------------------------------------------- 42 | Folder Runtime: 17.80 sec. 43 | Overall Runtime: 17.80 sec. 44 | -------------------------------------------------------------------------------- /profiling/result-p21-pyparsing-cpython.txt: -------------------------------------------------------------------------------- 1 | 2 | Entering Folder 3 | 4 | Entering Folder 5 | File: ATS1-out.stp; Size: 18.12 kB; Loading Time: 0.22 sec. 6 | File: ATS10-out.stp; Size: 113.92 kB; Loading Time: 1.45 sec. 7 | File: ATS2-out.stp; Size: 33.95 kB; Loading Time: 0.37 sec. 8 | File: ATS3-out.stp; Size: 59.17 kB; Loading Time: 0.67 sec. 9 | File: ATS4-out.stp; Size: 109.92 kB; Loading Time: 1.37 sec. 10 | File: ATS7-out.stp; Size: 127.90 kB; Loading Time: 1.50 sec. 11 | File: ATS8-out.stp; Size: 263.56 kB; Loading Time: 3.59 sec. 12 | ------------------------------------------------------------------------------- 13 | Folder Runtime: 9.18 sec. 14 | 15 | Entering Folder 16 | File: as1-oc-214.stp; Size: 431.61 kB; Loading Time: 6.03 sec. 17 | File: dm1-id-214.stp; Size: 85.51 kB; Loading Time: 1.19 sec. 18 | File: FOOT.stp; Size: 6.59 kB; Loading Time: 0.11 sec. 19 | File: FOOT_BACK_000.stp; Size: 26.07 kB; Loading Time: 0.43 sec. 20 | File: FOOT_FRONT_000.stp; Size: 26.01 kB; Loading Time: 0.43 sec. 21 | File: HEAD.stp; Size: 6.58 kB; Loading Time: 0.10 sec. 22 | File: HEAD_BACK.stp; Size: 45.76 kB; Loading Time: 0.65 sec. 23 | File: HEAD_FRONT.stp; Size: 15.56 kB; Loading Time: 0.23 sec. 24 | File: io1-cm-214.stp; Size: 41.71 kB; Loading Time: 0.71 sec. 25 | File: MAINBODY.stp; Size: 6.63 kB; Loading Time: 0.10 sec. 26 | File: MAINBODY_BACK.stp; Size: 112.62 kB; Loading Time: 1.66 sec. 27 | File: MAINBODY_FRONT.stp; Size: 85.46 kB; Loading Time: 1.30 sec. 28 | File: s1-c5-214.stp; Size: 12.11 kB; Loading Time: 0.18 sec. 29 | File: sg1-c5-214.stp; Size: 23.27 kB; Loading Time: 0.43 sec. 30 | File: TAIL.stp; Size: 7.41 kB; Loading Time: 0.12 sec. 31 | File: TAIL_MIDDLE_PART.stp; Size: 37.85 kB; Loading Time: 0.66 sec. 32 | File: TAIL_TURBINE.stp; Size: 39.50 kB; Loading Time: 0.67 sec. 33 | ------------------------------------------------------------------------------- 34 | Folder Runtime: 15.02 sec. 35 | 36 | Entering Folder 37 | File: Bien-Zenker_Jasmin-Sun-AC14-V2.ifc; Size: 4697.76 kB; Loading Time: 59.87 sec. 38 | ------------------------------------------------------------------------------- 39 | Folder Runtime: 59.87 sec. 40 | README.md is not as STEP-file. 41 | ------------------------------------------------------------------------------- 42 | Folder Runtime: 84.12 sec. 43 | Overall Runtime: 84.12 sec. 44 | -------------------------------------------------------------------------------- /profiling/result-p21-pyparsing-pypy3.txt: -------------------------------------------------------------------------------- 1 | 2 | Entering Folder 3 | 4 | Entering Folder 5 | File: ATS1-out.stp; Size: 18.12 kB; Loading Time: 0.22 sec. 6 | File: ATS10-out.stp; Size: 113.92 kB; Loading Time: 0.32 sec. 7 | File: ATS2-out.stp; Size: 33.95 kB; Loading Time: 0.03 sec. 8 | File: ATS3-out.stp; Size: 59.17 kB; Loading Time: 0.06 sec. 9 | File: ATS4-out.stp; Size: 109.92 kB; Loading Time: 0.04 sec. 10 | File: ATS7-out.stp; Size: 127.90 kB; Loading Time: 0.08 sec. 11 | File: ATS8-out.stp; Size: 263.56 kB; Loading Time: 0.07 sec. 12 | ------------------------------------------------------------------------------- 13 | Folder Runtime: 0.82 sec. 14 | 15 | Entering Folder 16 | File: as1-oc-214.stp; Size: 431.61 kB; Loading Time: 0.36 sec. 17 | File: dm1-id-214.stp; Size: 85.51 kB; Loading Time: 0.04 sec. 18 | File: FOOT.stp; Size: 6.59 kB; Loading Time: 0.01 sec. 19 | File: FOOT_BACK_000.stp; Size: 26.07 kB; Loading Time: 0.01 sec. 20 | File: FOOT_FRONT_000.stp; Size: 26.01 kB; Loading Time: 0.01 sec. 21 | File: HEAD.stp; Size: 6.58 kB; Loading Time: 0.01 sec. 22 | File: HEAD_BACK.stp; Size: 45.76 kB; Loading Time: 0.02 sec. 23 | File: HEAD_FRONT.stp; Size: 15.56 kB; Loading Time: 0.01 sec. 24 | File: io1-cm-214.stp; Size: 41.71 kB; Loading Time: 0.02 sec. 25 | File: MAINBODY.stp; Size: 6.63 kB; Loading Time: 0.01 sec. 26 | File: MAINBODY_BACK.stp; Size: 112.62 kB; Loading Time: 0.04 sec. 27 | File: MAINBODY_FRONT.stp; Size: 85.46 kB; Loading Time: 0.03 sec. 28 | File: s1-c5-214.stp; Size: 12.11 kB; Loading Time: 0.01 sec. 29 | File: sg1-c5-214.stp; Size: 23.27 kB; Loading Time: 0.01 sec. 30 | File: TAIL.stp; Size: 7.41 kB; Loading Time: 0.01 sec. 31 | File: TAIL_MIDDLE_PART.stp; Size: 37.85 kB; Loading Time: 0.01 sec. 32 | File: TAIL_TURBINE.stp; Size: 39.50 kB; Loading Time: 0.02 sec. 33 | ------------------------------------------------------------------------------- 34 | Folder Runtime: 0.64 sec. 35 | 36 | Entering Folder 37 | File: Bien-Zenker_Jasmin-Sun-AC14-V2.ifc; Size: 4697.76 kB; Loading Time: 16.20 sec. 38 | ------------------------------------------------------------------------------- 39 | Folder Runtime: 16.20 sec. 40 | README.md is not as STEP-file. 41 | ------------------------------------------------------------------------------- 42 | Folder Runtime: 17.66 sec. 43 | Overall Runtime: 17.66 sec. 44 | -------------------------------------------------------------------------------- /pytest.ini: -------------------------------------------------------------------------------- 1 | [pytest] 2 | 3 | testpaths = tests 4 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | pyparsing 2 | antlr4-python3-runtime<4.10 3 | pytest 4 | wheel 5 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [bdist_wheel] 2 | universal = 0 3 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # Created: 25.12.2019 3 | # Copyright (c) 2019-2020 Manfred Moitzi 4 | # License: MIT License 5 | import os 6 | from setuptools import setup, find_packages 7 | 8 | 9 | # setuptools docs: https://setuptools.readthedocs.io/en/latest/setuptools.html 10 | 11 | def get_version(): 12 | # do not import package, because required packages may not be installed yet 13 | v = {} 14 | exec(open('./src/steputils/version.py').read(), v) 15 | return v['__version__'] 16 | 17 | 18 | def read(fname, until=""): 19 | def read_until(lines): 20 | last_index = -1 21 | for index, line in enumerate(lines): 22 | if line.startswith(until): 23 | last_index = index 24 | break 25 | return "".join(lines[:last_index]) 26 | 27 | try: 28 | with open(os.path.join(os.path.dirname(__file__), fname)) as f: 29 | return read_until(f.readlines()) if until else f.read() 30 | except IOError: 31 | return "File '%s' not found.\n" % fname 32 | 33 | 34 | setup( 35 | name='steputils', 36 | version=get_version(), 37 | description='A Python package to read/write STEP data files.', 38 | author='Manfred Moitzi', 39 | author_email='me@mozman.at', 40 | url='http://github.com/mozman/steputils', 41 | download_url='https://pypi.org/project/steputils/', 42 | project_urls={ 43 | 'Documentation': 'https://steputils.readthedocs.io', 44 | 'Source': 'http://github.com/mozman/steputils', 45 | 'Bug Tracker': 'https://github.com/mozman/steputils/issues', 46 | }, 47 | python_requires='>=3.7', 48 | package_dir={'': 'src'}, 49 | packages=find_packages('src'), 50 | provides=['steputils'], 51 | install_requires=['pyparsing', 'antlr4-python3-runtime<4.10'], 52 | setup_requires=['wheel'], 53 | tests_require=['pytest'], 54 | keywords=['IFC4', 'CAD', 'STEP'], 55 | long_description=read('README.md') + read('NEWS.md'), 56 | long_description_content_type="text/markdown", 57 | platforms="OS Independent", 58 | license="MIT License", 59 | classifiers=[ 60 | "Development Status :: 3 - Alpha", 61 | "License :: OSI Approved :: MIT License", 62 | "Operating System :: OS Independent", 63 | "Programming Language :: Python :: 3", 64 | "Programming Language :: Python :: 3.7", 65 | "Programming Language :: Python :: 3.8", 66 | "Programming Language :: Python :: 3.9", 67 | "Programming Language :: Python :: 3.10", 68 | "Programming Language :: Python :: Implementation :: CPython", 69 | "Programming Language :: Python :: Implementation :: PyPy", 70 | "Intended Audience :: Developers", 71 | "Topic :: Software Development :: Libraries :: Python Modules", 72 | ] 73 | ) 74 | 75 | # Development Status :: 1 - Planning 76 | # Development Status :: 2 - Pre-Alpha 77 | # Development Status :: 3 - Alpha 78 | # Development Status :: 4 - Beta 79 | # Development Status :: 5 - Production/Stable 80 | # Development Status :: 6 - Mature 81 | # Development Status :: 7 - Inactive 82 | -------------------------------------------------------------------------------- /src/steputils/__init__.py: -------------------------------------------------------------------------------- 1 | # Created: 26.12.2019 2 | # Copyright (c) 2019-2020, Manfred Moitzi 3 | # License: MIT License 4 | import sys 5 | from .version import version, __version__ 6 | 7 | VERSION = __version__ 8 | __author__ = "mozman " 9 | 10 | PYPY = hasattr(sys, 'pypy_version_info') 11 | PYPY_ON_WINDOWS = sys.platform.startswith('win') and PYPY 12 | -------------------------------------------------------------------------------- /src/steputils/exceptions.py: -------------------------------------------------------------------------------- 1 | # Created: 07.01.2020 2 | # Copyright (c) 2020 Manfred Moitzi 3 | # License: MIT License 4 | 5 | 6 | class ParseError(Exception): 7 | pass 8 | 9 | 10 | class StringDecodingError(ParseError): 11 | pass 12 | 13 | 14 | class StepFileStructureError(Exception): 15 | pass 16 | -------------------------------------------------------------------------------- /src/steputils/express/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2020 Manfred Moitzi 2 | # License: MIT License 3 | import antlr4 4 | from .expressLexer import expressLexer 5 | from .expressParser import expressParser 6 | 7 | 8 | def tokens(text): 9 | stream = antlr4.InputStream(text) 10 | lexer = expressLexer(stream) 11 | return antlr4.CommonTokenStream(lexer) 12 | 13 | 14 | class Parser: 15 | def __init__(self, text): 16 | self.parser = expressParser(tokens(text)) 17 | 18 | def schema(self): 19 | return self.parser.syntax().children[0] # just return first schema 20 | -------------------------------------------------------------------------------- /src/steputils/express/ast.py: -------------------------------------------------------------------------------- 1 | # Created: 12.01.2020 2 | # Copyright (c) 2020 Manfred Moitzi 3 | # License: MIT License 4 | from typing import Iterable, Optional 5 | 6 | 7 | class Literal(str): 8 | @classmethod 9 | def action(cls, toks): 10 | return cls(toks[0]) 11 | 12 | 13 | class StringLiteral(Literal): 14 | @classmethod 15 | def action(cls, toks): 16 | return cls(toks[0][1:-1]) # remove quotes 17 | 18 | @classmethod 19 | def decode(cls, toks): 20 | return cls(''.join(chr(int(c, 16)) for c in toks)) 21 | 22 | 23 | class LogicalLiteral(Literal): 24 | pass 25 | 26 | 27 | class BuiltInConstant(Literal): 28 | pass 29 | 30 | 31 | class BuiltInFunction(Literal): 32 | pass 33 | 34 | 35 | class BuiltInProcedure(Literal): 36 | pass 37 | 38 | 39 | class Type(Literal): 40 | pass 41 | 42 | 43 | class SimpleID(Literal): 44 | pass 45 | 46 | 47 | class Operand(Literal): 48 | pass 49 | 50 | 51 | def is_literal(item): 52 | return isinstance(item, (StringLiteral, LogicalLiteral, int, float)) 53 | 54 | 55 | class AST: 56 | def __init__(self, name: str, children: Iterable): 57 | self.name = name 58 | self._children = tuple(children) 59 | 60 | @property 61 | def children(self): 62 | return self._children 63 | 64 | @property 65 | def value(self): 66 | return self.children[0] 67 | 68 | def __repr__(self): 69 | content = ', '.join(repr(c) for c in self.children) 70 | return f'({self.name}, {content})' 71 | 72 | def __len__(self): 73 | return len(self.children) 74 | 75 | def __iter__(self): 76 | return iter(self.children) 77 | 78 | def __getitem__(self, item): 79 | return self.children[item] 80 | 81 | @staticmethod 82 | def action(toks): 83 | return AST(toks[0], toks[1:]) 84 | 85 | 86 | class Primary(AST): 87 | @staticmethod 88 | def action(toks): 89 | if is_literal(toks[0]): 90 | return toks[0] 91 | else: 92 | return Primary('Primary', toks) 93 | 94 | 95 | class BoundSpec(AST): 96 | @staticmethod 97 | def action(toks): 98 | return BoundSpec('BoundSpec', (toks[1], toks[3])) 99 | 100 | 101 | class IndexQualifier(AST): 102 | @staticmethod 103 | def action(toks): 104 | index_1 = toks[1] 105 | try: 106 | index_2 = toks[4] 107 | except IndexError: 108 | index_2 = None 109 | return IndexQualifier('IndexQualifier', (index_1, index_2)) 110 | -------------------------------------------------------------------------------- /src/steputils/express/express.tokens: -------------------------------------------------------------------------------- 1 | T__0=1 2 | T__1=2 3 | T__2=3 4 | T__3=4 5 | T__4=5 6 | T__5=6 7 | T__6=7 8 | T__7=8 9 | T__8=9 10 | T__9=10 11 | T__10=11 12 | T__11=12 13 | T__12=13 14 | T__13=14 15 | T__14=15 16 | T__15=16 17 | T__16=17 18 | T__17=18 19 | T__18=19 20 | T__19=20 21 | T__20=21 22 | T__21=22 23 | T__22=23 24 | T__23=24 25 | T__24=25 26 | T__25=26 27 | T__26=27 28 | T__27=28 29 | T__28=29 30 | ABS=30 31 | ABSTRACT=31 32 | ACOS=32 33 | AGGREGATE=33 34 | ALIAS=34 35 | AND=35 36 | ANDOR=36 37 | ARRAY=37 38 | AS=38 39 | ASIN=39 40 | ATAN=40 41 | BAG=41 42 | BASED_ON=42 43 | BEGIN=43 44 | BINARY=44 45 | BLENGTH=45 46 | BOOLEAN=46 47 | BY=47 48 | CASE=48 49 | CONSTANT=49 50 | CONST_E=50 51 | COS=51 52 | DERIVE=52 53 | DIV=53 54 | ELSE=54 55 | END=55 56 | END_ALIAS=56 57 | END_CASE=57 58 | END_CONSTANT=58 59 | END_ENTITY=59 60 | END_FUNCTION=60 61 | END_IF=61 62 | END_LOCAL=62 63 | END_PROCEDURE=63 64 | END_REPEAT=64 65 | END_RULE=65 66 | END_SCHEMA=66 67 | END_SUBTYPE_CONSTRAINT=67 68 | END_TYPE=68 69 | ENTITY=69 70 | ENUMERATION=70 71 | ESCAPE=71 72 | EXISTS=72 73 | EXTENSIBLE=73 74 | EXP=74 75 | FALSE=75 76 | FIXED=76 77 | FOR=77 78 | FORMAT=78 79 | FROM=79 80 | FUNCTION=80 81 | GENERIC=81 82 | GENERIC_ENTITY=82 83 | HIBOUND=83 84 | HIINDEX=84 85 | IF=85 86 | IN=86 87 | INSERT=87 88 | INTEGER=88 89 | INVERSE=89 90 | LENGTH=90 91 | LIKE=91 92 | LIST=92 93 | LOBOUND=93 94 | LOCAL=94 95 | LOG=95 96 | LOG10=96 97 | LOG2=97 98 | LOGICAL=98 99 | LOINDEX=99 100 | MOD=100 101 | NOT=101 102 | NUMBER=102 103 | NVL=103 104 | ODD=104 105 | OF=105 106 | ONEOF=106 107 | OPTIONAL=107 108 | OR=108 109 | OTHERWISE=109 110 | PI=110 111 | PROCEDURE=111 112 | QUERY=112 113 | REAL=113 114 | REFERENCE=114 115 | REMOVE=115 116 | RENAMED=116 117 | REPEAT=117 118 | RETURN=118 119 | ROLESOF=119 120 | RULE=120 121 | SCHEMA=121 122 | SELECT=122 123 | SELF=123 124 | SET=124 125 | SIN=125 126 | SIZEOF=126 127 | SKIP_=127 128 | SQRT=128 129 | STRING=129 130 | SUBTYPE=130 131 | SUBTYPE_CONSTRAINT=131 132 | SUPERTYPE=132 133 | TAN=133 134 | THEN=134 135 | TO=135 136 | TOTAL_OVER=136 137 | TRUE=137 138 | TYPE=138 139 | TYPEOF=139 140 | UNIQUE=140 141 | UNKNOWN=141 142 | UNTIL=142 143 | USE=143 144 | USEDIN=144 145 | VALUE=145 146 | VALUE_IN=146 147 | VALUE_UNIQUE=147 148 | VAR=148 149 | WHERE=149 150 | WHILE=150 151 | WITH=151 152 | XOR=152 153 | BINARY_LITERAL=153 154 | ENCODED_STRING_LITERAL=154 155 | INTEGER_LITERAL=155 156 | REAL_LITERAL=156 157 | SIMPLE_ID=157 158 | QUOTECHAR=158 159 | SIMPLE_STRING_LITERAL=159 160 | COMMENTS=160 161 | TAIL_REMARK=161 162 | WS=162 163 | ';'=1 164 | '('=2 165 | ','=3 166 | ')'=4 167 | '+'=5 168 | '-'=6 169 | '['=7 170 | ']'=8 171 | ':'=9 172 | ':='=10 173 | '.'=11 174 | '?'=12 175 | '**'=13 176 | '\\'=14 177 | '{'=15 178 | '}'=16 179 | '<'=17 180 | '<='=18 181 | '*'=19 182 | '/'=20 183 | '||'=21 184 | '<*'=22 185 | '|'=23 186 | '>'=24 187 | '>='=25 188 | '<>'=26 189 | '='=27 190 | ':<>:'=28 191 | ':=:'=29 192 | 'ABS'=30 193 | 'ABSTRACT'=31 194 | 'ACOS'=32 195 | 'AGGREGATE'=33 196 | 'ALIAS'=34 197 | 'AND'=35 198 | 'ANDOR'=36 199 | 'ARRAY'=37 200 | 'AS'=38 201 | 'ASIN'=39 202 | 'ATAN'=40 203 | 'BAG'=41 204 | 'BASED_ON'=42 205 | 'BEGIN'=43 206 | 'BINARY'=44 207 | 'BLENGTH'=45 208 | 'BOOLEAN'=46 209 | 'BY'=47 210 | 'CASE'=48 211 | 'CONSTANT'=49 212 | 'CONST_E'=50 213 | 'COS'=51 214 | 'DERIVE'=52 215 | 'DIV'=53 216 | 'ELSE'=54 217 | 'END'=55 218 | 'END_ALIAS'=56 219 | 'END_CASE'=57 220 | 'END_CONSTANT'=58 221 | 'END_ENTITY'=59 222 | 'END_FUNCTION'=60 223 | 'END_IF'=61 224 | 'END_LOCAL'=62 225 | 'END_PROCEDURE'=63 226 | 'END_REPEAT'=64 227 | 'END_RULE'=65 228 | 'END_SCHEMA'=66 229 | 'END_SUBTYPE_CONSTRAINT'=67 230 | 'END_TYPE'=68 231 | 'ENTITY'=69 232 | 'ENUMERATION'=70 233 | 'ESCAPE'=71 234 | 'EXISTS'=72 235 | 'EXTENSIBLE'=73 236 | 'EXP'=74 237 | 'FALSE'=75 238 | 'FIXED'=76 239 | 'FOR'=77 240 | 'FORMAT'=78 241 | 'FROM'=79 242 | 'FUNCTION'=80 243 | 'GENERIC'=81 244 | 'GENERIC_ENTITY'=82 245 | 'HIBOUND'=83 246 | 'HIINDEX'=84 247 | 'IF'=85 248 | 'IN'=86 249 | 'INSERT'=87 250 | 'INTEGER'=88 251 | 'INVERSE'=89 252 | 'LENGTH'=90 253 | 'LIKE'=91 254 | 'LIST'=92 255 | 'LOBOUND'=93 256 | 'LOCAL'=94 257 | 'LOG'=95 258 | 'LOG10'=96 259 | 'LOG2'=97 260 | 'LOGICAL'=98 261 | 'LOINDEX'=99 262 | 'MOD'=100 263 | 'NOT'=101 264 | 'NUMBER'=102 265 | 'NVL'=103 266 | 'ODD'=104 267 | 'OF'=105 268 | 'ONEOF'=106 269 | 'OPTIONAL'=107 270 | 'OR'=108 271 | 'OTHERWISE'=109 272 | 'PI'=110 273 | 'PROCEDURE'=111 274 | 'QUERY'=112 275 | 'REAL'=113 276 | 'REFERENCE'=114 277 | 'REMOVE'=115 278 | 'RENAMED'=116 279 | 'REPEAT'=117 280 | 'RETURN'=118 281 | 'ROLESOF'=119 282 | 'RULE'=120 283 | 'SCHEMA'=121 284 | 'SELECT'=122 285 | 'SELF'=123 286 | 'SET'=124 287 | 'SIN'=125 288 | 'SIZEOF'=126 289 | 'SKIP'=127 290 | 'SQRT'=128 291 | 'STRING'=129 292 | 'SUBTYPE'=130 293 | 'SUBTYPE_CONSTRAINT'=131 294 | 'SUPERTYPE'=132 295 | 'TAN'=133 296 | 'THEN'=134 297 | 'TO'=135 298 | 'TOTAL_OVER'=136 299 | 'TRUE'=137 300 | 'TYPE'=138 301 | 'TYPEOF'=139 302 | 'UNIQUE'=140 303 | 'UNKNOWN'=141 304 | 'UNTIL'=142 305 | 'USE'=143 306 | 'USEDIN'=144 307 | 'VALUE'=145 308 | 'VALUE_IN'=146 309 | 'VALUE_UNIQUE'=147 310 | 'VAR'=148 311 | 'WHERE'=149 312 | 'WHILE'=150 313 | 'WITH'=151 314 | 'XOR'=152 315 | '\''=158 316 | -------------------------------------------------------------------------------- /src/steputils/express/expressLexer.tokens: -------------------------------------------------------------------------------- 1 | T__0=1 2 | T__1=2 3 | T__2=3 4 | T__3=4 5 | T__4=5 6 | T__5=6 7 | T__6=7 8 | T__7=8 9 | T__8=9 10 | T__9=10 11 | T__10=11 12 | T__11=12 13 | T__12=13 14 | T__13=14 15 | T__14=15 16 | T__15=16 17 | T__16=17 18 | T__17=18 19 | T__18=19 20 | T__19=20 21 | T__20=21 22 | T__21=22 23 | T__22=23 24 | T__23=24 25 | T__24=25 26 | T__25=26 27 | T__26=27 28 | T__27=28 29 | T__28=29 30 | ABS=30 31 | ABSTRACT=31 32 | ACOS=32 33 | AGGREGATE=33 34 | ALIAS=34 35 | AND=35 36 | ANDOR=36 37 | ARRAY=37 38 | AS=38 39 | ASIN=39 40 | ATAN=40 41 | BAG=41 42 | BASED_ON=42 43 | BEGIN=43 44 | BINARY=44 45 | BLENGTH=45 46 | BOOLEAN=46 47 | BY=47 48 | CASE=48 49 | CONSTANT=49 50 | CONST_E=50 51 | COS=51 52 | DERIVE=52 53 | DIV=53 54 | ELSE=54 55 | END=55 56 | END_ALIAS=56 57 | END_CASE=57 58 | END_CONSTANT=58 59 | END_ENTITY=59 60 | END_FUNCTION=60 61 | END_IF=61 62 | END_LOCAL=62 63 | END_PROCEDURE=63 64 | END_REPEAT=64 65 | END_RULE=65 66 | END_SCHEMA=66 67 | END_SUBTYPE_CONSTRAINT=67 68 | END_TYPE=68 69 | ENTITY=69 70 | ENUMERATION=70 71 | ESCAPE=71 72 | EXISTS=72 73 | EXTENSIBLE=73 74 | EXP=74 75 | FALSE=75 76 | FIXED=76 77 | FOR=77 78 | FORMAT=78 79 | FROM=79 80 | FUNCTION=80 81 | GENERIC=81 82 | GENERIC_ENTITY=82 83 | HIBOUND=83 84 | HIINDEX=84 85 | IF=85 86 | IN=86 87 | INSERT=87 88 | INTEGER=88 89 | INVERSE=89 90 | LENGTH=90 91 | LIKE=91 92 | LIST=92 93 | LOBOUND=93 94 | LOCAL=94 95 | LOG=95 96 | LOG10=96 97 | LOG2=97 98 | LOGICAL=98 99 | LOINDEX=99 100 | MOD=100 101 | NOT=101 102 | NUMBER=102 103 | NVL=103 104 | ODD=104 105 | OF=105 106 | ONEOF=106 107 | OPTIONAL=107 108 | OR=108 109 | OTHERWISE=109 110 | PI=110 111 | PROCEDURE=111 112 | QUERY=112 113 | REAL=113 114 | REFERENCE=114 115 | REMOVE=115 116 | RENAMED=116 117 | REPEAT=117 118 | RETURN=118 119 | ROLESOF=119 120 | RULE=120 121 | SCHEMA=121 122 | SELECT=122 123 | SELF=123 124 | SET=124 125 | SIN=125 126 | SIZEOF=126 127 | SKIP_=127 128 | SQRT=128 129 | STRING=129 130 | SUBTYPE=130 131 | SUBTYPE_CONSTRAINT=131 132 | SUPERTYPE=132 133 | TAN=133 134 | THEN=134 135 | TO=135 136 | TOTAL_OVER=136 137 | TRUE=137 138 | TYPE=138 139 | TYPEOF=139 140 | UNIQUE=140 141 | UNKNOWN=141 142 | UNTIL=142 143 | USE=143 144 | USEDIN=144 145 | VALUE=145 146 | VALUE_IN=146 147 | VALUE_UNIQUE=147 148 | VAR=148 149 | WHERE=149 150 | WHILE=150 151 | WITH=151 152 | XOR=152 153 | BINARY_LITERAL=153 154 | ENCODED_STRING_LITERAL=154 155 | INTEGER_LITERAL=155 156 | REAL_LITERAL=156 157 | SIMPLE_ID=157 158 | QUOTECHAR=158 159 | SIMPLE_STRING_LITERAL=159 160 | COMMENTS=160 161 | TAIL_REMARK=161 162 | WS=162 163 | ';'=1 164 | '('=2 165 | ','=3 166 | ')'=4 167 | '+'=5 168 | '-'=6 169 | '['=7 170 | ']'=8 171 | ':'=9 172 | ':='=10 173 | '.'=11 174 | '?'=12 175 | '**'=13 176 | '\\'=14 177 | '{'=15 178 | '}'=16 179 | '<'=17 180 | '<='=18 181 | '*'=19 182 | '/'=20 183 | '||'=21 184 | '<*'=22 185 | '|'=23 186 | '>'=24 187 | '>='=25 188 | '<>'=26 189 | '='=27 190 | ':<>:'=28 191 | ':=:'=29 192 | 'ABS'=30 193 | 'ABSTRACT'=31 194 | 'ACOS'=32 195 | 'AGGREGATE'=33 196 | 'ALIAS'=34 197 | 'AND'=35 198 | 'ANDOR'=36 199 | 'ARRAY'=37 200 | 'AS'=38 201 | 'ASIN'=39 202 | 'ATAN'=40 203 | 'BAG'=41 204 | 'BASED_ON'=42 205 | 'BEGIN'=43 206 | 'BINARY'=44 207 | 'BLENGTH'=45 208 | 'BOOLEAN'=46 209 | 'BY'=47 210 | 'CASE'=48 211 | 'CONSTANT'=49 212 | 'CONST_E'=50 213 | 'COS'=51 214 | 'DERIVE'=52 215 | 'DIV'=53 216 | 'ELSE'=54 217 | 'END'=55 218 | 'END_ALIAS'=56 219 | 'END_CASE'=57 220 | 'END_CONSTANT'=58 221 | 'END_ENTITY'=59 222 | 'END_FUNCTION'=60 223 | 'END_IF'=61 224 | 'END_LOCAL'=62 225 | 'END_PROCEDURE'=63 226 | 'END_REPEAT'=64 227 | 'END_RULE'=65 228 | 'END_SCHEMA'=66 229 | 'END_SUBTYPE_CONSTRAINT'=67 230 | 'END_TYPE'=68 231 | 'ENTITY'=69 232 | 'ENUMERATION'=70 233 | 'ESCAPE'=71 234 | 'EXISTS'=72 235 | 'EXTENSIBLE'=73 236 | 'EXP'=74 237 | 'FALSE'=75 238 | 'FIXED'=76 239 | 'FOR'=77 240 | 'FORMAT'=78 241 | 'FROM'=79 242 | 'FUNCTION'=80 243 | 'GENERIC'=81 244 | 'GENERIC_ENTITY'=82 245 | 'HIBOUND'=83 246 | 'HIINDEX'=84 247 | 'IF'=85 248 | 'IN'=86 249 | 'INSERT'=87 250 | 'INTEGER'=88 251 | 'INVERSE'=89 252 | 'LENGTH'=90 253 | 'LIKE'=91 254 | 'LIST'=92 255 | 'LOBOUND'=93 256 | 'LOCAL'=94 257 | 'LOG'=95 258 | 'LOG10'=96 259 | 'LOG2'=97 260 | 'LOGICAL'=98 261 | 'LOINDEX'=99 262 | 'MOD'=100 263 | 'NOT'=101 264 | 'NUMBER'=102 265 | 'NVL'=103 266 | 'ODD'=104 267 | 'OF'=105 268 | 'ONEOF'=106 269 | 'OPTIONAL'=107 270 | 'OR'=108 271 | 'OTHERWISE'=109 272 | 'PI'=110 273 | 'PROCEDURE'=111 274 | 'QUERY'=112 275 | 'REAL'=113 276 | 'REFERENCE'=114 277 | 'REMOVE'=115 278 | 'RENAMED'=116 279 | 'REPEAT'=117 280 | 'RETURN'=118 281 | 'ROLESOF'=119 282 | 'RULE'=120 283 | 'SCHEMA'=121 284 | 'SELECT'=122 285 | 'SELF'=123 286 | 'SET'=124 287 | 'SIN'=125 288 | 'SIZEOF'=126 289 | 'SKIP'=127 290 | 'SQRT'=128 291 | 'STRING'=129 292 | 'SUBTYPE'=130 293 | 'SUBTYPE_CONSTRAINT'=131 294 | 'SUPERTYPE'=132 295 | 'TAN'=133 296 | 'THEN'=134 297 | 'TO'=135 298 | 'TOTAL_OVER'=136 299 | 'TRUE'=137 300 | 'TYPE'=138 301 | 'TYPEOF'=139 302 | 'UNIQUE'=140 303 | 'UNKNOWN'=141 304 | 'UNTIL'=142 305 | 'USE'=143 306 | 'USEDIN'=144 307 | 'VALUE'=145 308 | 'VALUE_IN'=146 309 | 'VALUE_UNIQUE'=147 310 | 'VAR'=148 311 | 'WHERE'=149 312 | 'WHILE'=150 313 | 'WITH'=151 314 | 'XOR'=152 315 | '\''=158 316 | -------------------------------------------------------------------------------- /src/steputils/strings.py: -------------------------------------------------------------------------------- 1 | # Created: 07.01.2020 2 | # Copyright (c) 2020 Manfred Moitzi 3 | # License: MIT License 4 | import re 5 | from .exceptions import StringDecodingError 6 | 7 | EOF = '\0' 8 | HEX_16BIT = "{:04X}" 9 | HEX_32BIT = "{:08X}" 10 | EXT_START_16 = '\\X2\\' 11 | EXT_START_32 = '\\X4\\' 12 | EXT_END = "\\X0\\" 13 | EXT_ENCODING = { 14 | 16: HEX_16BIT, 15 | 32: HEX_32BIT, 16 | } 17 | 18 | 19 | def step_encoder(s: str) -> str: 20 | buffer = [] 21 | encoding = 0 # 0 for no encoding, 16 for 16bit encoding, 32 for 32bit encoding 22 | for char in s: 23 | value = ord(char) 24 | if value < 127: # just ASCII code 25 | if encoding: # stop extended encoding 26 | buffer.append(EXT_END) 27 | encoding = 0 28 | if char == '\\': # escaping backslash 29 | char = '\\\\' 30 | elif char == "'": # escaping apostrophe 31 | char = "''" 32 | buffer.append(char) 33 | else: # value > 126 34 | if not encoding: # start new extended character sequence 35 | if value < 65536: # 16bit character 36 | encoding = 16 37 | buffer.append(EXT_START_16) 38 | else: # 32bit character 39 | encoding = 32 40 | buffer.append(EXT_START_32) 41 | elif value >= 65536 and encoding == 16: 42 | # already extended 16bit encoding, but 32bit encoding is required 43 | # stop 16bit encoding 44 | buffer.append(EXT_END) 45 | # and start 32bit encoding 46 | encoding = 32 47 | buffer.append(EXT_START_32) 48 | buffer.append(EXT_ENCODING[encoding].format(value)) 49 | if encoding: 50 | buffer.append(EXT_END) 51 | return ''.join(buffer) 52 | 53 | 54 | # control_directive = page | alphabet | extended2 | extended4 | arbitrary . 55 | # page = '\S\' character - not supported 56 | # alphabet = '\P' upper '\' - not supported 57 | # arbitrary = '\X\' hex_one - not supported 58 | # extended2 ='\X2\' HEX_16BIT { HEX_16BIT } EXT_END 59 | # extended2 ='\X4\' HEX_32BIT { HEX_32BIT } EXT_END 60 | 61 | EXT_MATCH = re.compile(r'\\(X[24])\\([0-9A-F]+)\\X0\\') 62 | 63 | 64 | def _decode_bytes(ext_type: str, hexstr: str) -> str: 65 | hex_char_count = 4 if ext_type == 'X2' else 8 66 | length = len(hexstr) 67 | if length % hex_char_count: 68 | raise StringDecodingError 69 | chars = [] 70 | start = 0 71 | 72 | while start < length: 73 | char = chr(int(hexstr[start:start + hex_char_count], 16)) 74 | chars.append(char) 75 | start += hex_char_count 76 | return ''.join(chars) 77 | 78 | 79 | def step_decoder(s: str) -> str: 80 | origin = s 81 | while True: 82 | r = EXT_MATCH.search(s) 83 | if r is None: 84 | break 85 | try: 86 | decoded_chars = _decode_bytes(r[1], r[2]) 87 | except StringDecodingError: 88 | raise StringDecodingError(f'Invalid extended encoding in string "{origin}".') 89 | s = s.replace(r[0], decoded_chars) 90 | return s.replace('\\\\', '\\') 91 | 92 | 93 | class StringBuffer: 94 | def __init__(self, buffer: str): 95 | self._buffer = buffer 96 | self._cursor = 0 97 | self.line_number = 1 98 | 99 | def look(self, n: int = 0) -> str: 100 | try: 101 | return self._buffer[self._cursor + n] 102 | except IndexError: 103 | self._cursor = len(self._buffer) 104 | return EOF 105 | 106 | def get(self) -> str: 107 | value = self.look() 108 | if value == '\n': 109 | self.line_number += 1 110 | self._cursor += 1 111 | return value 112 | 113 | def skip(self, n: int = 1) -> None: 114 | self._cursor += n 115 | -------------------------------------------------------------------------------- /src/steputils/tools.py: -------------------------------------------------------------------------------- 1 | # Created: 26.12.2019 2 | # Copyright (c) 2019-2020 Manfred Moitzi 3 | # License: MIT License 4 | from base64 import encodebytes 5 | from uuid import uuid4 6 | 7 | 8 | def guid() -> str: 9 | """ 10 | Returns a globally unique id as unicode string with a length of 11 | 22 characters according to the IFC standard. 12 | """ 13 | return encodebytes(uuid4().bytes)[:22].decode() 14 | -------------------------------------------------------------------------------- /src/steputils/version.py: -------------------------------------------------------------------------------- 1 | # version scheme for version: (major, minor, micro, release_level) 2 | # 3 | # major: 4 | # 0 .. not all planned features done 5 | # 1 .. all features available 6 | # 2 .. if significant API change (2, 3, ...) 7 | # 8 | # minor: 9 | # changes with new features or minor API changes 10 | # 11 | # micro: 12 | # changes with bug fixes, maybe also minor API changes 13 | # 14 | # release_state: 15 | # a .. alpha: adding new features - non public development state 16 | # b .. beta: testing new features - public development state 17 | # rc .. release candidate: testing release - public testing 18 | # release: public release 19 | # 20 | # examples: 21 | # major pre release alpha 2: VERSION = "0.9a2"; version = (0, 9, 0, 'a2') 22 | # major release candidate 0: VERSION = "0.9rc0"; version = (0, 9, 0, 'rc0') 23 | # major release: VERSION = "0.9"; version = (0, 9, 0, 'release') 24 | # 1. bug fix release beta0: VERSION = "0.9.1b0"; version = (0, 9, 1, 'b0') 25 | # 2. bug fix release: VERSION = "0.9.2"; version = (0, 9, 2, 'release') 26 | 27 | version = (0, 1, 0, 'release') 28 | __version__ = "0.1" 29 | -------------------------------------------------------------------------------- /tests/express/test_antlr4_parser.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2020 Manfred Moitzi 2 | # License: MIT License 3 | import pytest 4 | from steputils.express import Parser 5 | 6 | 7 | def test_schema(): 8 | parser = Parser(""" 9 | SCHEMA test; 10 | END_SCHEMA; 11 | """) 12 | tree = parser.schema() 13 | assert tree is not None 14 | 15 | 16 | def test_type_decl(): 17 | parser = Parser(""" 18 | SCHEMA test; 19 | 20 | TYPE test = REAL; 21 | END_TYPE; 22 | 23 | TYPE day_in_week_number = INTEGER; 24 | WHERE 1 <= SELF; 25 | END_TYPE; -- day_in_week_number 26 | 27 | END_SCHEMA; 28 | """) 29 | tree = parser.schema() 30 | assert tree is not None 31 | 32 | 33 | def test_function(): 34 | parser = Parser(""" 35 | SCHEMA test; 36 | 37 | FUNCTION IfcConsecutiveSegments 38 | (Segments : LIST [1:?] OF IfcSegmentIndexSelect) 39 | : BOOLEAN; 40 | 41 | LOCAL 42 | Result : BOOLEAN := TRUE; 43 | END_LOCAL; 44 | 45 | REPEAT i := 1 TO (HIINDEX(Segments)-1); 46 | IF Segments[i][HIINDEX(Segments[i])] <> Segments[i+1][1] THEN 47 | BEGIN 48 | Result := FALSE; 49 | ESCAPE; 50 | END; 51 | END_IF; 52 | END_REPEAT; 53 | 54 | RETURN (Result); 55 | END_FUNCTION; 56 | 57 | END_SCHEMA; 58 | """) 59 | tree = parser.schema() 60 | assert tree is not None 61 | 62 | 63 | if __name__ == '__main__': 64 | pytest.main([__file__]) 65 | -------------------------------------------------------------------------------- /tests/express/test_ast.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2020 Manfred Moitzi 2 | # License: MIT License 3 | import pytest 4 | from steputils.express import ast 5 | 6 | 7 | def test_ast_node(): 8 | root = ast.AST('TEST', (1, 2, "a")) 9 | assert list(root) == [1, 2, 'a'] 10 | assert root.name == 'TEST' 11 | assert root[0] == 1 12 | assert root[2] == 'a' 13 | assert repr(root) == "(TEST, 1, 2, 'a')" 14 | 15 | 16 | if __name__ == '__main__': 17 | pytest.main([__file__]) 18 | -------------------------------------------------------------------------------- /tests/express/test_constant_decl.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2020 Manfred Moitzi 2 | # License: MIT License 3 | 4 | import pytest 5 | from steputils.express.pyparser import constant_decl, Tokens 6 | 7 | 8 | def test_typedef_real(): 9 | c = Tokens(constant_decl.parseString(""" 10 | CONSTANT 11 | dummy_gri : geometric_representation_item := representation_item('') || 12 | geometric_representation_item(); 13 | dummy_tri : topological_representation_item := representation_item('') 14 | || topological_representation_item(); 15 | END_CONSTANT; 16 | """)) 17 | 18 | assert str(c) == "CONSTANT dummy_gri : geometric_representation_item := representation_item ( ) || " \ 19 | "geometric_representation_item ( ) ; " \ 20 | "dummy_tri : topological_representation_item := representation_item ( ) || " \ 21 | "topological_representation_item ( ) ; " \ 22 | "END_CONSTANT ;" 23 | 24 | 25 | if __name__ == '__main__': 26 | pytest.main([__file__]) 27 | -------------------------------------------------------------------------------- /tests/express/test_entity.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2020 Manfred Moitzi 2 | # License: MIT License 3 | import pytest 4 | from steputils.express.pyparser import entity_decl, supertype_constraint, one_of, supertype_rule, Tokens 5 | 6 | 7 | def test_simple_entity_decl(): 8 | e = Tokens(entity_decl.parseString(""" 9 | ENTITY action; 10 | name : label; 11 | description : text; 12 | chosen_method : action_method; 13 | END_ENTITY; -- action 14 | """)) 15 | assert str(e) == "ENTITY action ; name : label ; description : text ; chosen_method : action_method ; END_ENTITY ;" 16 | 17 | 18 | def test_simple_entity_decl_2(): 19 | e = Tokens(entity_decl.parseString(""" 20 | ENTITY IfcActor 21 | SUPERTYPE OF (ONEOF(IfcOccupant)) 22 | SUBTYPE OF (IfcObject); 23 | TheActor : IfcActorSelect; 24 | INVERSE IsActingUpon : SET [0:?] OF IfcRelAssignsToActor FOR RelatingActor; 25 | END_ENTITY; 26 | """)) 27 | assert str(e) == "ENTITY IfcActor SUPERTYPE OF ( ONEOF ( IfcOccupant ) ) " \ 28 | "SUBTYPE OF ( IfcObject ) ; " \ 29 | "TheActor : IfcActorSelect ; " \ 30 | "INVERSE IsActingUpon : SET [ 0 : ? ] OF IfcRelAssignsToActor FOR RelatingActor ; " \ 31 | "END_ENTITY ;" 32 | 33 | 34 | """ 35 | ENTITY IfcActorRole; 36 | Role : IfcRoleEnum; 37 | UserDefinedRole : OPTIONAL IfcLabel; 38 | Description : OPTIONAL IfcText; 39 | INVERSE 40 | HasExternalReference : SET [0:?] OF IfcExternalReferenceRelationship FOR RelatedResourceObjects; 41 | WHERE 42 | WR1 : (Role <> IfcRoleEnum.USERDEFINED) OR 43 | ((Role = IfcRoleEnum.USERDEFINED) AND EXISTS(SELF.UserDefinedRole)); 44 | END_ENTITY; 45 | """ 46 | 47 | 48 | def test_simple_entity_decl_3(): 49 | e = Tokens(entity_decl.parseString(""" 50 | ENTITY IfcActorRole; 51 | Role : IfcRoleEnum; 52 | UserDefinedRole : OPTIONAL IfcLabel; 53 | Description : OPTIONAL IfcText; 54 | INVERSE 55 | HasExternalReference : SET [0:?] OF IfcExternalReferenceRelationship FOR RelatedResourceObjects; 56 | WHERE SELF > 0; 57 | END_ENTITY; 58 | """)) 59 | assert len(e) == 38 60 | 61 | 62 | def test_simple_entity_decl_4(): 63 | e = Tokens(entity_decl.parseString(""" 64 | ENTITY IfcAddress 65 | ABSTRACT SUPERTYPE OF (ONEOF 66 | (IfcPostalAddress 67 | ,IfcTelecomAddress)); 68 | Purpose : OPTIONAL IfcAddressTypeEnum; 69 | Description : OPTIONAL IfcText; 70 | UserDefinedPurpose : OPTIONAL IfcLabel; 71 | INVERSE 72 | OfPerson : SET [0:?] OF IfcPerson FOR Addresses; 73 | OfOrganization : SET [0:?] OF IfcOrganization FOR Addresses; 74 | WHERE 75 | WR1 : (NOT(EXISTS(Purpose))) OR 76 | ((Purpose <> IfcAddressTypeEnum.USERDEFINED) OR 77 | ((Purpose = IfcAddressTypeEnum.USERDEFINED) AND 78 | EXISTS(SELF.UserDefinedPurpose))); 79 | END_ENTITY; 80 | """)) 81 | assert len(e) == 98 82 | 83 | 84 | def test_simple_entity_decl_5(): 85 | e = Tokens(entity_decl.parseString(r""" 86 | ENTITY IfcAdvancedBrep 87 | SUPERTYPE OF (ONEOF 88 | (IfcAdvancedBrepWithVoids)) 89 | SUBTYPE OF (IfcManifoldSolidBrep); 90 | WHERE 91 | HasAdvancedFaces : SIZEOF(QUERY(Afs <* SELF\IfcManifoldSolidBrep.Outer.CfsFaces | 92 | (NOT ('IFC4X2.IFCADVANCEDFACE' IN TYPEOF(Afs))) 93 | )) = 0; 94 | END_ENTITY; 95 | """)) 96 | assert len(e) == 51 97 | 98 | 99 | def test_simple_entity_decl_6(): 100 | e = Tokens(entity_decl.parseString(r""" 101 | ENTITY IfcAdvancedBrepWithVoids 102 | SUBTYPE OF (IfcAdvancedBrep); 103 | Voids : SET [1:?] OF IfcClosedShell; 104 | WHERE 105 | VoidsHaveAdvancedFaces : SIZEOF (QUERY (Vsh <* Voids | 106 | SIZEOF (QUERY (Afs <* Vsh.CfsFaces | 107 | (NOT ('IFC4X2.IFCADVANCEDFACE' IN TYPEOF(Afs))) 108 | )) = 0 109 | )) = 0; 110 | END_ENTITY; 111 | """)) 112 | assert len(e) == 62 113 | 114 | 115 | def test_one_of(): 116 | r = Tokens(one_of.parseString("ONEOF(IfcOccupant)")) 117 | assert str(r) == "ONEOF ( IfcOccupant )" 118 | 119 | 120 | def test_supertype_rule(): 121 | s = Tokens(supertype_rule.parseString("SUPERTYPE OF (ONEOF(IfcOccupant))")) 122 | assert str(s) == "SUPERTYPE OF ( ONEOF ( IfcOccupant ) )" 123 | 124 | 125 | def test_supertype_constraint(): 126 | s = Tokens(supertype_constraint.parseString("SUPERTYPE OF (ONEOF(IfcOccupant))")) 127 | assert str(s) == "SUPERTYPE OF ( ONEOF ( IfcOccupant ) )" 128 | 129 | 130 | if __name__ == '__main__': 131 | pytest.main([__file__]) 132 | -------------------------------------------------------------------------------- /tests/express/test_parse_actions.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from steputils.express import pyparser, ast 4 | primary = pyparser.primary.copy().addParseAction(ast.Primary.action) 5 | bound_spec = pyparser.bound_spec.copy().addParseAction(ast.BoundSpec.action) 6 | 7 | 8 | def test_bound_spec(): 9 | r = bound_spec.parseString('[3:3]')[0] 10 | assert len(r) == 2 11 | bound_1 = r[0] 12 | bound_2 = r[1] 13 | assert bound_1 == 3 14 | assert bound_2 == 3 15 | assert repr(r) == '(BoundSpec, 3, 3)' 16 | 17 | 18 | def test_primary(): 19 | r = primary.parseString('SELF[1]')[0] 20 | assert repr(r) == "(Primary, 'SELF', '[', 1, ']')" 21 | 22 | r = primary.parseString('1')[0] 23 | assert type(r) is int 24 | 25 | r = primary.parseString('1.0')[0] 26 | assert type(r) is float 27 | 28 | r = primary.parseString("'s'")[0] 29 | assert type(r) is ast.StringLiteral 30 | 31 | 32 | if __name__ == '__main__': 33 | pytest.main([__file__]) 34 | 35 | -------------------------------------------------------------------------------- /tests/express/test_simple_expressions.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2020 Manfred Moitzi 2 | # License: MIT License 3 | 4 | import pytest 5 | 6 | from steputils.express.pyparser import ( 7 | list_type, bound_spec, array_type, index_qualifier, simple_expression, string_literal, 8 | aggregate_initializer, real_literal, interval, entity_constructor, primary, 9 | simple_factor, expression, integer_literal, ast, enumeration_type, underlying_type, 10 | select_type, comments, tail_remark, query_expression, aggregate_source_, qualified_attribute, 11 | attribute_qualifier, 12 | Tokens) 13 | 14 | 15 | def test_bound_spec(): 16 | r = Tokens(bound_spec.parseString('[3:3]')) 17 | assert str(r) == '[ 3 : 3 ]' 18 | 19 | 20 | def test_list(): 21 | r = Tokens(list_type.parseString('LIST [3:3] OF IfcPositiveInteger')) 22 | assert str(r) == 'LIST [ 3 : 3 ] OF IfcPositiveInteger' 23 | 24 | 25 | def test_string_literal(): 26 | r = Tokens(string_literal.parseString("'test'")) 27 | assert type(r[0]) is ast.StringLiteral 28 | assert r[0] == 'test' 29 | 30 | 31 | def test_simple_expression(): 32 | r = Tokens(simple_expression.parseString('SELF')) 33 | assert str(r) == 'SELF' 34 | 35 | r = Tokens(simple_expression.parseString('1')) 36 | assert r[0] == 1 37 | 38 | 39 | def test_simple_expression_as_function_call(): 40 | r = Tokens(simple_expression.parseString("ABS(100)")) 41 | assert str(r) == "ABS ( 100 )" 42 | r = Tokens(simple_expression.parseString("ABS(SELF[2])")) 43 | assert str(r) == "ABS ( SELF [ 2 ] )" 44 | 45 | 46 | def test_expression_as_function_call(): 47 | r = Tokens(expression.parseString("ABS(SELF[2])")) 48 | assert str(r) == "ABS ( SELF [ 2 ] )" 49 | 50 | 51 | def test_simple_factor_as_function_call(): 52 | r = Tokens(simple_factor.parseString("ABS(SELF[2])")) 53 | assert str(r) == "ABS ( SELF [ 2 ] )" 54 | 55 | 56 | def test_primary_as_function_call(): 57 | r = Tokens(primary.parseString("ABS(SELF[2])")) 58 | assert str(r) == "ABS ( SELF [ 2 ] )" 59 | 60 | 61 | def test_primary(): 62 | r = Tokens(primary.parseString('SELF[1]')) 63 | assert str(r) == 'SELF [ 1 ]' 64 | 65 | 66 | def test_aggregate_init(): 67 | assert Tokens(aggregate_initializer.parseString("[]")) == ['[', ']'] 68 | r = Tokens(aggregate_initializer.parseString("['test', 100]")) 69 | assert r[0] == '[' 70 | assert type(r[1]) is ast.StringLiteral 71 | assert r[1] == 'test' 72 | assert r[2] == ',' 73 | assert r[3] == 100 74 | assert type(r[3]) is int 75 | assert r[4] == ']' 76 | 77 | 78 | def test_enumeration(): 79 | e = Tokens(enumeration_type.parseString("ENUMERATION OF (EMAIL, FAX ,PHONE ,POST,VERBAL,USERDEFINED,NOTDEFINED);")) 80 | assert str(e) == "ENUMERATION OF ( EMAIL , FAX , PHONE , POST , VERBAL , USERDEFINED , NOTDEFINED )" 81 | 82 | 83 | def test_underlying_type_enum(): 84 | e = Tokens(underlying_type.parseString("ENUMERATION OF (EMAIL, FAX ,PHONE ,POST,VERBAL,USERDEFINED,NOTDEFINED);")) 85 | assert str(e) == "ENUMERATION OF ( EMAIL , FAX , PHONE , POST , VERBAL , USERDEFINED , NOTDEFINED )" 86 | 87 | 88 | def test_select(): 89 | t = Tokens(select_type.parseString("SELECT (IfcDerivedMeasureValue, IfcMeasureValue, IfcSimpleValue)")) 90 | assert str(t) == "SELECT ( IfcDerivedMeasureValue , IfcMeasureValue , IfcSimpleValue )" 91 | 92 | 93 | def test_array(): 94 | r = Tokens(array_type.parseString('ARRAY [1:2] OF REAL')) 95 | assert str(r) == 'ARRAY [ 1 : 2 ] OF REAL' 96 | 97 | 98 | def test_integer_literal(): 99 | assert Tokens(integer_literal.parseString('1'))[0] == 1 100 | assert Tokens(integer_literal.parseString('-2'))[0] == -2 101 | assert Tokens(integer_literal.parseString('+10000'))[0] == 10000 102 | 103 | 104 | def test_type_real(): 105 | assert Tokens(real_literal.parseString('1.0'))[0] == 1.0 106 | assert Tokens(real_literal.parseString('0.'))[0] == 0. 107 | assert Tokens(real_literal.parseString('-1.0'))[0] == -1.0 108 | assert Tokens(real_literal.parseString('-1.0e-5'))[0] == -1e-5 109 | 110 | 111 | def test_index_qualifier(): 112 | r = Tokens(index_qualifier.parseString('[1]')) 113 | assert r == ['[', '1', ']'] 114 | r = Tokens(index_qualifier.parseString('[ 1 : 2+ 3]')) 115 | assert str(r) == '[ 1 : 2 + 3 ]' 116 | 117 | 118 | def test_interval(): 119 | r = Tokens(interval.parseString('{1 < x < 2}')) 120 | assert str(r) == '{ 1 < x < 2 }' 121 | r = Tokens(interval.parseString('{(1+1) < x < (2+2)}')) 122 | assert str(r) == '{ ( 1 + 1 ) < x < ( 2 + 2 ) }' 123 | 124 | 125 | def test_entity_constructor(): 126 | r = Tokens(entity_constructor.parseString("test()")) 127 | assert str(r) == "test ( )" 128 | r = Tokens(entity_constructor.parseString("ABS(100)")) 129 | assert str(r) == "ABS ( 100 )" 130 | r = Tokens(entity_constructor.parseString("ABS(SELF)")) 131 | assert str(r) == "ABS ( SELF )" 132 | 133 | 134 | def test_simple_factor(): 135 | r = Tokens(simple_factor.parseString("test()")) 136 | assert str(r) == "test ( )" 137 | r = Tokens(simple_factor.parseString("ABS(100)")) 138 | assert str(r) == "ABS ( 100 )" 139 | r = Tokens(simple_factor.parseString("ABS(SELF)")) 140 | assert str(r) == "ABS ( SELF )" 141 | r = Tokens(simple_factor.parseString("ABS(SELF[2])")) 142 | assert str(r) == "ABS ( SELF [ 2 ] )" 143 | 144 | 145 | def test_sizeof_expr(): 146 | r = Tokens(expression.parseString(r"SIZEOF(a)")) 147 | assert str(r) == "SIZEOF ( a )" 148 | 149 | 150 | def test_aggregate_source(): 151 | r = Tokens(aggregate_source_.parseString("AAA")) 152 | assert str(r) == "AAA" 153 | r = Tokens(aggregate_source_.parseString("AAA.bbb")) 154 | assert str(r) == "AAA . bbb" 155 | r = Tokens(aggregate_source_.parseString("AAA.bbb.ccc")) 156 | assert str(r) == "AAA . bbb . ccc" 157 | r = Tokens(aggregate_source_.parseString(r"SELF\AAA.bbb")) 158 | assert str(r) == r"SELF \ AAA . bbb" 159 | r = Tokens(aggregate_source_.parseString(r"SELF\AAA.bbb.ccc")) 160 | assert str(r) == r"SELF \ AAA . bbb . ccc" 161 | 162 | 163 | def test_query_expr(): 164 | r = Tokens(query_expression.parseString("QUERY(a <* b | 1 = 1)")) 165 | assert str(r) == "QUERY ( a <* b | 1 = 1 )" 166 | 167 | 168 | def test_attr_qualifier(): 169 | r = Tokens(attribute_qualifier.parseString(".Outer.CfsFaces")) 170 | assert str(r) == ". Outer . CfsFaces" 171 | 172 | 173 | def test_qualified_attr(): 174 | r = Tokens(qualified_attribute.parseString(r"SELF\IfcManifoldSolidBrep.Outer.CfsFaces")) 175 | assert str(r) == r"SELF \ IfcManifoldSolidBrep . Outer . CfsFaces" 176 | 177 | 178 | def test_query_expr_2(): 179 | r = Tokens(query_expression.parseString(r"QUERY(Afs <* SELF\IfcManifoldSolidBrep.Outer.CfsFaces | " 180 | r"(NOT ('IFC4X2.IFCADVANCEDFACE' IN TYPEOF(Afs))))")) 181 | assert str(r) == r"QUERY ( Afs <* SELF \ IfcManifoldSolidBrep . Outer . CfsFaces | " \ 182 | r"( NOT ( IFC4X2.IFCADVANCEDFACE IN TYPEOF ( Afs ) ) ) )" 183 | 184 | 185 | def test_query_expr_3(): 186 | r = Tokens(query_expression.parseString("QUERY (Vsh <* Voids | SIZEOF (QUERY (Afs <* Vsh.CfsFaces | " 187 | "(NOT ('IFC4X2.IFCADVANCEDFACE' IN TYPEOF(Afs))) )) = 0))")) 188 | assert str(r) == "QUERY ( Vsh <* Voids | SIZEOF ( QUERY ( Afs <* Vsh . CfsFaces | ( NOT ( IFC4X2.IFCADVANCEDFACE IN " \ 189 | "TYPEOF ( Afs ) ) ) ) ) = 0 )" 190 | 191 | 192 | def test_complex_expr_1(): 193 | r = Tokens(expression.parseString(r"SIZEOF(a) = 0")) 194 | assert str(r) == r"SIZEOF ( a ) = 0" 195 | 196 | 197 | def test_complex_expr_2(): 198 | r = Tokens(expression.parseString(r"SIZEOF(QUERY(Afs <* SELF\IfcManifoldSolidBrep.Outer.CfsFaces | " 199 | r"(NOT ('IFC4X2.IFCADVANCEDFACE' IN TYPEOF(Afs))))) = 0")) 200 | assert str(r) == r"SIZEOF ( QUERY ( Afs <* SELF \ IfcManifoldSolidBrep . Outer . CfsFaces | " \ 201 | r"( NOT ( IFC4X2.IFCADVANCEDFACE IN TYPEOF ( Afs ) ) ) ) ) = 0" 202 | 203 | 204 | def test_comments(): 205 | assert str(Tokens(comments.parseString("(* comment *)"))) == '(* comment *)' 206 | assert str(Tokens(comments.parseString("(* comment * *)"))) == '(* comment * *)' 207 | assert str(Tokens(comments.parseString("(** comment **)"))) == '(** comment **)' 208 | assert str(Tokens(comments.parseString("(** (* comment **)"))) == '(** (* comment **)' 209 | 210 | 211 | def test_tail_remark(): 212 | assert str(Tokens(tail_remark.parseString("-- approved_item \n"))) == '-- approved_item' 213 | assert str(Tokens(tail_remark.parseString("-- approved_item.test \n next line"))) == '-- approved_item . test' 214 | 215 | 216 | def test_ignore_tail_remark(): 217 | expr_test = simple_expression('TEST') 218 | expr_test.ignore(tail_remark) 219 | assert str(Tokens(expr_test.parseString(" 1 + 1 -- approved_item \n xxx"))) == '1 + 1' 220 | assert str(Tokens(expr_test.parseString(" 1 + 1 -- approved_item simple.name \n END_TYPE;"))) == '1 + 1' 221 | 222 | 223 | if __name__ == '__main__': 224 | pytest.main([__file__]) 225 | -------------------------------------------------------------------------------- /tests/express/test_type_decl.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2020 Manfred Moitzi 2 | # License: MIT License 3 | 4 | import pytest 5 | from steputils.express.pyparser import type_decl, where_clause, Tokens 6 | 7 | 8 | def test_typedef_real(): 9 | r = Tokens(type_decl.parseString('TYPE IfcAbsorbedDoseMeasure = REAL;END_TYPE;')) 10 | assert str(r) == 'TYPE IfcAbsorbedDoseMeasure = REAL ; END_TYPE ;' 11 | 12 | 13 | def test_typedef_list(): 14 | r = Tokens(type_decl.parseString('TYPE IfcArcIndex = LIST [3:3] OF IfcPositiveInteger;END_TYPE;')) 15 | assert str(r) == 'TYPE IfcArcIndex = LIST [ 3 : 3 ] OF IfcPositiveInteger ; END_TYPE ;' 16 | 17 | 18 | def test_typedef_enum(): 19 | t = Tokens(type_decl.parseString(""" 20 | TYPE IfcActionRequestTypeEnum = ENUMERATION OF 21 | (EMAIL, 22 | FAX, 23 | PHONE, 24 | POST, 25 | VERBAL, 26 | USERDEFINED, 27 | NOTDEFINED); 28 | END_TYPE; 29 | """)) 30 | assert str(t) == "TYPE IfcActionRequestTypeEnum = ENUMERATION OF " \ 31 | "( EMAIL , FAX , PHONE , POST , VERBAL , USERDEFINED , NOTDEFINED ) ; " \ 32 | "END_TYPE ;" 33 | 34 | 35 | def test_typedef_select(): 36 | t = Tokens(type_decl.parseString(""" 37 | TYPE IfcValue = SELECT ( 38 | IfcDerivedMeasureValue, 39 | IfcMeasureValue, 40 | IfcSimpleValue); 41 | END_TYPE; 42 | """)) 43 | assert str(t) == "TYPE IfcValue = SELECT ( IfcDerivedMeasureValue , IfcMeasureValue , IfcSimpleValue ) ; END_TYPE ;" 44 | 45 | 46 | def test_where_clause_0(): 47 | r = Tokens(where_clause.parseString("WHERE SELF > 0;")) 48 | assert str(r) == "WHERE SELF > 0 ;" 49 | 50 | 51 | def test_where_clause_1(): 52 | r = Tokens(where_clause.parseString("WHERE GreaterThanZero : SELF > 0;")) 53 | assert str(r) == "WHERE GreaterThanZero : SELF > 0 ;" 54 | 55 | 56 | def test_where_clause_2(): 57 | r = Tokens(where_clause.parseString(" WHERE SELF IN ['left', 'middle'];")) 58 | assert str(r) == "WHERE SELF IN [ left , middle ] ;" 59 | r = Tokens(where_clause.parseString(" WHERE WR1 : SELF IN ['left', 'middle'];")) 60 | assert str(r) == "WHERE WR1 : SELF IN [ left , middle ] ;" 61 | 62 | 63 | def test_where_clause_3(): 64 | r = Tokens(where_clause.parseString("WHERE SELF > 0; SELF < 2;")) 65 | assert str(r) == "WHERE SELF > 0 ; SELF < 2 ;" 66 | 67 | 68 | def test_where_clause_4(): 69 | r = Tokens(where_clause.parseString("WHERE SELF > 0; SELF < 2; END_TYPE;")) 70 | assert str(r) == "WHERE SELF > 0 ; SELF < 2 ;", 'should ignore: END_TYPE;' 71 | 72 | 73 | def test_where_clause_5(): 74 | r = Tokens(where_clause.parseString("WHERE MinutesInRange : ABS(SELF[2]) < 60;")) 75 | assert str(r) == "WHERE MinutesInRange : ABS ( SELF [ 2 ] ) < 60 ;" 76 | 77 | 78 | def test_where_clause_6(): 79 | r = Tokens(where_clause.parseString("WHERE WR1 : (Role <> IfcRoleEnum.USERDEFINED) OR " 80 | " ((Role = IfcRoleEnum.USERDEFINED) AND EXISTS(SELF.UserDefinedRole));")) 81 | assert str(r) == "WHERE WR1 : ( Role <> IfcRoleEnum . USERDEFINED ) OR ( ( Role = IfcRoleEnum . USERDEFINED ) AND " \ 82 | "EXISTS ( SELF . UserDefinedRole ) ) ;" 83 | 84 | 85 | def test_where_clause_7(): 86 | r = Tokens(where_clause.parseString( 87 | r"WHERE HasAdvancedFaces : SIZEOF(QUERY(Afs <* SELF\IfcManifoldSolidBrep.Outer.CfsFaces | " 88 | r"(NOT ('IFC4X2.IFCADVANCEDFACE' IN TYPEOF(Afs))))) = 0;" 89 | )) 90 | assert str(r) == r"WHERE HasAdvancedFaces : SIZEOF ( QUERY ( Afs <* SELF \ IfcManifoldSolidBrep . Outer . CfsFaces | " \ 91 | "( NOT ( IFC4X2.IFCADVANCEDFACE IN TYPEOF ( Afs ) ) ) ) ) = 0 ;" 92 | 93 | 94 | def test_where_rule_0(): 95 | r = Tokens(type_decl.parseString("TYPE XType = INTEGER; WHERE SELF > 0; END_TYPE;")) 96 | assert str(r) == "TYPE XType = INTEGER ; WHERE SELF > 0 ; END_TYPE ;" 97 | 98 | 99 | def test_where_rule_1(): 100 | r = Tokens(type_decl.parseString(""" 101 | TYPE IfcCardinalPointReference = INTEGER; 102 | WHERE 103 | GreaterThanZero : SELF > 0; 104 | END_TYPE; 105 | """)) 106 | 107 | assert str(r) == "TYPE IfcCardinalPointReference = INTEGER ; WHERE GreaterThanZero : SELF > 0 ; END_TYPE ;" 108 | 109 | 110 | def test_where_rule_2(): 111 | r = Tokens(type_decl.parseString(""" 112 | TYPE IfcCompoundPlaneAngleMeasure = LIST [3:4] OF INTEGER; 113 | WHERE 114 | MinutesInRange : ABS(SELF[2]) < 60; 115 | SecondsInRange : ABS(SELF[3]) < 60; 116 | MicrosecondsInRange : (SIZEOF(SELF) = 3) OR (ABS(SELF[4]) < 1000000); 117 | ConsistentSign : ((SELF[1] >= 0) AND (SELF[2] >= 0) AND (SELF[3] >= 0) AND ((SIZEOF(SELF) = 3) OR (SELF[4] >= 0))) 118 | OR 119 | ((SELF[1] <= 0) AND (SELF[2] <= 0) AND (SELF[3] <= 0) AND ((SIZEOF(SELF) = 3) OR (SELF[4] <= 0))); 120 | END_TYPE; 121 | """)) 122 | assert len(r) == 162 123 | 124 | 125 | if __name__ == '__main__': 126 | pytest.main([__file__]) 127 | -------------------------------------------------------------------------------- /tests/p21/test_p21_api.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2019-2022 Manfred Moitzi 2 | # License: MIT License 3 | import pytest 4 | from io import StringIO 5 | 6 | from steputils import p21 7 | 8 | 9 | @pytest.fixture 10 | def stpfile(): 11 | stp = p21.new_step_file() 12 | timestamp = p21.timestamp() 13 | stp.header.set_file_description(('notes1', 'notes2')) 14 | stp.header.set_file_name('test.stp', timestamp) 15 | section = stp.new_data_section() 16 | section.add(p21.simple_entity_instance('#100', p21.entity('TEST', (1, 2, 3)))) 17 | section.add(p21.simple_entity_instance('#1', p21.entity('TEST', (3, 2, 1)))) 18 | stp.new_data_section(name='SEC1', schema='IFC2X3') 19 | return stp 20 | 21 | 22 | def test_has_reference(stpfile): 23 | assert stpfile.has_reference('#100') is True 24 | assert stpfile.has_reference('#1') is True 25 | assert stpfile.has_reference('#2') is False 26 | 27 | 28 | def test_new_named_data_section(): 29 | stp = p21.new_step_file() 30 | stp.new_data_section() 31 | assert len(stp.data) == 1 32 | stp.new_data_section('WithName', 'Schema') 33 | assert len(stp.data) == 2 34 | with pytest.raises(ValueError): 35 | # A named data section requires a file schema 36 | stp.new_data_section('WithName') 37 | 38 | 39 | def test_iter_protocol(stpfile): 40 | result = list(stpfile) 41 | assert len(result) == 2 42 | assert p21.is_simple_entity_instance(result[0]) 43 | 44 | 45 | def test_step_file_getter(stpfile): 46 | assert stpfile['#100'].ref == '#100' 47 | assert stpfile['#1'].ref == '#1' 48 | 49 | 50 | def test_step_file_delete_entity_instance_by_ref(stpfile): 51 | assert stpfile['#100'].ref == '#100' 52 | del stpfile['#100'] 53 | with pytest.raises(KeyError): 54 | stpfile['#100'] 55 | 56 | 57 | def test_len(stpfile): 58 | assert len(stpfile) == 2 59 | 60 | 61 | def test_header(stpfile): 62 | stpfile._set_schemas() 63 | timestamp = stpfile.header['FILE_NAME'].params[1] 64 | fp = StringIO() 65 | stpfile.header.write(fp) 66 | result = fp.getvalue().split('\n') 67 | assert result[0] == "HEADER;" 68 | assert result[1] == "FILE_DESCRIPTION(('notes1','notes2'),'2;1');" 69 | assert result[2] == f"FILE_NAME('test.stp','{timestamp}',(''),(''),'','','');" 70 | assert result[3] == "FILE_SCHEMA(('IFC2X3'));" 71 | assert result[4] == "ENDSEC;" 72 | 73 | 74 | def test_data_section_1(stpfile): 75 | fp = StringIO() 76 | stpfile.data[0].write(fp) 77 | result = fp.getvalue().split('\n') 78 | assert result[0] == 'DATA;' 79 | assert result[1] == "#100=TEST(1,2,3);" 80 | assert result[2] == "#1=TEST(3,2,1);" 81 | assert result[-2] == 'ENDSEC;' 82 | 83 | 84 | def test_data_section_2(stpfile): 85 | fp = StringIO() 86 | stpfile.data[1].write(fp) 87 | result = fp.getvalue().split('\n') 88 | assert result[0] == "DATA('SEC1',('IFC2X3'));" 89 | assert result[-2] == 'ENDSEC;' 90 | 91 | 92 | def test_iso_10303_21_marker(stpfile): 93 | result = str(stpfile).split('\n') 94 | assert result[0] == 'ISO-10303-21;' 95 | # StingIO() last '' marks ends of file 96 | assert result[-2] == 'END-ISO-10303-21;' 97 | 98 | 99 | def test_creation_of_file_schema_entry(stpfile): 100 | assert 'FILE_SCHEMA' not in stpfile.header 101 | # FILE_SCHEMA will be created automatically if not defined by user, but is ('NONE') 102 | # if data sections have no schema attribute. 103 | stpfile._set_schemas() 104 | entry = stpfile.header['FILE_SCHEMA'] 105 | assert entry.params[0] == ('IFC2X3',) 106 | 107 | 108 | if __name__ == '__main__': 109 | pytest.main([__file__]) 110 | -------------------------------------------------------------------------------- /tests/p21/test_p21_factory.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from steputils import p21 4 | from steputils.p21 import parameter_string 5 | 6 | 7 | def test_enum(): 8 | enum = p21.enum('.ENUM.') 9 | assert p21.is_enum(enum) is True 10 | assert p21.is_enum('.ENUM.') is False, 'Enumerations have to be typed.' 11 | 12 | 13 | def test_unset_param(): 14 | assert p21.unset_parameter('*') == '*' 15 | assert p21.unset_parameter('$') == '$' 16 | pytest.raises(ValueError, p21.unset_parameter, '#') 17 | assert p21.is_unset_parameter(p21.unset_parameter('*')) is True 18 | assert p21.is_unset_parameter(p21.unset_parameter('$')) is True 19 | assert p21.is_unset_parameter('*') is False, 'Unset parameters have to typed.' 20 | 21 | 22 | def test_parameter_list(): 23 | assert p21.parameter_list(1, 2, 'hello') == (1, 2, 'hello') 24 | assert str(p21.parameter_list(1, 2, 'hello')) == "(1,2,'hello')" 25 | assert p21.is_parameter_list(p21.parameter_list(1, 2, 'hello')) is True 26 | assert p21.is_parameter_list((1, 2, 3)) is False, 'Parameter lists has to be typed.' 27 | 28 | 29 | def test_binary(): 30 | assert p21.binary(12).value == 12 31 | assert str(p21.binary(12)) == '"0C"' 32 | assert p21.is_binary(p21.binary(12)) is True 33 | b = p21.binary(12, 3) 34 | assert b.value == 12 35 | assert b.unused == 3 36 | 37 | 38 | def test_keyword(): 39 | assert p21.keyword('TEST') == 'TEST' 40 | assert p21.is_keyword(p21.keyword('TEST')) is True 41 | assert p21.is_keyword('TEST') is False, 'Keywords have to be typed.' 42 | # user defined keyword 43 | assert p21.is_keyword(p21.keyword('!TEST')) is True 44 | pytest.raises(ValueError, p21.keyword, 'TEST.') 45 | 46 | 47 | def test_typed_parameter(): 48 | assert str(p21.typed_parameter('TEST', 1)) == 'TEST(1)' 49 | assert p21.is_typed_parameter(p21.typed_parameter('TEST', 1)) is True 50 | tp = p21.typed_parameter('TEST', 1) 51 | assert tp.type_name == 'TEST' 52 | assert tp.param == 1 53 | 54 | 55 | def test_reference(): 56 | assert p21.reference('#100') == '#100' 57 | assert p21.is_reference(p21.reference('#100')) is True 58 | assert p21.is_reference('#100') is False, 'References have to be typed.' 59 | pytest.raises(ValueError, p21.reference, '100') 60 | 61 | 62 | def test_entity(): 63 | e = p21.entity('TEST', (1, 2, 'hello')) 64 | assert e.name == 'TEST' 65 | assert p21.is_parameter_list(e.params) is True 66 | assert e.params == (1, 2, 'hello') 67 | assert str(e) == "TEST(1,2,'hello')" 68 | 69 | 70 | def test_simple_entity_instance(): 71 | instance = p21.simple_instance('#100', 'TEST', (1, 2, 3)) 72 | assert instance.ref == '#100' 73 | assert instance.entity.name == 'TEST' 74 | assert instance.entity.params == (1, 2, 3) 75 | assert p21.is_simple_entity_instance(instance) is True 76 | assert str(instance) == "#100=TEST(1,2,3);\n" 77 | 78 | 79 | def test_complex_entity_instance(): 80 | instance = p21.complex_entity_instance('#100', [ 81 | p21.entity('TEST', (1, 2, 'hello')), 82 | p21.entity('TEST2', (3, 4, 'greetings')), 83 | ]) 84 | assert instance.ref == '#100' 85 | assert instance.entities[0].name == 'TEST' 86 | assert instance.entities[1].name == 'TEST2' 87 | assert str(instance) == "#100=(TEST(1,2,'hello')TEST2(3,4,'greetings'));\n" 88 | 89 | 90 | def test_parameter_to_string(): 91 | assert parameter_string(p21.unset_parameter('*')) == "*" 92 | # Untyped strings will always be quoted!!! 93 | assert parameter_string('*') == "'*'" 94 | assert parameter_string(p21.unset_parameter('$')) == "$" 95 | assert parameter_string(None) == "$", 'None should be unset parameter' 96 | assert parameter_string(p21.keyword('KEY')) == "KEY" 97 | assert parameter_string(p21.enum('.ENUM.')) == ".ENUM." 98 | assert parameter_string(p21.reference('#100')) == "#100" 99 | assert parameter_string(p21.typed_parameter('TYPE', 12)) == "TYPE(12)" 100 | assert parameter_string(p21.typed_parameter('TYPE', 'hello')) == "TYPE('hello')" 101 | assert parameter_string('simple string') == "'simple string'" 102 | assert parameter_string(123) == "123" 103 | assert parameter_string(1.23) == "1.23" 104 | 105 | 106 | def test_parameter_list_to_string(): 107 | assert parameter_string((123, 456) == "(123,456)") 108 | assert parameter_string([123, 456] == "(123,456)") 109 | assert parameter_string(p21.parameter_list([123, 456]) == "(123,456)") 110 | assert parameter_string((123, (456, (789, '10')))) == "(123,(456,(789,'10')))" 111 | assert parameter_string((123, None, 456)) == "(123,$,456)", 'None should be unset parameter' 112 | 113 | 114 | if __name__ == '__main__': 115 | pytest.main([__file__]) 116 | -------------------------------------------------------------------------------- /tests/p21/test_p21_lexer.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2020 Manfred Moitzi 2 | # License: MIT License 3 | 4 | import pytest 5 | from steputils import p21 6 | 7 | 8 | def test_lexer_skip_whitespace(): 9 | l = p21.Lexer(' ; \n ; ') 10 | assert list(l) == [';', ';'] 11 | assert l.line_number == 2 12 | 13 | 14 | def test_lexer_skip_comments(): 15 | l = p21.Lexer(' ;\n/* xbyb();.,''""\\\\ */;') 16 | assert list(l) == [';', ';'] 17 | assert l.line_number == 2 18 | 19 | 20 | def test_lexer_skip_comments_nl(): 21 | l = p21.Lexer(' ;\n/* xbyb();.,\n''""\\\\ */;') 22 | assert list(l) == [';', ';'] 23 | assert l.line_number == 3 24 | 25 | 26 | def test_lexer_missing_end_of_comment(): 27 | l = p21.Lexer(' ;\n/* xbyb();.,\n''""\\\\ ;') 28 | with pytest.raises(p21.ParseError): 29 | list(l) 30 | assert l.line_number == 3 31 | 32 | 33 | def test_lexer_invalid_keywords(): 34 | # Lexer accepts invalid keywords if they are a valid reference -> parser check required 35 | assert list(p21.Lexer("#1=#100(100);")) == ['#1', '=', '#100', '(', 100, ')', ';'] 36 | # But simple parameters like int, float, enum, binary and string will be detected 37 | # because of the invalid termination '(' 38 | with pytest.raises(p21.ParseError): 39 | list(p21.Lexer("#1=100(100);")) 40 | 41 | 42 | def test_lexer_simple_string(): 43 | # String is always a parameter and therefor requires an following terminator ',' or ')' 44 | assert list(p21.Lexer("'ABC' ,")) == ['ABC', ','] 45 | # Characters like \n ot \t also terminate parameters 46 | assert type(list(p21.Lexer("'ABC'\t"))[0]) is str 47 | assert list(p21.Lexer("'ABC'\n")) == ['ABC'] 48 | 49 | 50 | def test_lexer_string_with_special_chars(): 51 | r = list(p21.Lexer(r"'\X2\000F\X0\ ?*$',")) 52 | assert r == ['\u000f ?*$', ','] 53 | 54 | 55 | def test_lexer_string_with_escaped_apostrophe(): 56 | r = list(p21.Lexer("'Henry''s Pub',")) 57 | assert r == ["Henry's Pub", ','] 58 | 59 | 60 | def test_lexer_string_with_enclosed_comments(): 61 | r = list(p21.Lexer(r"'/* a comment */',")) 62 | assert r == ["/* a comment */", ','] 63 | 64 | 65 | def test_lexer_string_across_lines(): 66 | assert list(p21.Lexer("'multi\nline',")) == ['multiline', ','] 67 | 68 | 69 | def test_lexer_empty_string(): 70 | assert list(p21.Lexer("'',")) == ['', ','] 71 | assert list(p21.Lexer("'''',")) == ["'", ','] 72 | 73 | 74 | def test_lexer_binary(): 75 | # Binary is always a parameter and therefor requires an following terminator ',' or ')' 76 | assert list(p21.Lexer('"0F", "1FF",')) == [15, ',', 255, ','] 77 | assert type(list(p21.Lexer('"0F",'))[0]) is int 78 | 79 | 80 | def test_lexer_binary_error(): 81 | with pytest.raises(p21.ParseError): 82 | list(p21.Lexer('"",')) 83 | with pytest.raises(p21.ParseError): 84 | list(p21.Lexer('"0F,')) 85 | with pytest.raises(p21.ParseError): 86 | list(p21.Lexer('"FF",')) 87 | 88 | 89 | def test_lexer_number(): 90 | # Number is always a parameter and therefor requires an following terminator ',' or ')' 91 | assert list(p21.Lexer('0, -1, +2, -0.5, +9.3,'))[::2] == [0, -1, 2, -0.5, 9.3] 92 | assert list(p21.Lexer('1e10, +1E-2, 1.5e-2, 1.6e+10,'))[::2] == [1e10, 1e-2, 1.5e-2, 1.6e+10] 93 | assert list(p21.Lexer('(1,2,3.5)')) == ['(', 1, ',', 2, ',', 3.5, ')'] 94 | assert type(list(p21.Lexer('0,'))[0]) is int 95 | assert type(list(p21.Lexer('0.0,'))[0]) is float 96 | 97 | 98 | def test_lexer_number_error(): 99 | with pytest.raises(p21.ParseError): 100 | list(p21.Lexer('1a,')) 101 | with pytest.raises(p21.ParseError): 102 | list(p21.Lexer('1.5a,')) 103 | with pytest.raises(p21.ParseError): 104 | list(p21.Lexer('1e10a,')) 105 | with pytest.raises(p21.ParseError): 106 | list(p21.Lexer('1.5e0.5,')) 107 | with pytest.raises(p21.ParseError): 108 | list(p21.Lexer('1.5e+0.5,')) 109 | 110 | 111 | def test_lexer_enum(): 112 | # Enumeration is always a parameter and therefor requires an following terminator ',' or ')' 113 | assert list(p21.Lexer('.TRUE., .A1., .B_2., .__X__.,'))[::2] == ['.TRUE.', '.A1.', '.B_2.', '.__X__.'] 114 | assert type(list(p21.Lexer('.TRUE.,'))[0]) is p21.Enumeration 115 | 116 | 117 | def test_lexer_enum_error(): 118 | with pytest.raises(p21.ParseError): 119 | list(p21.Lexer('.FALSE, ')) 120 | with pytest.raises(p21.ParseError): 121 | list(p21.Lexer('.0A., ')) 122 | with pytest.raises(p21.ParseError): 123 | list(p21.Lexer('.., ')) 124 | with pytest.raises(p21.ParseError): 125 | list(p21.Lexer('.enum.,')) 126 | 127 | 128 | def test_lexer_keyword(): 129 | assert list(p21.Lexer('KEYWORD() X0KEY _KEY')) == ['KEYWORD', '(', ')', 'X0KEY', '_KEY'] 130 | # allow lowercase keywords, not according to the STEP standard 131 | assert list(p21.Lexer('Keyword_() X0Key')) == ['Keyword_', '(', ')', 'X0Key'] 132 | assert list(p21.Lexer('!Keyword_() !X0Key')) == ['!Keyword_', '(', ')', '!X0Key'] 133 | # leading number in front of keywords should raise an error in parser 134 | with pytest.raises(p21.ParseError): 135 | list(p21.Lexer('0Keyword')) 136 | # allow '-' to match ISO-10303-21 this is not not a valid keyword!, and should raise a Error in the Parser 137 | assert list(p21.Lexer('ISO-10303-21;')) == ['ISO-10303-21', ';'] 138 | assert type(list(p21.Lexer('KEYWORD'))[0]) is p21.Keyword 139 | assert type(list(p21.Lexer('!KEYWORD'))[0]) is p21.UserKeyword 140 | assert isinstance(list(p21.Lexer('!KEYWORD'))[0], p21.Keyword) 141 | 142 | 143 | def test_lexer_keyword_error(): 144 | with pytest.raises(p21.ParseError): 145 | list(p21.Lexer('kEYWORD')) 146 | with pytest.raises(p21.ParseError): 147 | list(p21.Lexer('KEY -EYWORD')) 148 | 149 | 150 | def test_lexer_reference(): 151 | assert list(p21.Lexer('#100=KEY')) == ['#100', '=', 'KEY'] 152 | 153 | 154 | def test_lexer_reference_error(): 155 | with pytest.raises(p21.ParseError): 156 | list(p21.Lexer('#100a')) 157 | with pytest.raises(p21.ParseError): 158 | list(p21.Lexer('#a')) 159 | 160 | 161 | def test_keyword_matcher(): 162 | from steputils.p21 import KEYWORD 163 | assert KEYWORD.fullmatch('KEYWORD') is not None 164 | assert KEYWORD.fullmatch('0KEYWORD') is None 165 | assert KEYWORD.fullmatch('ISO-10303-21') is None 166 | 167 | 168 | if __name__ == '__main__': 169 | pytest.main([__file__]) 170 | -------------------------------------------------------------------------------- /tests/p21/test_p21_parser.py: -------------------------------------------------------------------------------- 1 | # Created: 28.12.2019 2 | # Copyright (c) 2019 Manfred Moitzi 3 | # License: MIT License 4 | import pytest 5 | from steputils import p21 6 | 7 | STEP_FILE = r"""ISO-10303-21; 8 | HEADER; 9 | FILE_DESCRIPTION(('ViewDefinition [CoordinationView, SpaceBoundary2ndLevelAddOnView, QuantityTakeOffAddOnView]','Option [Filter: ]'),'2;1'); 10 | FILE_NAME('S:\\[IFC]\\[COMPLETE-BUILDINGS]\\xyz.ifc','2011-01-17T09:42:14',('Architect'),('Building Designer Office'),'PreProc - EDM 5.0','ArchiCAD 14.00 Release 1. Windows Build Number of the Ifc 2x3 interface: 3427','The authorising person'); 11 | FILE_SCHEMA(('IFC2X3')); 12 | ENDSEC; 13 | 14 | DATA; 15 | #8= IFCORGANIZATION('','Nicht definiert','',$,$); 16 | #10= IFCORGANIZATION('GS','Graphisoft','Graphisoft',$,$); 17 | #5= IFCAPPLICATION(#10,'14.0','ArchiCAD 14.0','ArchiCAD'); 18 | #6= IFCPERSON('','Nicht definiert','',$,$,$,$,$); 19 | 20 | ENDSEC; 21 | 22 | END-ISO-10303-21; 23 | """ 24 | 25 | 26 | @pytest.fixture(scope='module') 27 | def stpfile(): 28 | return p21.loads(STEP_FILE) 29 | 30 | 31 | def test_header(stpfile): 32 | assert stpfile.header['FILE_DESCRIPTION'].params[0][ 33 | 0] == 'ViewDefinition [CoordinationView, SpaceBoundary2ndLevelAddOnView, QuantityTakeOffAddOnView]' 34 | assert stpfile.header['FILE_DESCRIPTION'].params[0][1] == 'Option [Filter: ]' 35 | assert stpfile.header['FILE_DESCRIPTION'].params[1] == '2;1' 36 | assert stpfile.header['FILE_NAME'].params[0] == 'S:\\[IFC]\\[COMPLETE-BUILDINGS]\\xyz.ifc' 37 | assert stpfile.header['FILE_SCHEMA'].params[0] == ('IFC2X3',) 38 | 39 | 40 | def test_data_section(stpfile): 41 | data = stpfile.data[0] 42 | instance = data['#5'] 43 | assert instance.ref == '#5' 44 | assert p21.is_simple_entity_instance(instance) is True 45 | assert instance.entity.name == 'IFCAPPLICATION' 46 | assert instance.entity.params == ('#10', '14.0', 'ArchiCAD 14.0', 'ArchiCAD') 47 | 48 | ref = instance.entity.params[0] 49 | assert p21.is_reference(ref) 50 | instance2 = stpfile[ref] 51 | assert instance2.ref == ref 52 | assert p21.is_simple_entity_instance(instance2) is True 53 | assert instance2.entity.name == 'IFCORGANIZATION' 54 | assert instance2.entity.params == ('GS', 'Graphisoft', 'Graphisoft', '$', '$') 55 | assert p21.is_unset_parameter(instance2.entity.params[3]) is True 56 | 57 | 58 | def test_data_order(stpfile): 59 | data = stpfile.data[0] 60 | assert list(data.references()) == ['#8', '#10', '#5', '#6'] 61 | 62 | 63 | # contains comments 64 | # typed parameter 65 | # complex entity instances 66 | 67 | COMPLEX_FILE = r"""ISO-10303-21; 68 | HEADER; 69 | 70 | FILE_DESCRIPTION(('CATIA V5 STEP'),'2;1'); 71 | 72 | FILE_NAME('E:\\Public\\Archive_PDES\\TR22\\NativeFiles\\s1\\s1-c5-214.stp','2008-08-18T12:41:46+00:00',('none'),('none'),'CATIA Version 5 Release 19 SP 1 (IN-PROTO)','CATIA V5 STEP AP214','none'); 73 | 74 | FILE_SCHEMA(('AUTOMOTIVE_DESIGN { 1 0 10303 214 1 1 1 1 }')); 75 | 76 | ENDSEC; 77 | /* file written by CATIA V5R19 */ 78 | DATA; 79 | #5=PRODUCT('*MASTER','*MASTER',' ',(#2)) ; 80 | #1=APPLICATION_CONTEXT('automotive design') ; 81 | #175=CARTESIAN_POINT('NONE',(0.,0.,0.)) ; 82 | #176=CARTESIAN_POINT('NONE',(0.,1.43622047244,-0.00905511811024)) ; 83 | #193=CARTESIAN_POINT('NONE',(0.,0.,0.)) ; 84 | #194=CARTESIAN_POINT('NONE',(0.,-1.43622047244,-0.00905511811024)) ; 85 | #57=DIRECTION('NONE',(0.0393700787402,0.,0.)) ; 86 | #58=DIRECTION('NONE',(0.,0.,0.0393700787402)) ; 87 | #59=DIRECTION('NONE',(0.0393700787402,0.,0.)) ; 88 | #24=UNCERTAINTY_MEASURE_WITH_UNIT(LENGTH_MEASURE(0.000196850393701),#23,'distance_accuracy_value','CONFUSED CURVE UNCERTAINTY') ; 89 | #17=(LENGTH_UNIT()NAMED_UNIT(*)SI_UNIT(.MILLI.,.METRE.)) ; 90 | #100= FEA_LINEAR_ELASTICITY('',FEA_ISOTROPIC_SYMMETRIC_TENSOR4_3D( 91 | (10000000.,0.33))); 92 | #101= SIMPLE_ENTITY(TYPE1(TYPE2(TYPE3(1)))); 93 | #102= SIMPLE_ENTITY(TYPE1(TYPE2(TYPE3((1,2,3))))); 94 | 95 | ENDSEC; 96 | END-ISO-10303-21; 97 | """ 98 | 99 | 100 | @pytest.fixture(scope='module') 101 | def complex_file(): 102 | return p21.loads(COMPLEX_FILE) 103 | 104 | 105 | def test_typed_parameter(complex_file): 106 | instance = complex_file['#24'] 107 | assert instance.entity.name == "UNCERTAINTY_MEASURE_WITH_UNIT" 108 | typed_param = instance.entity.params[0] 109 | assert p21.is_typed_parameter(typed_param) is True 110 | assert typed_param.type_name == "LENGTH_MEASURE" 111 | assert typed_param.param == 0.000196850393701 112 | 113 | 114 | def test_complex_instance(complex_file): 115 | instance = complex_file['#17'] 116 | assert p21.is_complex_entity_instance(instance) is True 117 | entities = instance.entities 118 | assert len(entities) == 3 119 | assert entities[0].name == "LENGTH_UNIT" 120 | assert len(entities[0].params) == 0 121 | assert entities[1].name == "NAMED_UNIT" 122 | assert entities[1].params[0] == "*" 123 | assert p21.is_unset_parameter(entities[1].params[0]) 124 | assert entities[2].name == "SI_UNIT" 125 | assert entities[2].params == ('.MILLI.', '.METRE.') 126 | assert p21.is_enum(entities[2].params[0]) is True 127 | 128 | 129 | def test_typed_parameter_list(complex_file): 130 | instance = complex_file['#100'] 131 | assert instance.entity.name == 'FEA_LINEAR_ELASTICITY' 132 | entity = instance.entity 133 | assert len(entity.params) == 2 134 | typed_param = entity.params[1] 135 | assert p21.is_typed_parameter(typed_param) 136 | assert typed_param.type_name == 'FEA_ISOTROPIC_SYMMETRIC_TENSOR4_3D' 137 | param_list = typed_param.param 138 | assert p21.is_parameter_list(param_list) 139 | assert type(param_list[0]) is float 140 | assert param_list[0] == 10_000_000. 141 | assert param_list[1] == 0.33 142 | 143 | 144 | def test_nested_typed_parameters(complex_file): 145 | instance = complex_file['#101'] 146 | assert instance.entity.name == 'SIMPLE_ENTITY' 147 | entity = instance.entity 148 | assert len(entity.params) == 1 149 | t1 = entity.params[0] 150 | assert p21.is_typed_parameter(t1) 151 | assert t1.type_name == 'TYPE1' 152 | assert p21.is_typed_parameter(t1.param) # TYPE2 153 | assert t1.param.type_name == 'TYPE2' 154 | assert p21.is_typed_parameter(t1.param.param) # TYPE3 155 | assert t1.param.param.type_name == 'TYPE3' 156 | 157 | 158 | if __name__ == '__main__': 159 | pytest.main([__file__]) 160 | -------------------------------------------------------------------------------- /tests/p21/test_strings.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from steputils.strings import step_encoder, step_decoder, StringDecodingError, StringBuffer, EOF 4 | 5 | 6 | def test_buffer(): 7 | b = StringBuffer('test') 8 | assert b.look() == 't' 9 | assert b.look(1) == 'e' 10 | assert b.get() == 't' 11 | assert b.look() == 'e' 12 | assert b.get() == 'e' 13 | assert b.get() == 's' 14 | assert b.get() == 't' 15 | assert b.get() == EOF 16 | assert b.get() == EOF 17 | assert b.get() == EOF 18 | assert b.look() == EOF 19 | assert b.look(3) == EOF 20 | 21 | 22 | def test_string_encoder(): 23 | assert step_encoder('ABC') == 'ABC' 24 | assert step_encoder('"') == '"' 25 | assert step_encoder("'") == "''" 26 | assert step_encoder('\'') == '\'\'' 27 | assert step_encoder('\\') == '\\\\' 28 | assert step_encoder('ABCÄ') == 'ABC\\X2\\00C4\\X0\\' 29 | assert step_encoder('ABCÄÖ') == 'ABC\\X2\\00C400D6\\X0\\' 30 | assert step_encoder('CÄÖC') == 'C\\X2\\00C400D6\\X0\\C' 31 | assert step_encoder('CÄ\\ÖC') == 'C\\X2\\00C4\\X0\\\\\\\\X2\\00D6\\X0\\C' 32 | assert step_encoder('CÄ\'ÖC') == 'C\\X2\\00C4\\X0\\\'\'\\X2\\00D6\\X0\\C' 33 | 34 | 35 | def test_string_decoder(): 36 | assert step_decoder('ABC') == 'ABC' 37 | assert step_decoder("\"") == "\"" 38 | assert step_decoder("'") == "'" 39 | assert step_decoder("''") == "''", "Apostrophe decoding has to be done by the lexer." 40 | assert step_decoder("x''x") == "x''x" 41 | assert step_decoder("x\"x") == "x\"x" 42 | assert step_decoder("\\\\") == "\\" 43 | assert step_decoder("x\\\\x") == "x\\x" 44 | assert step_decoder('ABC\\X2\\00C4\\X0\\') == 'ABCÄ' 45 | assert step_decoder('ABC\\X2\\00C400D6\\X0\\') == 'ABCÄÖ' 46 | assert step_decoder('C\\X2\\00C400D6\\X0\\C') == 'CÄÖC' 47 | assert step_decoder('C\\X2\\00C4\\X0\\\\\\\\X2\\00D6\\X0\\C') == 'CÄ\\ÖC' 48 | # does not decode escaped apostrophes ' 49 | assert step_decoder('C\\X2\\00C4\\X0\\\'\'\\X2\\00D6\\X0\\C') == 'CÄ\'\'ÖC' 50 | 51 | 52 | def test_extended_string_decoderx2(): 53 | assert step_decoder("\\X2\\00E4\\X0\\") == '\u00E4' 54 | 55 | 56 | def test_extended_string_decoder_multi_x2(): 57 | assert step_decoder("\\X2\\00E400E4\\X0\\") == '\u00E4\u00E4' 58 | 59 | 60 | def test_extended_string_decoder_x4(): 61 | assert step_decoder("\\X4\\000000E4\\X0\\") == '\u00E4' 62 | 63 | 64 | def test_extended_string_decoder_error(): 65 | # invalid count of hex chars 66 | pytest.raises(StringDecodingError, step_decoder, "\\X2\\0E4\\X0\\") 67 | pytest.raises(StringDecodingError, step_decoder, "\\X4\\00000E4\\X0\\") 68 | 69 | 70 | if __name__ == '__main__': 71 | pytest.main([__file__]) 72 | --------------------------------------------------------------------------------