Coverage for lobster/tools/trlc/text_generation.py: 100%
34 statements
« prev ^ index » next coverage.py v7.10.7, created at 2026-09-22 04:43 +0000
« prev ^ index » next coverage.py v7.10.7, created at 2026-09-22 04:43 +0000
1# LOBSTER - Lightweight Open BMW Software Traceability Evidence Report
2# Copyright (C) 2025 Bayerische Motoren Werke Aktiengesellschaft (BMW AG)
3#
4# This program is free software: you can redistribute it and/or modify
5# it under the terms of the GNU Affero General Public License as
6# published by the Free Software Foundation, either version 3 of the
7# License, or (at your option) any later version.
8#
9# This program is distributed in the hope that it will be useful, but
10# WITHOUT ANY WARRANTY; without even the implied warranty of
11# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12# Affero General Public License for more details.
13#
14# You should have received a copy of the GNU Affero General Public
15# License along with this program. If not, see
16# <https://www.gnu.org/licenses/>.
18from typing import Iterable, List
19import re
20from trlc import ast
21from lobster.tools.trlc.errors import TupleComponentError
22from lobster.tools.trlc.instruction import (
23 Instruction, InstructionType, ConstantInstruction, FieldInstruction,
24)
27PATTERN = re.compile(r"\$\([a-z][a-z0-9_]*\)", re.IGNORECASE)
30def parse_instructions(text: str) -> List[Instruction]:
31 """Parse a one-line text representing a list of instructions"""
32 instructions = []
33 cpos = 0
34 for match in PATTERN.finditer(text):
35 if match.span()[0] > cpos:
36 instructions.append(ConstantInstruction(text[cpos:match.span()[0]]))
37 instructions.append(FieldInstruction(match.group(0)[2:-1]))
38 cpos = match.span()[1]
39 if cpos < len(text):
40 instructions.append(ConstantInstruction(text[cpos:]))
41 return instructions
44def build_text_from_instructions(
45 instructions: Iterable[Instruction],
46 tuple_aggregate: ast.Tuple_Aggregate,
47) -> str:
48 tuple_data = tuple_aggregate.to_python_object()
49 if not tuple_data:
50 raise ValueError("Cannot convert empty TRLC tuple to text!")
51 if not instructions:
52 raise ValueError(
53 f"'to_string' instructions for tuple '{tuple_aggregate.typ.name}' "
54 f"are empty!",
55 )
56 result = []
57 for instruction in instructions:
58 if instruction.typ == InstructionType.CONSTANT_TEXT:
59 result.append(instruction.value)
60 elif instruction.typ == InstructionType.FIELD:
61 component_value = tuple_data.get(instruction.value)
62 # Note: If the field does not exist in the record type OR if the record
63 # object is not fully populated, we raise an error.
64 if component_value is None:
65 raise TupleComponentError(instruction.value, tuple_aggregate)
66 result.append(str(component_value))
67 else:
68 raise ValueError(f"Unknown instruction type: {instruction.typ}")
69 return "".join(result)