steel-lang-2 1.0.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 +11 -0
- package/README.md +119 -0
- package/package.json +25 -0
- package/src/environment.js +35 -0
- package/src/index.js +49 -0
- package/src/interpreter.js +99 -0
- package/src/lexer.js +94 -0
- package/src/parser.js +198 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 theworker02
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to do so, subject to the following conditions:
|
|
7
|
+
|
|
8
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
9
|
+
|
|
10
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
11
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE
|
package/README.md
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
[](https://www.npmjs.com/package/steel-lang-2)
|
|
2
|
+
|
|
3
|
+
Steel is a simple, readable programming language designed to be easy to learn and intuitive to use.
|
|
4
|
+
|
|
5
|
+
It focuses on clean syntax, minimal complexity, and fast execution through a custom JavaScript interpreter.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install steel-lang-2
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
Features:
|
|
18
|
+
β’ π§ Human-readable syntax
|
|
19
|
+
β’ β‘ Lightweight interpreter (Node.js)
|
|
20
|
+
β’ π§± Simple variable system
|
|
21
|
+
β’ π Control flow (if statements, loops)
|
|
22
|
+
β’ π¬ Built-in output (say)
|
|
23
|
+
β’ π¨ VS Code syntax highlighting support
|
|
24
|
+
β’ π§ Easy to extend and modify
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
Project Structure:
|
|
30
|
+
|
|
31
|
+
steel-lang/
|
|
32
|
+
βββ src/ # Language engine
|
|
33
|
+
β βββ lexer.js
|
|
34
|
+
β βββ parser.js
|
|
35
|
+
β βββ interpreter.js
|
|
36
|
+
β βββ environment.js
|
|
37
|
+
β βββ index.js
|
|
38
|
+
βββ examples/ # Example programs
|
|
39
|
+
β βββ hello.steel
|
|
40
|
+
β βββ logic.steel
|
|
41
|
+
β βββ variables.steel
|
|
42
|
+
βββ stl/ # VS Code extension
|
|
43
|
+
β βββ syntaxes/
|
|
44
|
+
β βββ icons/
|
|
45
|
+
β βββ package.json
|
|
46
|
+
βββ README.md
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
π Getting Started
|
|
53
|
+
|
|
54
|
+
Clone the repository
|
|
55
|
+
|
|
56
|
+
1. git clone https://github.com/theworker02/steel-lang.git
|
|
57
|
+
|
|
58
|
+
2. cd steel-lang
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
Run a Steel program
|
|
63
|
+
|
|
64
|
+
3. "node src/index.js examples/hello.steel"
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
π§ͺ Example:
|
|
68
|
+
|
|
69
|
+
set x to 10
|
|
70
|
+
|
|
71
|
+
if x > 5 then
|
|
72
|
+
say "Steel is working!"
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
https://steel-lang.base44.app
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
π§© VS Code Extension:
|
|
80
|
+
|
|
81
|
+
Steel includes a custom VS Code extension for syntax highlighting.
|
|
82
|
+
|
|
83
|
+
Run locally: βcd stlβ
|
|
84
|
+
|
|
85
|
+
Then open the folder in VS code and press: F5
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
π― Goals:
|
|
89
|
+
|
|
90
|
+
β’ Keep syntax simple and readable
|
|
91
|
+
β’ Build a fully custom language ecosystem
|
|
92
|
+
β’ Add functions, modules, and advanced features
|
|
93
|
+
β’ Expand tooling (CLI, debugger, autocomplete)
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
https://steel-lang.base44.app
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
π οΈ Future Plans:
|
|
100
|
+
|
|
101
|
+
β’ CLI command (steel run file.steel)
|
|
102
|
+
β’ Error handling improvements
|
|
103
|
+
β’ Function system
|
|
104
|
+
β’ Package manager (maybe π)
|
|
105
|
+
β’ Full VS Code integration (IntelliSense)
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
π€ Contributing:
|
|
109
|
+
|
|
110
|
+
Pull requests are welcome. This project is actively evolving and is considered to me still a work in progress. A lot has to be done honestly.
|
|
111
|
+
|
|
112
|
+
License: MIT License
|
|
113
|
+
|
|
114
|
+
Author: theworker02
|
|
115
|
+
|
|
116
|
+
βοΈ Status:
|
|
117
|
+
|
|
118
|
+
π§ Early Development/Work in progress, but fully functional
|
|
119
|
+
|
package/package.json
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"icon": "icons/steel.png",
|
|
3
|
+
"name": "steel-lang-2",
|
|
4
|
+
"version": "1.0.0",
|
|
5
|
+
"files": [
|
|
6
|
+
"src/",
|
|
7
|
+
"icons/"
|
|
8
|
+
],
|
|
9
|
+
"description": "A simple, human-readable scripting language",
|
|
10
|
+
"main": "src/index.js",
|
|
11
|
+
"scripts": {
|
|
12
|
+
"start": "node src/index.js examples/hello.steel"
|
|
13
|
+
},
|
|
14
|
+
"keywords": [
|
|
15
|
+
"language",
|
|
16
|
+
"interpreter",
|
|
17
|
+
"steel"
|
|
18
|
+
],
|
|
19
|
+
"author": "theworker02",
|
|
20
|
+
"license": "MIT",
|
|
21
|
+
"repository": {
|
|
22
|
+
"type": "git",
|
|
23
|
+
"url": "https://github.com/magnexis/Steel-lang.git"
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
class Environment {
|
|
2
|
+
|
|
3
|
+
constructor() {
|
|
4
|
+
|
|
5
|
+
this.variables = {};
|
|
6
|
+
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
set(name, value) {
|
|
12
|
+
|
|
13
|
+
this.variables[name] = value;
|
|
14
|
+
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
get(name) {
|
|
20
|
+
|
|
21
|
+
if (!(name in this.variables)) {
|
|
22
|
+
|
|
23
|
+
throw new Error(`Variable "${name}" is not defined`);
|
|
24
|
+
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return this.variables[name];
|
|
28
|
+
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
module.exports = Environment;
|
package/src/index.js
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
|
|
3
|
+
const { tokenize } = require('./lexer');
|
|
4
|
+
|
|
5
|
+
const { parse } = require('./parser');
|
|
6
|
+
|
|
7
|
+
const { interpret } = require('./interpreter');
|
|
8
|
+
|
|
9
|
+
const Environment = require('./environment');
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
const file = process.argv[2];
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
if (!file) {
|
|
18
|
+
|
|
19
|
+
console.log("Usage: node src/index.js <file.steel>");
|
|
20
|
+
|
|
21
|
+
process.exit(1);
|
|
22
|
+
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
try {
|
|
28
|
+
|
|
29
|
+
const code = fs.readFileSync(file, 'utf-8');
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
const tokens = tokenize(code);
|
|
34
|
+
|
|
35
|
+
const ast = parse(tokens);
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
const env = new Environment();
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
interpret(ast, env);
|
|
44
|
+
|
|
45
|
+
} catch (err) {
|
|
46
|
+
|
|
47
|
+
console.error("Error:", err.message);
|
|
48
|
+
|
|
49
|
+
}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
function interpret(ast, env) {
|
|
2
|
+
|
|
3
|
+
for (let node of ast) {
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
if (node.type === 'VariableDeclaration') {
|
|
8
|
+
|
|
9
|
+
const value = evaluate(node.value, env);
|
|
10
|
+
|
|
11
|
+
env.set(node.name, value);
|
|
12
|
+
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
if (node.type === 'SayStatement') {
|
|
18
|
+
|
|
19
|
+
const value = evaluate(node.value, env);
|
|
20
|
+
|
|
21
|
+
console.log(value);
|
|
22
|
+
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
if (node.type === 'IfStatement') {
|
|
28
|
+
|
|
29
|
+
const condition = evaluate(node.condition, env);
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
if (condition) {
|
|
34
|
+
|
|
35
|
+
interpret(node.body, env);
|
|
36
|
+
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
function evaluate(node, env) {
|
|
50
|
+
|
|
51
|
+
if (node.type === 'NUMBER') return node.value;
|
|
52
|
+
|
|
53
|
+
if (node.type === 'STRING') return node.value;
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
if (node.type === 'IDENTIFIER') {
|
|
58
|
+
|
|
59
|
+
return env.get(node.value);
|
|
60
|
+
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
if (node.type === 'BinaryExpression') {
|
|
66
|
+
|
|
67
|
+
const left = evaluate(node.left, env);
|
|
68
|
+
|
|
69
|
+
const right = evaluate(node.right, env);
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
switch (node.operator) {
|
|
74
|
+
|
|
75
|
+
case 'PLUS': return left + right;
|
|
76
|
+
|
|
77
|
+
case 'MINUS': return left - right;
|
|
78
|
+
|
|
79
|
+
case 'GT': return left > right;
|
|
80
|
+
|
|
81
|
+
case 'LT': return left < right;
|
|
82
|
+
|
|
83
|
+
case 'EQEQ': return left === right;
|
|
84
|
+
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
return null;
|
|
92
|
+
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
module.exports = { interpret };
|
|
98
|
+
|
|
99
|
+
|
package/src/lexer.js
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
const KEYWORDS = {
|
|
2
|
+
|
|
3
|
+
set: 'SET',
|
|
4
|
+
|
|
5
|
+
to: 'TO',
|
|
6
|
+
|
|
7
|
+
say: 'SAY',
|
|
8
|
+
|
|
9
|
+
if: 'IF',
|
|
10
|
+
|
|
11
|
+
then: 'THEN',
|
|
12
|
+
|
|
13
|
+
end: 'END',
|
|
14
|
+
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
const OPERATORS = {
|
|
18
|
+
'+': 'PLUS',
|
|
19
|
+
'-': 'MINUS',
|
|
20
|
+
'>': 'GT',
|
|
21
|
+
'<': 'LT',
|
|
22
|
+
'==': 'EQEQ',
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
function tokenize(input) {
|
|
28
|
+
|
|
29
|
+
const tokens = [];
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
const regex = /"[^"]*"|==|[+\-<>]|[^\s]+/g;
|
|
34
|
+
|
|
35
|
+
const words = input.match(regex) || [];
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
for (let word of words) {
|
|
40
|
+
|
|
41
|
+
if (OPERATORS[word]) {
|
|
42
|
+
|
|
43
|
+
tokens.push({ type: OPERATORS[word] });
|
|
44
|
+
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
else if (!isNaN(word)) {
|
|
48
|
+
|
|
49
|
+
tokens.push({ type: 'NUMBER', value: Number(word) });
|
|
50
|
+
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
else if (word.startsWith('"')) {
|
|
54
|
+
|
|
55
|
+
tokens.push({
|
|
56
|
+
|
|
57
|
+
type: 'STRING',
|
|
58
|
+
|
|
59
|
+
value: word.slice(1, -1)
|
|
60
|
+
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
else if (KEYWORDS[word]) {
|
|
66
|
+
|
|
67
|
+
tokens.push({ type: KEYWORDS[word] });
|
|
68
|
+
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
else {
|
|
72
|
+
|
|
73
|
+
tokens.push({
|
|
74
|
+
|
|
75
|
+
type: 'IDENTIFIER',
|
|
76
|
+
|
|
77
|
+
value: word
|
|
78
|
+
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
return tokens;
|
|
88
|
+
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
module.exports = { tokenize };
|
|
94
|
+
|
package/src/parser.js
ADDED
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
function parse(tokens) {
|
|
2
|
+
|
|
3
|
+
let i = 0;
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
function parseExpression() {
|
|
8
|
+
|
|
9
|
+
if (i >= tokens.length) {
|
|
10
|
+
throw new SyntaxError('Unexpected end of input');
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const left = tokens[i++];
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
if (tokens[i] && ['PLUS', 'MINUS', 'GT', 'LT', 'EQEQ'].includes(tokens[i].type)) {
|
|
18
|
+
|
|
19
|
+
const operator = tokens[i++];
|
|
20
|
+
|
|
21
|
+
if (!tokens[i]) throw new SyntaxError('Expected right-hand operand');
|
|
22
|
+
|
|
23
|
+
const right = tokens[i++];
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
return {
|
|
28
|
+
|
|
29
|
+
type: 'BinaryExpression',
|
|
30
|
+
|
|
31
|
+
left,
|
|
32
|
+
|
|
33
|
+
operator: operator.type,
|
|
34
|
+
|
|
35
|
+
right
|
|
36
|
+
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
return left;
|
|
44
|
+
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
const ast = [];
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
while (i < tokens.length) {
|
|
54
|
+
|
|
55
|
+
const token = tokens[i];
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
// set x to ...
|
|
60
|
+
|
|
61
|
+
if (token.type === 'SET') {
|
|
62
|
+
|
|
63
|
+
if (!tokens[i + 1] || tokens[i + 1].type !== 'IDENTIFIER') {
|
|
64
|
+
throw new SyntaxError("'SET' expects a variable name");
|
|
65
|
+
}
|
|
66
|
+
if (!tokens[i + 2] || tokens[i + 2].type !== 'TO') {
|
|
67
|
+
throw new SyntaxError("'SET' expects 'TO' after variable name");
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const name = tokens[i + 1].value;
|
|
71
|
+
|
|
72
|
+
i += 3; // skip SET name TO
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
const value = parseExpression();
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
ast.push({
|
|
81
|
+
|
|
82
|
+
type: 'VariableDeclaration',
|
|
83
|
+
|
|
84
|
+
name,
|
|
85
|
+
|
|
86
|
+
value
|
|
87
|
+
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
// say ...
|
|
95
|
+
|
|
96
|
+
else if (token.type === 'SAY') {
|
|
97
|
+
|
|
98
|
+
i++;
|
|
99
|
+
|
|
100
|
+
const value = parseExpression();
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
ast.push({
|
|
105
|
+
|
|
106
|
+
type: 'SayStatement',
|
|
107
|
+
|
|
108
|
+
value
|
|
109
|
+
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
// if condition then ... end
|
|
117
|
+
|
|
118
|
+
else if (token.type === 'IF') {
|
|
119
|
+
|
|
120
|
+
i++;
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
const condition = parseExpression();
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
if (!tokens[i] || tokens[i].type !== 'THEN') {
|
|
129
|
+
throw new SyntaxError("'IF' expects 'THEN' after condition");
|
|
130
|
+
}
|
|
131
|
+
i++; // skip THEN
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
const body = [];
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
while (tokens[i] && tokens[i].type !== 'END') {
|
|
140
|
+
|
|
141
|
+
if (tokens[i].type === 'SAY') {
|
|
142
|
+
|
|
143
|
+
i++;
|
|
144
|
+
|
|
145
|
+
body.push({
|
|
146
|
+
|
|
147
|
+
type: 'SayStatement',
|
|
148
|
+
|
|
149
|
+
value: parseExpression()
|
|
150
|
+
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
} else {
|
|
154
|
+
|
|
155
|
+
i++;
|
|
156
|
+
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
if (!tokens[i]) {
|
|
162
|
+
throw new SyntaxError("'IF' block missing 'END'");
|
|
163
|
+
}
|
|
164
|
+
i++; // skip END
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
ast.push({
|
|
169
|
+
|
|
170
|
+
type: 'IfStatement',
|
|
171
|
+
|
|
172
|
+
condition,
|
|
173
|
+
|
|
174
|
+
body
|
|
175
|
+
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
else {
|
|
183
|
+
|
|
184
|
+
i++;
|
|
185
|
+
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
return ast;
|
|
193
|
+
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
module.exports = { parse };
|