Skip to content

Commit 9299100

Browse files
committed
Implemented support for sections in Plain.
1 parent 5ca9502 commit 9299100

File tree

4 files changed

+418
-261
lines changed

4 files changed

+418
-261
lines changed

Plain-language-specification.md

Lines changed: 31 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,6 @@ Plain language is structured English based on markdown syntax.
1313
Here's an example of a "hello,world" program in Plain.
1414

1515
```plain
16-
***Definitions:***
17-
1816
***Non-Functional Requirements:***
1917
2018
- Implementation should be in Python.
@@ -26,19 +24,42 @@ Here's an example of a "hello,world" program in Plain.
2624

2725
# Source structure
2826

29-
Every Plain source file requires the following top level sections:
27+
## Source organization
28+
29+
Plain source can be organized in sections and subsection using markdown headers.
30+
31+
```plain
32+
# Section 1
33+
34+
# Section 2
35+
36+
***Non-Functional Requirements:***
37+
38+
- Simple non-functional requirement
39+
40+
## Section 2.1
41+
42+
***Functional Requirements:***
43+
44+
- Simple functional requirement
45+
```
46+
47+
### Specifications
48+
49+
There are four types of specifications:
3050

3151
- `***Definitions:***`
3252
- `***Non-Functional Requirements:***`
3353
- `***Functional Requirements:***`
34-
35-
The following top level section is optional:
3654
- `***Test Requirements:***`
3755

56+
Every plain source file requires at least one functional requirement and an associated non-functional requirement.
57+
58+
Functional requirements must reside in leaf sections while other specifications can be placed also in non-leaf sections. Specifications in non-leaf sections apply not just to the section itself but to all of its subsections.
3859

39-
## Definitons
60+
## Definitions
4061

41-
The top level section `***Definitions:***` is a list of definitions of new terms.
62+
The `***Definitions:***` specification is a list of definitions of new terms.
4263

4364
Here's an example of a simple definiton.
4465

@@ -61,7 +82,7 @@ The definition of a term is provided in natural language. There are no restricti
6182

6283
## Non-Functional Requirements
6384

64-
The top level section `***Non-Functional Requirements:***` is a list of instructions that steer software code implementation and provide details of execution environment.
85+
The `***Non-Functional Requirements:***` specification is a list of instructions that steer software code implementation and provide details of execution environment.
6586

6687
Here's an example of a simple instruction specifying only that the Plain specification should be rendered to Python software code.
6788

@@ -83,7 +104,7 @@ Here's an example of more complex instructions.
83104

84105
## Functional Requirements
85106

86-
The top level section `***Functional Requirements:***` provides a description of functionality that should be rendered to software code. The descriptions should be provided in natural language as a markdown list. When referring to other terms **The Noun convention** should be followed.
107+
The `***Functional Requirements:***` specificationprovides a description of functionality that should be rendered to software code. The descriptions should be provided in natural language as a markdown list. When referring to other terms **The Noun convention** should be followed.
87108

88109
Here's an example of a simple description of the functionality of the "hello, world" application.
89110

@@ -125,7 +146,7 @@ Functional requirements are rendered incrementally one by one. Consequently earl
125146

126147
## Test Requirements
127148

128-
The top level section `***Test Requirements:***` is a list of instructions that steer implementation of end-to-end tests and provide details of testing environment.
149+
The `***Test Requirements:***` specification is a list of instructions that steer implementation of end-to-end tests and provide details of testing environment.
129150

130151
Here's an example specification of test requirements.
131152

codeplain_REST_api.py

Lines changed: 16 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -57,42 +57,21 @@ def post_request(self, endpoint_url, headers, payload):
5757
return response_json
5858

5959

60-
def get_plain_sections(self, plain_source, loaded_templates):
60+
def get_plain_source_tree(self, plain_source, loaded_templates):
6161
"""
62-
Extracts labeled sections from the given plain text source in Markdown format.
62+
Builds plain source tree from the given plain text source in Markdown format.
6363
6464
Args:
6565
plain_source (str): A string containing the plain text source to be parsed.
66-
The input must be in Markdown format and include the following
67-
mandatory labeled sections, each marked by a specific label
68-
at the start of the line:
69-
70-
- "***Definitions:***"
71-
- "***Functional Requirements:***"
72-
- "***Non-Functional Requirements:***"
73-
74-
Other labeled sections, such as "***Test Requirements:***",
75-
are optional but will be parsed if present. The input should be
76-
non-empty and formatted using consistent Markdown syntax with
77-
the required labels to ensure successful parsing and extraction.
7866
loaded_templates (dict): A dictionary containing the loaded templates.
7967
8068
Returns:
81-
list: A list of labeled sections extracted from the plain source. Each section is
82-
a structured segment within the source text, starting with a recognized
83-
labeled header followed by its content.
69+
dict: A plain source tree.
8470
8571
Raises:
8672
Exception: If parsing of plain_source fails.
87-
88-
Notes:
89-
This method processes the input text using predefined parsing rules to identify
90-
and extract labeled sections. The document must be formatted correctly in Markdown,
91-
with specific labels (e.g., "***Definitions:***") marking the start of each segment.
92-
The "Definitions," "Functional Requirements," and "Non-Functional Requirements"
93-
labeled sections are required, while others are optional.
9473
"""
95-
endpoint_url = f"{self.api_url}/plain_sections"
74+
endpoint_url = f"{self.api_url}/plain_source_tree"
9675
headers = {
9776
"X-API-Key": self.api_key,
9877
"Content-Type": "application/json"
@@ -106,15 +85,14 @@ def get_plain_sections(self, plain_source, loaded_templates):
10685
return self.post_request(endpoint_url, headers, payload)
10786

10887

109-
def render_functional_requirement(self, frid, plain_sections, linked_resources, existing_files_content):
88+
def render_functional_requirement(self, frid, plain_source_tree, linked_resources, existing_files_content):
11089
"""
11190
Renders the content of a functional requirement based on the provided ID,
112-
corresponding sections from a Plain document, and existing files' content.
91+
plain source tree, and existing files' content.
11392
11493
Args:
11594
frid (str): The unique identifier for the functional requirement to be rendered.
116-
plain_sections (list of str): A list of sections from the Plain document that
117-
are relevant to the functional requirement.
95+
plain_source_tree (dict): A dictionary containing the plain source tree.
11896
linked_resources (dict): A dictionary where the keys represent resource names
11997
and the values are the content of those resources.
12098
existing_files_content (dict): A dictionary where the keys represent code base
@@ -125,7 +103,7 @@ def render_functional_requirement(self, frid, plain_sections, linked_resources,
125103
appropriately based on the inputs.
126104
127105
Raises:
128-
ValueError: If the frid is invalid or the necessary sections cannot be found.
106+
ValueError: If the frid is invalid or the necessary plain source tree is not valid.
129107
"""
130108
endpoint_url = f"{self.api_url}/render_functional_requirement"
131109
headers = {
@@ -135,15 +113,15 @@ def render_functional_requirement(self, frid, plain_sections, linked_resources,
135113

136114
payload = {
137115
"frid": frid,
138-
"plain_sections": plain_sections,
116+
"plain_source_tree": plain_source_tree,
139117
"linked_resources": linked_resources,
140118
"existing_files_content": existing_files_content
141119
}
142-
120+
143121
return self.post_request(endpoint_url, headers, payload)
144122

145123

146-
def fix_unittests_issue(self, frid, plain_sections, linked_resources, existing_files_content, unittests_issue):
124+
def fix_unittests_issue(self, frid, plain_source_tree, linked_resources, existing_files_content, unittests_issue):
147125
endpoint_url = f"{self.api_url}/fix_unittests_issue"
148126
headers = {
149127
"X-API-Key": self.api_key,
@@ -152,7 +130,7 @@ def fix_unittests_issue(self, frid, plain_sections, linked_resources, existing_f
152130

153131
payload = {
154132
"frid": frid,
155-
"plain_sections": plain_sections,
133+
"plain_source_tree": plain_source_tree,
156134
"linked_resources": linked_resources,
157135
"existing_files_content": existing_files_content,
158136
"unittests_issue": unittests_issue
@@ -176,7 +154,7 @@ def refactor_source_files_if_needed(self, files_to_check, existing_files_content
176154
return self.post_request(endpoint_url, headers, payload)
177155

178156

179-
def render_e2e_tests(self, frid, plain_sections, linked_resources, existing_files_content):
157+
def render_e2e_tests(self, frid, plain_source_tree, linked_resources, existing_files_content):
180158
endpoint_url = f"{self.api_url}/render_e2e_tests"
181159
headers = {
182160
"X-API-Key": self.api_key,
@@ -185,7 +163,7 @@ def render_e2e_tests(self, frid, plain_sections, linked_resources, existing_file
185163

186164
payload = {
187165
"frid": frid,
188-
"plain_sections": plain_sections,
166+
"plain_source_tree": plain_source_tree,
189167
"linked_resources": linked_resources,
190168
"existing_files_content": existing_files_content
191169
}
@@ -208,7 +186,7 @@ def generate_folder_name_from_functional_requirement(self, functional_requiremen
208186
return self.post_request(endpoint_url, headers, payload)
209187

210188

211-
def fix_e2e_tests_issue(self, frid, functional_requirement_id, plain_sections, linked_resources, existing_files_content, code_diff, e2e_tests_files, e2e_tests_issue, implementation_fix_count):
189+
def fix_e2e_tests_issue(self, frid, functional_requirement_id, plain_source_tree, linked_resources, existing_files_content, code_diff, e2e_tests_files, e2e_tests_issue, implementation_fix_count):
212190
endpoint_url = f"{self.api_url}/fix_e2e_tests_issue"
213191
headers = {
214192
"X-API-Key": self.api_key,
@@ -218,7 +196,7 @@ def fix_e2e_tests_issue(self, frid, functional_requirement_id, plain_sections, l
218196
payload = {
219197
"frid": frid,
220198
"functional_requirement_id": functional_requirement_id,
221-
"plain_sections": plain_sections,
199+
"plain_source_tree": plain_source_tree,
222200
"linked_resources": linked_resources,
223201
"existing_files_content": existing_files_content,
224202
"code_diff": code_diff,

0 commit comments

Comments
 (0)