@flexiberry/berrycore 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/dist/adapter/cli-adapter.d.ts +37 -0
- package/dist/adapter/cli-adapter.js +119 -0
- package/dist/berry-core.d.ts +108 -0
- package/dist/berry-core.js +258 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.js +18 -0
- package/dist/interpreter/environment.d.ts +45 -0
- package/dist/interpreter/environment.js +96 -0
- package/dist/interpreter/errors.d.ts +16 -0
- package/dist/interpreter/errors.js +27 -0
- package/dist/interpreter/interpreter.d.ts +111 -0
- package/dist/interpreter/interpreter.js +682 -0
- package/dist/interpreter/interpreter.types.d.ts +182 -0
- package/dist/interpreter/interpreter.types.js +73 -0
- package/dist/parser/ast/ast.engine.d.ts +103 -0
- package/dist/parser/ast/ast.engine.js +526 -0
- package/dist/parser/ast/ast.types.d.ts +242 -0
- package/dist/parser/ast/ast.types.js +37 -0
- package/dist/parser/formatter/formatter.d.ts +44 -0
- package/dist/parser/formatter/formatter.js +214 -0
- package/dist/parser/tokenizer/reader/grammer/api.grammer.d.ts +2 -0
- package/dist/parser/tokenizer/reader/grammer/api.grammer.js +102 -0
- package/dist/parser/tokenizer/reader/grammer/capture.grammer.d.ts +2 -0
- package/dist/parser/tokenizer/reader/grammer/capture.grammer.js +21 -0
- package/dist/parser/tokenizer/reader/grammer/check.grammer.d.ts +2 -0
- package/dist/parser/tokenizer/reader/grammer/check.grammer.js +21 -0
- package/dist/parser/tokenizer/reader/grammer/comment.grammer.d.ts +2 -0
- package/dist/parser/tokenizer/reader/grammer/comment.grammer.js +13 -0
- package/dist/parser/tokenizer/reader/grammer/conditions.grammer.d.ts +2 -0
- package/dist/parser/tokenizer/reader/grammer/conditions.grammer.js +68 -0
- package/dist/parser/tokenizer/reader/grammer/input.grammer.d.ts +2 -0
- package/dist/parser/tokenizer/reader/grammer/input.grammer.js +17 -0
- package/dist/parser/tokenizer/reader/grammer/keyvalue.grammer.d.ts +2 -0
- package/dist/parser/tokenizer/reader/grammer/keyvalue.grammer.js +240 -0
- package/dist/parser/tokenizer/reader/grammer/link.grammer.d.ts +2 -0
- package/dist/parser/tokenizer/reader/grammer/link.grammer.js +17 -0
- package/dist/parser/tokenizer/reader/grammer/params.grammer.d.ts +2 -0
- package/dist/parser/tokenizer/reader/grammer/params.grammer.js +21 -0
- package/dist/parser/tokenizer/reader/grammer/step.grammer.d.ts +2 -0
- package/dist/parser/tokenizer/reader/grammer/step.grammer.js +25 -0
- package/dist/parser/tokenizer/reader/grammer/task.grammer.d.ts +2 -0
- package/dist/parser/tokenizer/reader/grammer/task.grammer.js +17 -0
- package/dist/parser/tokenizer/reader/grammer/var.grammer.d.ts +2 -0
- package/dist/parser/tokenizer/reader/grammer/var.grammer.js +47 -0
- package/dist/parser/tokenizer/reader/lexer.engine.d.ts +43 -0
- package/dist/parser/tokenizer/reader/lexer.engine.js +178 -0
- package/dist/parser/tokenizer/reader/lexer.types.d.ts +18 -0
- package/dist/parser/tokenizer/reader/lexer.types.js +1 -0
- package/dist/parser/tokenizer/token.d.ts +13 -0
- package/dist/parser/tokenizer/token.js +13 -0
- package/dist/parser/tokenizer/tokenType.d.ts +58 -0
- package/dist/parser/tokenizer/tokenType.js +64 -0
- package/dist/script/format-util.d.ts +33 -0
- package/dist/script/format-util.js +94 -0
- package/dist/script/postman.util.d.ts +88 -0
- package/dist/script/postman.util.js +176 -0
- package/dist/script/swagger.util.d.ts +80 -0
- package/dist/script/swagger.util.js +202 -0
- package/dist/util/store-util.d.ts +5 -0
- package/dist/util/store-util.js +22 -0
- package/package.json +25 -0
- package/readme.md +107 -0
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AST Node Types for Flexiberry DSL
|
|
3
|
+
*
|
|
4
|
+
* Pure data interfaces — no methods, no logic.
|
|
5
|
+
* Each node type maps to a specific lexer grammar rule from reader_v2.
|
|
6
|
+
*/
|
|
7
|
+
export declare enum NodeType {
|
|
8
|
+
Program = "Program",
|
|
9
|
+
VarDeclaration = "VarDeclaration",
|
|
10
|
+
PointerReference = "PointerReference",
|
|
11
|
+
LinkStatement = "LinkStatement",
|
|
12
|
+
InputStatement = "InputStatement",
|
|
13
|
+
ApiBlock = "ApiBlock",
|
|
14
|
+
UrlStatement = "UrlStatement",
|
|
15
|
+
HeaderBlock = "HeaderBlock",
|
|
16
|
+
BodyBlock = "BodyBlock",
|
|
17
|
+
TaskBlock = "TaskBlock",
|
|
18
|
+
StepBlock = "StepBlock",
|
|
19
|
+
ParamsBlock = "ParamsBlock",
|
|
20
|
+
CaptureBlock = "CaptureBlock",
|
|
21
|
+
CheckBlock = "CheckBlock",
|
|
22
|
+
KeyValuePair = "KeyValuePair",
|
|
23
|
+
Condition = "Condition",
|
|
24
|
+
BinaryExpression = "BinaryExpression",
|
|
25
|
+
Identifier = "Identifier",
|
|
26
|
+
Literal = "Literal",
|
|
27
|
+
Comment = "Comment"
|
|
28
|
+
}
|
|
29
|
+
/** Source location attached to every AST node */
|
|
30
|
+
export interface NodePosition {
|
|
31
|
+
readonly line: number;
|
|
32
|
+
readonly column: number;
|
|
33
|
+
}
|
|
34
|
+
export interface BaseNode {
|
|
35
|
+
readonly type: NodeType;
|
|
36
|
+
readonly position: NodePosition;
|
|
37
|
+
}
|
|
38
|
+
/** Top-level program — an ordered list of declarations */
|
|
39
|
+
export interface ProgramNode extends BaseNode {
|
|
40
|
+
readonly type: NodeType.Program;
|
|
41
|
+
readonly body: ReadonlyArray<StatementNode>;
|
|
42
|
+
}
|
|
43
|
+
export type StatementNode = VarDeclarationNode | LinkStatementNode | InputStatementNode | ApiBlockNode | TaskBlockNode | StepBlockNode | ParamsBlockNode | CaptureBlockNode | CheckBlockNode | CommentNode;
|
|
44
|
+
/**
|
|
45
|
+
* `Var @pointed title`
|
|
46
|
+
* followed by key-value pairs on subsequent lines
|
|
47
|
+
*
|
|
48
|
+
* Grammar: var.grammer.ts
|
|
49
|
+
* Tokens: Var, optional (Pointer + Pointed + Title), then keyValueGrammer loop
|
|
50
|
+
*/
|
|
51
|
+
export interface VarDeclarationNode extends BaseNode {
|
|
52
|
+
readonly type: NodeType.VarDeclaration;
|
|
53
|
+
readonly title: string | null;
|
|
54
|
+
readonly pointer: PointerReferenceNode | null;
|
|
55
|
+
readonly entries: ReadonlyArray<KeyValuePairNode>;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* `@pointed` reference inside a Var declaration
|
|
59
|
+
*
|
|
60
|
+
* Tokens: Pointer (@), Pointed (name)
|
|
61
|
+
*/
|
|
62
|
+
export interface PointerReferenceNode extends BaseNode {
|
|
63
|
+
readonly type: NodeType.PointerReference;
|
|
64
|
+
readonly symbol: string;
|
|
65
|
+
readonly target: string;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* `Link path/to/file.berry` or `Link http://example.com/file.berry`
|
|
69
|
+
*
|
|
70
|
+
* Grammar: link.grammer.ts
|
|
71
|
+
* Tokens: Link, LinkPath
|
|
72
|
+
*/
|
|
73
|
+
export interface LinkStatementNode extends BaseNode {
|
|
74
|
+
readonly type: NodeType.LinkStatement;
|
|
75
|
+
readonly path: string;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* `Input path/to/file.csv`
|
|
79
|
+
*
|
|
80
|
+
* Grammar: input.grammer.ts
|
|
81
|
+
* Tokens: Input, InputPath
|
|
82
|
+
*/
|
|
83
|
+
export interface InputStatementNode extends BaseNode {
|
|
84
|
+
readonly type: NodeType.InputStatement;
|
|
85
|
+
readonly path: string;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* `Api GET #myApi Some Title`
|
|
89
|
+
* followed by Url, Header, Body sub-blocks
|
|
90
|
+
*
|
|
91
|
+
* Grammar: api.grammer.ts
|
|
92
|
+
* Tokens: Api, ApiMethod, Hash, Identifier, Title
|
|
93
|
+
*/
|
|
94
|
+
export interface ApiBlockNode extends BaseNode {
|
|
95
|
+
readonly type: NodeType.ApiBlock;
|
|
96
|
+
readonly method: string | null;
|
|
97
|
+
readonly name: string;
|
|
98
|
+
readonly title: string | null;
|
|
99
|
+
readonly url: UrlStatementNode | null;
|
|
100
|
+
readonly headers: HeaderBlockNode | null;
|
|
101
|
+
readonly body: BodyBlockNode | null;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* `Url https://example.com/api`
|
|
105
|
+
*
|
|
106
|
+
* Grammar: api.grammer.ts (url rule)
|
|
107
|
+
* Tokens: Url, Value
|
|
108
|
+
*/
|
|
109
|
+
export interface UrlStatementNode extends BaseNode {
|
|
110
|
+
readonly type: NodeType.UrlStatement;
|
|
111
|
+
readonly value: string;
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* `Header` followed by key-value pairs
|
|
115
|
+
*
|
|
116
|
+
* Grammar: api.grammer.ts (Header rule)
|
|
117
|
+
* Tokens: Header, then keyValueGrammer loop
|
|
118
|
+
*/
|
|
119
|
+
export interface HeaderBlockNode extends BaseNode {
|
|
120
|
+
readonly type: NodeType.HeaderBlock;
|
|
121
|
+
readonly entries: ReadonlyArray<KeyValuePairNode>;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* `Body json` followed by backtick-wrapped content
|
|
125
|
+
*
|
|
126
|
+
* Grammar: api.grammer.ts (Body rule)
|
|
127
|
+
* Tokens: Body, BodyType, Backtick, Scalar, Backtick
|
|
128
|
+
*/
|
|
129
|
+
export interface BodyBlockNode extends BaseNode {
|
|
130
|
+
readonly type: NodeType.BodyBlock;
|
|
131
|
+
readonly bodyType: string;
|
|
132
|
+
readonly content: string;
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* `Task My Test Suite`
|
|
136
|
+
*
|
|
137
|
+
* Grammar: task.grammer.ts
|
|
138
|
+
* Tokens: Task, Title
|
|
139
|
+
*/
|
|
140
|
+
export interface TaskBlockNode extends BaseNode {
|
|
141
|
+
readonly type: NodeType.TaskBlock;
|
|
142
|
+
readonly title: string | null;
|
|
143
|
+
readonly steps: ReadonlyArray<StepBlockNode>;
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* `Step Call Api myApi`
|
|
147
|
+
*
|
|
148
|
+
* Grammar: step.grammer.ts
|
|
149
|
+
* Tokens: Step, Call, Api, Identifier
|
|
150
|
+
*/
|
|
151
|
+
export interface StepBlockNode extends BaseNode {
|
|
152
|
+
readonly type: NodeType.StepBlock;
|
|
153
|
+
readonly callType: string;
|
|
154
|
+
readonly targetType: string;
|
|
155
|
+
readonly targetName: string;
|
|
156
|
+
readonly params: ParamsBlockNode | null;
|
|
157
|
+
readonly capture: CaptureBlockNode | null;
|
|
158
|
+
readonly check: CheckBlockNode | null;
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* `Params` followed by key-value pairs
|
|
162
|
+
*
|
|
163
|
+
* Grammar: params.grammer.ts
|
|
164
|
+
* Tokens: Params, then keyValueGrammer loop
|
|
165
|
+
*/
|
|
166
|
+
export interface ParamsBlockNode extends BaseNode {
|
|
167
|
+
readonly type: NodeType.ParamsBlock;
|
|
168
|
+
readonly entries: ReadonlyArray<KeyValuePairNode>;
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* `Capture` followed by key-value pairs
|
|
172
|
+
*
|
|
173
|
+
* Grammar: capture.grammer.ts
|
|
174
|
+
* Tokens: Capture, then keyValueGrammer loop
|
|
175
|
+
*/
|
|
176
|
+
export interface CaptureBlockNode extends BaseNode {
|
|
177
|
+
readonly type: NodeType.CaptureBlock;
|
|
178
|
+
readonly entries: ReadonlyArray<KeyValuePairNode>;
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* `Check` followed by condition lines
|
|
182
|
+
*
|
|
183
|
+
* Grammar: check.grammer.ts
|
|
184
|
+
* Tokens: Check, then conditionGrammer loop
|
|
185
|
+
*/
|
|
186
|
+
export interface CheckBlockNode extends BaseNode {
|
|
187
|
+
readonly type: NodeType.CheckBlock;
|
|
188
|
+
readonly conditions: ReadonlyArray<ConditionNode>;
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* `- key: value` or `- "key": "value"` or `- key: \`multiline\``
|
|
192
|
+
*
|
|
193
|
+
* Grammar: keyvalue.grammer.ts
|
|
194
|
+
* Tokens: Hyphen, (Quote?, Identifier, Quote?, Colon), (Quote?, Scalar/Identifier, Quote?) or (Backtick, Scalar, Backtick)
|
|
195
|
+
*/
|
|
196
|
+
export interface KeyValuePairNode extends BaseNode {
|
|
197
|
+
readonly type: NodeType.KeyValuePair;
|
|
198
|
+
readonly key: string;
|
|
199
|
+
readonly value: string;
|
|
200
|
+
readonly isKeyQuoted: boolean;
|
|
201
|
+
readonly isValueQuoted: boolean;
|
|
202
|
+
readonly isMultiline: boolean;
|
|
203
|
+
readonly isEncrypted?: boolean;
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* `- lhs == rhs` with optional `OR` chains
|
|
207
|
+
*
|
|
208
|
+
* Grammar: conditions.grammer.ts
|
|
209
|
+
* Tokens: Hyphen, Lhs, Operator, Rhs, optional (Or, Lhs, Operator, Rhs)+
|
|
210
|
+
*/
|
|
211
|
+
export interface ConditionNode extends BaseNode {
|
|
212
|
+
readonly type: NodeType.Condition;
|
|
213
|
+
readonly lhs: string;
|
|
214
|
+
readonly operator: string;
|
|
215
|
+
readonly rhs: string;
|
|
216
|
+
readonly orConditions: ReadonlyArray<BinaryExpressionNode>;
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* An OR-chained binary expression within a condition
|
|
220
|
+
*
|
|
221
|
+
* Tokens: Or, Lhs, Operator, Rhs
|
|
222
|
+
*/
|
|
223
|
+
export interface BinaryExpressionNode extends BaseNode {
|
|
224
|
+
readonly type: NodeType.BinaryExpression;
|
|
225
|
+
readonly lhs: string;
|
|
226
|
+
readonly operator: string;
|
|
227
|
+
readonly rhs: string;
|
|
228
|
+
}
|
|
229
|
+
export interface IdentifierNode extends BaseNode {
|
|
230
|
+
readonly type: NodeType.Identifier;
|
|
231
|
+
readonly name: string;
|
|
232
|
+
}
|
|
233
|
+
export interface LiteralNode extends BaseNode {
|
|
234
|
+
readonly type: NodeType.Literal;
|
|
235
|
+
readonly value: string | number | boolean;
|
|
236
|
+
readonly raw: string;
|
|
237
|
+
}
|
|
238
|
+
export interface CommentNode extends BaseNode {
|
|
239
|
+
readonly type: NodeType.Comment;
|
|
240
|
+
readonly text: string;
|
|
241
|
+
}
|
|
242
|
+
export type ASTNode = ProgramNode | VarDeclarationNode | LinkStatementNode | InputStatementNode | PointerReferenceNode | ApiBlockNode | UrlStatementNode | HeaderBlockNode | BodyBlockNode | TaskBlockNode | StepBlockNode | ParamsBlockNode | CaptureBlockNode | CheckBlockNode | KeyValuePairNode | ConditionNode | BinaryExpressionNode | IdentifierNode | LiteralNode | CommentNode;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AST Node Types for Flexiberry DSL
|
|
3
|
+
*
|
|
4
|
+
* Pure data interfaces — no methods, no logic.
|
|
5
|
+
* Each node type maps to a specific lexer grammar rule from reader_v2.
|
|
6
|
+
*/
|
|
7
|
+
// ─── Node Type Discriminator ────────────────────────────────────────────────
|
|
8
|
+
export var NodeType;
|
|
9
|
+
(function (NodeType) {
|
|
10
|
+
NodeType["Program"] = "Program";
|
|
11
|
+
// Variable constructs
|
|
12
|
+
NodeType["VarDeclaration"] = "VarDeclaration";
|
|
13
|
+
NodeType["PointerReference"] = "PointerReference";
|
|
14
|
+
// Link & Input constructs
|
|
15
|
+
NodeType["LinkStatement"] = "LinkStatement";
|
|
16
|
+
NodeType["InputStatement"] = "InputStatement";
|
|
17
|
+
// API constructs
|
|
18
|
+
NodeType["ApiBlock"] = "ApiBlock";
|
|
19
|
+
NodeType["UrlStatement"] = "UrlStatement";
|
|
20
|
+
NodeType["HeaderBlock"] = "HeaderBlock";
|
|
21
|
+
NodeType["BodyBlock"] = "BodyBlock";
|
|
22
|
+
// Task / Step constructs
|
|
23
|
+
NodeType["TaskBlock"] = "TaskBlock";
|
|
24
|
+
NodeType["StepBlock"] = "StepBlock";
|
|
25
|
+
// Data constructs
|
|
26
|
+
NodeType["ParamsBlock"] = "ParamsBlock";
|
|
27
|
+
NodeType["CaptureBlock"] = "CaptureBlock";
|
|
28
|
+
NodeType["CheckBlock"] = "CheckBlock";
|
|
29
|
+
// Shared primitives
|
|
30
|
+
NodeType["KeyValuePair"] = "KeyValuePair";
|
|
31
|
+
NodeType["Condition"] = "Condition";
|
|
32
|
+
NodeType["BinaryExpression"] = "BinaryExpression";
|
|
33
|
+
// Terminals
|
|
34
|
+
NodeType["Identifier"] = "Identifier";
|
|
35
|
+
NodeType["Literal"] = "Literal";
|
|
36
|
+
NodeType["Comment"] = "Comment";
|
|
37
|
+
})(NodeType || (NodeType = {}));
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Berry Code Formatter
|
|
3
|
+
*
|
|
4
|
+
* Walks a ProgramNode AST and emits properly formatted Berry source code.
|
|
5
|
+
* This is a pure code-generator — no side effects, no runtime logic.
|
|
6
|
+
*
|
|
7
|
+
* Formatting rules:
|
|
8
|
+
* - Top-level blocks (Var, Api, Task) separated by blank lines
|
|
9
|
+
* - Api sub-blocks (Url, Header, Body) indented with no extra blank lines
|
|
10
|
+
* - Task → Step indented 1 level (8 spaces)
|
|
11
|
+
* - Step → Params/Capture/Check indented 2 levels (16 spaces)
|
|
12
|
+
* - Key-value entries indented under their parent with "- key: value"
|
|
13
|
+
* - Conditions indented under Check with "- lhs operator rhs"
|
|
14
|
+
*/
|
|
15
|
+
import { ProgramNode } from "../ast/ast.types.js";
|
|
16
|
+
export interface FormatterOptions {
|
|
17
|
+
/** Number of spaces per indent level (default: 8) */
|
|
18
|
+
readonly indentSize: number;
|
|
19
|
+
/** Blank lines between top-level blocks (default: 1) */
|
|
20
|
+
readonly blankLinesBetweenBlocks: number;
|
|
21
|
+
/** Whether to quote key-value string values (default: true) */
|
|
22
|
+
readonly quoteValues: boolean;
|
|
23
|
+
}
|
|
24
|
+
export declare class BerryFormatter {
|
|
25
|
+
private readonly options;
|
|
26
|
+
constructor(options?: Partial<FormatterOptions>);
|
|
27
|
+
/** Format an entire AST back into Berry source code */
|
|
28
|
+
format(ast: ProgramNode): string;
|
|
29
|
+
private formatStatement;
|
|
30
|
+
private formatVarDeclaration;
|
|
31
|
+
private formatApiBlock;
|
|
32
|
+
private formatUrlStatement;
|
|
33
|
+
private formatHeaderBlock;
|
|
34
|
+
private formatBodyBlock;
|
|
35
|
+
private formatTaskBlock;
|
|
36
|
+
private formatStepBlock;
|
|
37
|
+
private formatParamsBlock;
|
|
38
|
+
private formatCaptureBlock;
|
|
39
|
+
private formatCheckBlock;
|
|
40
|
+
private formatKeyValuePair;
|
|
41
|
+
private formatCondition;
|
|
42
|
+
private formatComment;
|
|
43
|
+
private indent;
|
|
44
|
+
}
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Berry Code Formatter
|
|
3
|
+
*
|
|
4
|
+
* Walks a ProgramNode AST and emits properly formatted Berry source code.
|
|
5
|
+
* This is a pure code-generator — no side effects, no runtime logic.
|
|
6
|
+
*
|
|
7
|
+
* Formatting rules:
|
|
8
|
+
* - Top-level blocks (Var, Api, Task) separated by blank lines
|
|
9
|
+
* - Api sub-blocks (Url, Header, Body) indented with no extra blank lines
|
|
10
|
+
* - Task → Step indented 1 level (8 spaces)
|
|
11
|
+
* - Step → Params/Capture/Check indented 2 levels (16 spaces)
|
|
12
|
+
* - Key-value entries indented under their parent with "- key: value"
|
|
13
|
+
* - Conditions indented under Check with "- lhs operator rhs"
|
|
14
|
+
*/
|
|
15
|
+
import { NodeType, } from "../ast/ast.types.js";
|
|
16
|
+
const DEFAULT_OPTIONS = {
|
|
17
|
+
indentSize: 8,
|
|
18
|
+
blankLinesBetweenBlocks: 1,
|
|
19
|
+
quoteValues: true,
|
|
20
|
+
};
|
|
21
|
+
// ─── Formatter ──────────────────────────────────────────────────────────────
|
|
22
|
+
export class BerryFormatter {
|
|
23
|
+
options;
|
|
24
|
+
constructor(options = {}) {
|
|
25
|
+
this.options = { ...DEFAULT_OPTIONS, ...options };
|
|
26
|
+
}
|
|
27
|
+
/** Format an entire AST back into Berry source code */
|
|
28
|
+
format(ast) {
|
|
29
|
+
const blocks = [];
|
|
30
|
+
for (const node of ast.body) {
|
|
31
|
+
const formatted = this.formatStatement(node);
|
|
32
|
+
if (formatted !== null) {
|
|
33
|
+
blocks.push(formatted);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
const separator = "\n" + "\n".repeat(this.options.blankLinesBetweenBlocks);
|
|
37
|
+
return blocks.join(separator) + "\n";
|
|
38
|
+
}
|
|
39
|
+
// ── Statement Dispatch ──────────────────────────────────────────────────
|
|
40
|
+
formatStatement(node) {
|
|
41
|
+
switch (node.type) {
|
|
42
|
+
case NodeType.VarDeclaration:
|
|
43
|
+
return this.formatVarDeclaration(node);
|
|
44
|
+
case NodeType.ApiBlock:
|
|
45
|
+
return this.formatApiBlock(node);
|
|
46
|
+
case NodeType.TaskBlock:
|
|
47
|
+
return this.formatTaskBlock(node);
|
|
48
|
+
case NodeType.StepBlock:
|
|
49
|
+
return this.formatStepBlock(node, 0);
|
|
50
|
+
case NodeType.ParamsBlock:
|
|
51
|
+
return this.formatParamsBlock(node, 0);
|
|
52
|
+
case NodeType.CaptureBlock:
|
|
53
|
+
return this.formatCaptureBlock(node, 0);
|
|
54
|
+
case NodeType.CheckBlock:
|
|
55
|
+
return this.formatCheckBlock(node, 0);
|
|
56
|
+
case NodeType.Comment:
|
|
57
|
+
return this.formatComment(node);
|
|
58
|
+
default:
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
// ── Var Declaration ─────────────────────────────────────────────────────
|
|
63
|
+
formatVarDeclaration(node) {
|
|
64
|
+
const lines = [];
|
|
65
|
+
// Var header
|
|
66
|
+
let header = "Var";
|
|
67
|
+
if (node.pointer) {
|
|
68
|
+
header += ` ${node.pointer.symbol}${node.pointer.target}`;
|
|
69
|
+
}
|
|
70
|
+
if (node.title) {
|
|
71
|
+
header += ` ${node.title}`;
|
|
72
|
+
}
|
|
73
|
+
lines.push(header);
|
|
74
|
+
// Key-value entries (indented 1 level)
|
|
75
|
+
for (const entry of node.entries) {
|
|
76
|
+
lines.push(this.formatKeyValuePair(entry, 1));
|
|
77
|
+
}
|
|
78
|
+
return lines.join("\n");
|
|
79
|
+
}
|
|
80
|
+
// ── Api Block ───────────────────────────────────────────────────────────
|
|
81
|
+
formatApiBlock(node) {
|
|
82
|
+
const lines = [];
|
|
83
|
+
// Api header: Api METHOD #name title
|
|
84
|
+
let header = "Api";
|
|
85
|
+
if (node.method) {
|
|
86
|
+
header += ` ${node.method}`;
|
|
87
|
+
}
|
|
88
|
+
header += ` #${node.name}`;
|
|
89
|
+
if (node.title) {
|
|
90
|
+
header += ` ${node.title}`;
|
|
91
|
+
}
|
|
92
|
+
lines.push(header);
|
|
93
|
+
// Url
|
|
94
|
+
if (node.url) {
|
|
95
|
+
lines.push(this.formatUrlStatement(node.url));
|
|
96
|
+
}
|
|
97
|
+
// Header
|
|
98
|
+
if (node.headers) {
|
|
99
|
+
lines.push(this.formatHeaderBlock(node.headers));
|
|
100
|
+
}
|
|
101
|
+
// Body
|
|
102
|
+
if (node.body) {
|
|
103
|
+
lines.push(this.formatBodyBlock(node.body));
|
|
104
|
+
}
|
|
105
|
+
return lines.join("\n");
|
|
106
|
+
}
|
|
107
|
+
formatUrlStatement(node) {
|
|
108
|
+
return `Url ${node.value}`;
|
|
109
|
+
}
|
|
110
|
+
formatHeaderBlock(node) {
|
|
111
|
+
const lines = ["Header"];
|
|
112
|
+
for (const entry of node.entries) {
|
|
113
|
+
lines.push(this.formatKeyValuePair(entry, 0));
|
|
114
|
+
}
|
|
115
|
+
return lines.join("\n");
|
|
116
|
+
}
|
|
117
|
+
formatBodyBlock(node) {
|
|
118
|
+
return `Body ${node.bodyType} \`${node.content}\``;
|
|
119
|
+
}
|
|
120
|
+
// ── Task Block ──────────────────────────────────────────────────────────
|
|
121
|
+
formatTaskBlock(node) {
|
|
122
|
+
const lines = [];
|
|
123
|
+
let header = "Task";
|
|
124
|
+
if (node.title) {
|
|
125
|
+
header += ` ${node.title.trim()}`;
|
|
126
|
+
}
|
|
127
|
+
lines.push(header);
|
|
128
|
+
// Steps indented 1 level under Task
|
|
129
|
+
for (const step of node.steps) {
|
|
130
|
+
lines.push(this.formatStepBlock(step, 1));
|
|
131
|
+
}
|
|
132
|
+
return lines.join("\n");
|
|
133
|
+
}
|
|
134
|
+
// ── Step Block ──────────────────────────────────────────────────────────
|
|
135
|
+
formatStepBlock(node, indent) {
|
|
136
|
+
const lines = [];
|
|
137
|
+
const pad = this.indent(indent);
|
|
138
|
+
// Step Call Api <name>
|
|
139
|
+
lines.push(`${pad}Step ${node.callType} ${node.targetType} ${node.targetName}`);
|
|
140
|
+
// Sub-blocks indented one more level
|
|
141
|
+
const subIndent = indent + 1;
|
|
142
|
+
if (node.params) {
|
|
143
|
+
lines.push(this.formatParamsBlock(node.params, subIndent));
|
|
144
|
+
}
|
|
145
|
+
if (node.capture) {
|
|
146
|
+
lines.push(this.formatCaptureBlock(node.capture, subIndent));
|
|
147
|
+
}
|
|
148
|
+
if (node.check) {
|
|
149
|
+
lines.push(this.formatCheckBlock(node.check, subIndent));
|
|
150
|
+
}
|
|
151
|
+
return lines.join("\n");
|
|
152
|
+
}
|
|
153
|
+
// ── Params / Capture / Check ────────────────────────────────────────────
|
|
154
|
+
formatParamsBlock(node, indent) {
|
|
155
|
+
const pad = this.indent(indent);
|
|
156
|
+
const lines = [`${pad}Params`];
|
|
157
|
+
for (const entry of node.entries) {
|
|
158
|
+
lines.push(this.formatKeyValuePair(entry, indent));
|
|
159
|
+
}
|
|
160
|
+
return lines.join("\n");
|
|
161
|
+
}
|
|
162
|
+
formatCaptureBlock(node, indent) {
|
|
163
|
+
const pad = this.indent(indent);
|
|
164
|
+
const lines = [`${pad}Capture`];
|
|
165
|
+
for (const entry of node.entries) {
|
|
166
|
+
lines.push(this.formatKeyValuePair(entry, indent));
|
|
167
|
+
}
|
|
168
|
+
return lines.join("\n");
|
|
169
|
+
}
|
|
170
|
+
formatCheckBlock(node, indent) {
|
|
171
|
+
const pad = this.indent(indent);
|
|
172
|
+
const lines = [`${pad}Check`];
|
|
173
|
+
for (const condition of node.conditions) {
|
|
174
|
+
lines.push(this.formatCondition(condition, indent));
|
|
175
|
+
}
|
|
176
|
+
return lines.join("\n");
|
|
177
|
+
}
|
|
178
|
+
// ── Key-Value Pair ──────────────────────────────────────────────────────
|
|
179
|
+
formatKeyValuePair(node, indent) {
|
|
180
|
+
const pad = this.indent(indent);
|
|
181
|
+
// Format key
|
|
182
|
+
const key = node.isKeyQuoted ? `'${node.key}'` : node.key;
|
|
183
|
+
// Format value
|
|
184
|
+
let value;
|
|
185
|
+
if (node.isMultiline) {
|
|
186
|
+
value = `\`${node.value}\``;
|
|
187
|
+
}
|
|
188
|
+
else if (node.isValueQuoted || this.options.quoteValues) {
|
|
189
|
+
value = `'${node.value}'`;
|
|
190
|
+
}
|
|
191
|
+
else {
|
|
192
|
+
value = node.value;
|
|
193
|
+
}
|
|
194
|
+
return `${pad}- ${key}: ${value}`;
|
|
195
|
+
}
|
|
196
|
+
// ── Condition ─────────────────────────────────────────────────────────
|
|
197
|
+
formatCondition(node, indent) {
|
|
198
|
+
const pad = this.indent(indent);
|
|
199
|
+
let line = `${pad}- ${node.lhs} ${node.operator} ${node.rhs}`;
|
|
200
|
+
// Append OR chains
|
|
201
|
+
for (const orExpr of node.orConditions) {
|
|
202
|
+
line += ` OR ${orExpr.lhs} ${orExpr.operator} ${orExpr.rhs}`;
|
|
203
|
+
}
|
|
204
|
+
return line;
|
|
205
|
+
}
|
|
206
|
+
// ── Comment ─────────────────────────────────────────────────────────────
|
|
207
|
+
formatComment(node) {
|
|
208
|
+
return `##${node.text}`;
|
|
209
|
+
}
|
|
210
|
+
// ── Helpers ─────────────────────────────────────────────────────────────
|
|
211
|
+
indent(level) {
|
|
212
|
+
return " ".repeat(level * this.options.indentSize);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { TokenType } from "../../tokenType.js";
|
|
2
|
+
import { keyValueGrammer } from "./keyvalue.grammer.js";
|
|
3
|
+
export const apiGrammer = [
|
|
4
|
+
{
|
|
5
|
+
name: "api statement",
|
|
6
|
+
regex: /^\s*(Api)(?:\s+(GET|POST|PUT|DELETE|PATCH))?\s+((#)(\S+))(?:\s+(.*))?$/,
|
|
7
|
+
groups: [
|
|
8
|
+
{
|
|
9
|
+
tokenType: TokenType.Api,
|
|
10
|
+
index: 1,
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
tokenType: TokenType.ApiMethod,
|
|
14
|
+
index: 2,
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
tokenType: TokenType.Hash,
|
|
18
|
+
index: 4,
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
tokenType: TokenType.Identifier,
|
|
22
|
+
index: 5,
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
tokenType: TokenType.Title,
|
|
26
|
+
index: 6,
|
|
27
|
+
},
|
|
28
|
+
],
|
|
29
|
+
isOptional: true,
|
|
30
|
+
isMultiline: false,
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
name: "url",
|
|
34
|
+
regex: /^\s*(Url)\s+(.*)$/,
|
|
35
|
+
groups: [
|
|
36
|
+
{
|
|
37
|
+
tokenType: TokenType.Url,
|
|
38
|
+
index: 1,
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
tokenType: TokenType.Value,
|
|
42
|
+
index: 2,
|
|
43
|
+
},
|
|
44
|
+
],
|
|
45
|
+
isMultiline: false,
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
name: "Header",
|
|
49
|
+
regex: /^\s*(Header)/,
|
|
50
|
+
groups: [
|
|
51
|
+
{
|
|
52
|
+
tokenType: TokenType.Header,
|
|
53
|
+
index: 1,
|
|
54
|
+
},
|
|
55
|
+
],
|
|
56
|
+
next: [
|
|
57
|
+
{
|
|
58
|
+
...keyValueGrammer,
|
|
59
|
+
moveNextLine: true,
|
|
60
|
+
},
|
|
61
|
+
],
|
|
62
|
+
isMultiline: false,
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
name: "Body",
|
|
66
|
+
regex: /^\s*(Body)\s(\w+)\s*/,
|
|
67
|
+
groups: [
|
|
68
|
+
{
|
|
69
|
+
tokenType: TokenType.Body,
|
|
70
|
+
index: 1,
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
tokenType: TokenType.BodyType,
|
|
74
|
+
index: 2,
|
|
75
|
+
},
|
|
76
|
+
],
|
|
77
|
+
next: [
|
|
78
|
+
{
|
|
79
|
+
name: "multilineValue",
|
|
80
|
+
regex: /(`)([\s\S]*?)(`)/,
|
|
81
|
+
isOptional: false,
|
|
82
|
+
groups: [
|
|
83
|
+
{
|
|
84
|
+
tokenType: TokenType.Backtick,
|
|
85
|
+
index: 1,
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
tokenType: TokenType.Scalar,
|
|
89
|
+
index: 2,
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
tokenType: TokenType.Backtick,
|
|
93
|
+
index: 3,
|
|
94
|
+
},
|
|
95
|
+
],
|
|
96
|
+
start: /(`)([\s\S]*?)/,
|
|
97
|
+
end: /(`)([\s\S]*?)(`)/,
|
|
98
|
+
mergeLines: true,
|
|
99
|
+
},
|
|
100
|
+
],
|
|
101
|
+
},
|
|
102
|
+
];
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { TokenType } from "../../tokenType.js";
|
|
2
|
+
import { keyValueGrammer } from "./keyvalue.grammer.js";
|
|
3
|
+
export const captureGrammer = {
|
|
4
|
+
name: "Capture",
|
|
5
|
+
regex: /^\s*(Capture)/,
|
|
6
|
+
groups: [
|
|
7
|
+
{
|
|
8
|
+
tokenType: TokenType.Capture,
|
|
9
|
+
index: 1,
|
|
10
|
+
},
|
|
11
|
+
],
|
|
12
|
+
next: [
|
|
13
|
+
{
|
|
14
|
+
...keyValueGrammer,
|
|
15
|
+
moveNextLine: true,
|
|
16
|
+
},
|
|
17
|
+
],
|
|
18
|
+
isOptional: true,
|
|
19
|
+
isMultiline: false,
|
|
20
|
+
moveNextLine: true,
|
|
21
|
+
};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { TokenType } from "../../tokenType.js";
|
|
2
|
+
import { conditionGrammer } from "./conditions.grammer.js";
|
|
3
|
+
export const checkGrammer = {
|
|
4
|
+
name: "Check",
|
|
5
|
+
regex: /^\s*(Check)/,
|
|
6
|
+
groups: [
|
|
7
|
+
{
|
|
8
|
+
tokenType: TokenType.Check,
|
|
9
|
+
index: 1,
|
|
10
|
+
},
|
|
11
|
+
],
|
|
12
|
+
next: [
|
|
13
|
+
{
|
|
14
|
+
...conditionGrammer,
|
|
15
|
+
moveNextLine: true,
|
|
16
|
+
},
|
|
17
|
+
],
|
|
18
|
+
isOptional: true,
|
|
19
|
+
isMultiline: false,
|
|
20
|
+
moveNextLine: true,
|
|
21
|
+
};
|