zexus 1.8.1 → 1.8.2
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/bin/zexus +12 -2
- package/bin/zpics +12 -2
- package/bin/zpm +12 -2
- package/bin/zx +12 -2
- package/bin/zx-deploy +12 -2
- package/bin/zx-dev +12 -2
- package/bin/zx-run +12 -2
- package/package.json +1 -1
- package/scripts/postinstall.js +192 -41
- package/src/zexus/__init__.py +1 -1
- package/src/zexus/cli/main.py +1 -1
- package/src/zexus/cli/zpm.py +1 -1
- package/src/zexus/parser/parser.py +34 -9
- package/src/zexus/parser/strategy_context.py +91 -2
- package/src/zexus/vm/compiler.py +469 -48
- package/src/zexus/vm/vm.py +249 -32
- package/src/zexus/zpm/package_manager.py +1 -1
- package/src/zexus.egg-info/PKG-INFO +1 -1
- package/src/zexus.egg-info/SOURCES.txt +9 -0
- package/src/zexus.egg-info/entry_points.txt +8 -0
package/bin/zexus
CHANGED
|
@@ -1,2 +1,12 @@
|
|
|
1
|
-
#!/usr/bin/env
|
|
2
|
-
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
const { spawn } = require('child_process');
|
|
3
|
+
const proc = spawn('python3', ['-m', 'zexus', ...process.argv.slice(2)], { stdio: 'inherit' });
|
|
4
|
+
proc.on('error', (err) => {
|
|
5
|
+
if (err.code === 'ENOENT') {
|
|
6
|
+
console.error('Error: python3 not found. Please install Python 3.8+ to use Zexus.');
|
|
7
|
+
process.exit(1);
|
|
8
|
+
}
|
|
9
|
+
console.error('Error:', err.message);
|
|
10
|
+
process.exit(1);
|
|
11
|
+
});
|
|
12
|
+
proc.on('close', (code) => process.exit(code ?? 1));
|
package/bin/zpics
CHANGED
|
@@ -1,2 +1,12 @@
|
|
|
1
|
-
#!/usr/bin/env
|
|
2
|
-
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
const { spawn } = require('child_process');
|
|
3
|
+
const proc = spawn('python3', ['-m', 'zexus.pics', ...process.argv.slice(2)], { stdio: 'inherit' });
|
|
4
|
+
proc.on('error', (err) => {
|
|
5
|
+
if (err.code === 'ENOENT') {
|
|
6
|
+
console.error('Error: python3 not found. Please install Python 3.8+ to use Zexus.');
|
|
7
|
+
process.exit(1);
|
|
8
|
+
}
|
|
9
|
+
console.error('Error:', err.message);
|
|
10
|
+
process.exit(1);
|
|
11
|
+
});
|
|
12
|
+
proc.on('close', (code) => process.exit(code ?? 1));
|
package/bin/zpm
CHANGED
|
@@ -1,2 +1,12 @@
|
|
|
1
|
-
#!/usr/bin/env
|
|
2
|
-
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
const { spawn } = require('child_process');
|
|
3
|
+
const proc = spawn('python3', ['-m', 'zexus.zpm', ...process.argv.slice(2)], { stdio: 'inherit' });
|
|
4
|
+
proc.on('error', (err) => {
|
|
5
|
+
if (err.code === 'ENOENT') {
|
|
6
|
+
console.error('Error: python3 not found. Please install Python 3.8+ to use Zexus.');
|
|
7
|
+
process.exit(1);
|
|
8
|
+
}
|
|
9
|
+
console.error('Error:', err.message);
|
|
10
|
+
process.exit(1);
|
|
11
|
+
});
|
|
12
|
+
proc.on('close', (code) => process.exit(code ?? 1));
|
package/bin/zx
CHANGED
|
@@ -1,2 +1,12 @@
|
|
|
1
|
-
#!/usr/bin/env
|
|
2
|
-
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
const { spawn } = require('child_process');
|
|
3
|
+
const proc = spawn('python3', ['-m', 'zexus', ...process.argv.slice(2)], { stdio: 'inherit' });
|
|
4
|
+
proc.on('error', (err) => {
|
|
5
|
+
if (err.code === 'ENOENT') {
|
|
6
|
+
console.error('Error: python3 not found. Please install Python 3.8+ to use Zexus.');
|
|
7
|
+
process.exit(1);
|
|
8
|
+
}
|
|
9
|
+
console.error('Error:', err.message);
|
|
10
|
+
process.exit(1);
|
|
11
|
+
});
|
|
12
|
+
proc.on('close', (code) => process.exit(code ?? 1));
|
package/bin/zx-deploy
CHANGED
|
@@ -1,2 +1,12 @@
|
|
|
1
|
-
#!/usr/bin/env
|
|
2
|
-
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
const { spawn } = require('child_process');
|
|
3
|
+
const proc = spawn('python3', ['-m', 'zexus.deploy', ...process.argv.slice(2)], { stdio: 'inherit' });
|
|
4
|
+
proc.on('error', (err) => {
|
|
5
|
+
if (err.code === 'ENOENT') {
|
|
6
|
+
console.error('Error: python3 not found. Please install Python 3.8+ to use Zexus.');
|
|
7
|
+
process.exit(1);
|
|
8
|
+
}
|
|
9
|
+
console.error('Error:', err.message);
|
|
10
|
+
process.exit(1);
|
|
11
|
+
});
|
|
12
|
+
proc.on('close', (code) => process.exit(code ?? 1));
|
package/bin/zx-dev
CHANGED
|
@@ -1,2 +1,12 @@
|
|
|
1
|
-
#!/usr/bin/env
|
|
2
|
-
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
const { spawn } = require('child_process');
|
|
3
|
+
const proc = spawn('python3', ['-m', 'zexus.dev', ...process.argv.slice(2)], { stdio: 'inherit' });
|
|
4
|
+
proc.on('error', (err) => {
|
|
5
|
+
if (err.code === 'ENOENT') {
|
|
6
|
+
console.error('Error: python3 not found. Please install Python 3.8+ to use Zexus.');
|
|
7
|
+
process.exit(1);
|
|
8
|
+
}
|
|
9
|
+
console.error('Error:', err.message);
|
|
10
|
+
process.exit(1);
|
|
11
|
+
});
|
|
12
|
+
proc.on('close', (code) => process.exit(code ?? 1));
|
package/bin/zx-run
CHANGED
|
@@ -1,2 +1,12 @@
|
|
|
1
|
-
#!/usr/bin/env
|
|
2
|
-
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
const { spawn } = require('child_process');
|
|
3
|
+
const proc = spawn('python3', ['-m', 'zexus.runner', ...process.argv.slice(2)], { stdio: 'inherit' });
|
|
4
|
+
proc.on('error', (err) => {
|
|
5
|
+
if (err.code === 'ENOENT') {
|
|
6
|
+
console.error('Error: python3 not found. Please install Python 3.8+ to use Zexus.');
|
|
7
|
+
process.exit(1);
|
|
8
|
+
}
|
|
9
|
+
console.error('Error:', err.message);
|
|
10
|
+
process.exit(1);
|
|
11
|
+
});
|
|
12
|
+
proc.on('close', (code) => process.exit(code ?? 1));
|
package/package.json
CHANGED
package/scripts/postinstall.js
CHANGED
|
@@ -3,74 +3,225 @@
|
|
|
3
3
|
const { execSync } = require('child_process');
|
|
4
4
|
const fs = require('fs');
|
|
5
5
|
const path = require('path');
|
|
6
|
+
const os = require('os');
|
|
6
7
|
|
|
7
8
|
console.log('\n🚀 Installing Zexus Programming Language...\n');
|
|
8
9
|
|
|
9
|
-
//
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
10
|
+
// ---------------------------------------------------------------------------
|
|
11
|
+
// Helpers
|
|
12
|
+
// ---------------------------------------------------------------------------
|
|
13
|
+
|
|
14
|
+
function hasCommand(cmd) {
|
|
15
|
+
try {
|
|
16
|
+
execSync(`${cmd} --version`, { stdio: 'ignore' });
|
|
17
|
+
return true;
|
|
18
|
+
} catch {
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
17
21
|
}
|
|
18
22
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
23
|
+
function run(cmd, opts = {}) {
|
|
24
|
+
return execSync(cmd, { encoding: 'utf-8', stdio: 'inherit', ...opts });
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function runQuiet(cmd) {
|
|
28
|
+
return execSync(cmd, { encoding: 'utf-8', stdio: 'pipe' });
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const isWin = os.platform() === 'win32';
|
|
32
|
+
const pip = isWin ? 'pip' : 'pip3';
|
|
33
|
+
const python = isWin ? 'python' : 'python3';
|
|
34
|
+
|
|
35
|
+
// ---------------------------------------------------------------------------
|
|
36
|
+
// 1. Python — check or install
|
|
37
|
+
// ---------------------------------------------------------------------------
|
|
38
|
+
|
|
39
|
+
let pythonAvailable = false;
|
|
40
|
+
|
|
41
|
+
if (hasCommand(python)) {
|
|
42
|
+
try {
|
|
43
|
+
const ver = runQuiet(`${python} --version`).trim();
|
|
44
|
+
console.log(`✓ Found ${ver}`);
|
|
45
|
+
// Verify >= 3.8
|
|
46
|
+
runQuiet(`${python} -c "import sys; sys.exit(0 if sys.version_info >= (3, 8) else 1)"`);
|
|
47
|
+
console.log('✓ Python version is 3.8 or higher');
|
|
48
|
+
pythonAvailable = true;
|
|
49
|
+
} catch {
|
|
50
|
+
console.error('❌ Python was found but version is below 3.8.');
|
|
51
|
+
console.error(' Please upgrade to Python 3.8+: https://www.python.org/downloads/');
|
|
52
|
+
}
|
|
53
|
+
} else {
|
|
54
|
+
console.warn('⚠️ python3 not found on PATH.');
|
|
55
|
+
// Attempt auto-install on common platforms
|
|
56
|
+
if (os.platform() === 'linux') {
|
|
57
|
+
console.log(' Attempting to install python3 via apt...');
|
|
58
|
+
try {
|
|
59
|
+
run('sudo apt-get update -qq && sudo apt-get install -y -qq python3 python3-pip python3-venv');
|
|
60
|
+
pythonAvailable = true;
|
|
61
|
+
console.log('✓ Python 3 installed via apt');
|
|
62
|
+
} catch {
|
|
63
|
+
console.warn(' Could not auto-install Python. Please install manually:');
|
|
64
|
+
console.warn(' https://www.python.org/downloads/');
|
|
65
|
+
}
|
|
66
|
+
} else if (os.platform() === 'darwin') {
|
|
67
|
+
if (hasCommand('brew')) {
|
|
68
|
+
console.log(' Attempting to install python3 via Homebrew...');
|
|
69
|
+
try {
|
|
70
|
+
run('brew install python@3');
|
|
71
|
+
pythonAvailable = true;
|
|
72
|
+
console.log('✓ Python 3 installed via Homebrew');
|
|
73
|
+
} catch {
|
|
74
|
+
console.warn(' Could not auto-install Python. Please install manually:');
|
|
75
|
+
console.warn(' https://www.python.org/downloads/');
|
|
76
|
+
}
|
|
77
|
+
} else {
|
|
78
|
+
console.warn(' Please install Python 3.8+: https://www.python.org/downloads/');
|
|
79
|
+
}
|
|
80
|
+
} else {
|
|
81
|
+
console.warn(' Please install Python 3.8+: https://www.python.org/downloads/');
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
if (!pythonAvailable) {
|
|
86
|
+
console.error('\n❌ Python 3.8+ is required but could not be found or installed.');
|
|
87
|
+
console.error(' Install it from: https://www.python.org/downloads/');
|
|
88
|
+
console.error(' Then re-run: npm rebuild zexus');
|
|
25
89
|
process.exit(1);
|
|
26
90
|
}
|
|
27
91
|
|
|
28
|
-
//
|
|
92
|
+
// ---------------------------------------------------------------------------
|
|
93
|
+
// 2. pip — ensure available
|
|
94
|
+
// ---------------------------------------------------------------------------
|
|
95
|
+
|
|
96
|
+
if (!hasCommand(pip) && !hasCommand('pip')) {
|
|
97
|
+
console.log(' pip not found — bootstrapping...');
|
|
98
|
+
try {
|
|
99
|
+
run(`${python} -m ensurepip --upgrade`);
|
|
100
|
+
} catch {
|
|
101
|
+
try {
|
|
102
|
+
run(`${python} -m pip install --upgrade pip`);
|
|
103
|
+
} catch {
|
|
104
|
+
console.warn('⚠️ Could not bootstrap pip. Python package install may fail.');
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// ---------------------------------------------------------------------------
|
|
110
|
+
// 3. Install Zexus Python package
|
|
111
|
+
// ---------------------------------------------------------------------------
|
|
112
|
+
|
|
29
113
|
console.log('\n📦 Installing Zexus Python package...');
|
|
114
|
+
|
|
115
|
+
// Check if zexus is already installed and up-to-date
|
|
116
|
+
let zexusInstalled = false;
|
|
30
117
|
try {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
console.error('\n❌ Failed to install Zexus Python package.');
|
|
37
|
-
console.error('Please run manually: pip3 install --user "zexus[full]"');
|
|
38
|
-
process.exit(1);
|
|
118
|
+
runQuiet(`${python} -c "import zexus"`);
|
|
119
|
+
zexusInstalled = true;
|
|
120
|
+
console.log('✓ Zexus Python package already installed');
|
|
121
|
+
} catch {
|
|
122
|
+
// Not installed yet
|
|
39
123
|
}
|
|
40
124
|
|
|
41
|
-
|
|
42
|
-
// This requires a Rust toolchain and may fail on systems without build tooling.
|
|
43
|
-
function hasCommand(cmd) {
|
|
125
|
+
if (!zexusInstalled) {
|
|
44
126
|
try {
|
|
45
|
-
|
|
46
|
-
|
|
127
|
+
run(`${python} -m pip install --user "zexus[full]"`);
|
|
128
|
+
console.log('✓ Zexus Python package installed successfully');
|
|
47
129
|
} catch {
|
|
48
|
-
|
|
130
|
+
// Retry without --user (some environments like venvs don't need it)
|
|
131
|
+
try {
|
|
132
|
+
run(`${python} -m pip install "zexus[full]"`);
|
|
133
|
+
console.log('✓ Zexus Python package installed successfully');
|
|
134
|
+
} catch {
|
|
135
|
+
console.error('❌ Failed to install Zexus Python package.');
|
|
136
|
+
console.error(` Please run manually: ${python} -m pip install "zexus[full]"`);
|
|
137
|
+
// Don't exit — commands that don't need Python may still work
|
|
138
|
+
}
|
|
49
139
|
}
|
|
50
140
|
}
|
|
51
141
|
|
|
142
|
+
// ---------------------------------------------------------------------------
|
|
143
|
+
// 4. Rust toolchain — check or install, then build rust_core
|
|
144
|
+
// ---------------------------------------------------------------------------
|
|
145
|
+
|
|
52
146
|
const pkgRoot = path.resolve(__dirname, '..');
|
|
53
147
|
const cargoToml = path.join(pkgRoot, 'rust_core', 'Cargo.toml');
|
|
54
148
|
|
|
55
|
-
if (fs.existsSync(cargoToml)
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
149
|
+
if (fs.existsSync(cargoToml)) {
|
|
150
|
+
let cargoReady = hasCommand('cargo');
|
|
151
|
+
|
|
152
|
+
if (!cargoReady) {
|
|
153
|
+
console.log('\n🦀 Rust toolchain (cargo) not found.');
|
|
154
|
+
console.log(' Attempting to install Rust via rustup...');
|
|
155
|
+
try {
|
|
156
|
+
if (isWin) {
|
|
157
|
+
// On Windows, download and run rustup-init
|
|
158
|
+
console.log(' Please install Rust manually: https://rustup.rs');
|
|
159
|
+
} else {
|
|
160
|
+
// Unix: install rustup non-interactively
|
|
161
|
+
run('curl --proto "=https" --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y');
|
|
162
|
+
// Source the cargo env so it's available in this session
|
|
163
|
+
const cargoEnv = path.join(os.homedir(), '.cargo', 'env');
|
|
164
|
+
if (fs.existsSync(cargoEnv)) {
|
|
165
|
+
// Read the cargo bin path and add to PATH for child processes
|
|
166
|
+
const cargoBin = path.join(os.homedir(), '.cargo', 'bin');
|
|
167
|
+
process.env.PATH = `${cargoBin}${path.delimiter}${process.env.PATH}`;
|
|
168
|
+
}
|
|
169
|
+
cargoReady = hasCommand('cargo');
|
|
170
|
+
if (cargoReady) {
|
|
171
|
+
console.log('✓ Rust toolchain installed via rustup');
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
} catch (err) {
|
|
175
|
+
console.warn('⚠️ Could not auto-install Rust. Continuing with pure-Python VM.');
|
|
176
|
+
console.warn(' To install manually: https://rustup.rs');
|
|
177
|
+
}
|
|
178
|
+
} else {
|
|
179
|
+
console.log('\n✓ Rust toolchain detected');
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
if (cargoReady) {
|
|
183
|
+
// Check if zexus_core is already importable
|
|
184
|
+
let rustVmInstalled = false;
|
|
185
|
+
try {
|
|
186
|
+
runQuiet(`${python} -c "import zexus_core"`);
|
|
187
|
+
rustVmInstalled = true;
|
|
188
|
+
console.log('✓ Rust VM extension (zexus_core) already installed');
|
|
189
|
+
} catch {
|
|
190
|
+
// Need to build
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
if (!rustVmInstalled) {
|
|
194
|
+
console.log('🔨 Building Rust VM extension (zexus_core)...');
|
|
195
|
+
try {
|
|
196
|
+
// Ensure maturin is available
|
|
197
|
+
try {
|
|
198
|
+
runQuiet(`${python} -m maturin --version`);
|
|
199
|
+
} catch {
|
|
200
|
+
console.log(' Installing maturin build tool...');
|
|
201
|
+
run(`${python} -m pip install --user --upgrade maturin`);
|
|
202
|
+
}
|
|
203
|
+
run(`${python} -m maturin develop -m "${cargoToml}" --release`);
|
|
204
|
+
runQuiet(`${python} -c "import zexus_core"`);
|
|
205
|
+
console.log('✓ Rust VM extension built and installed');
|
|
206
|
+
} catch (error) {
|
|
207
|
+
console.warn('\n⚠️ Rust VM build failed; continuing with pure-Python VM.');
|
|
208
|
+
console.warn(' To retry manually:');
|
|
209
|
+
console.warn(` ${python} -m pip install maturin && ${python} -m maturin develop -m rust_core/Cargo.toml --release`);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
66
212
|
}
|
|
67
213
|
} else {
|
|
68
|
-
console.log('\nℹ️
|
|
214
|
+
console.log('\nℹ️ rust_core/Cargo.toml not bundled; skipping Rust VM build.');
|
|
69
215
|
}
|
|
70
216
|
|
|
217
|
+
// ---------------------------------------------------------------------------
|
|
218
|
+
// Done
|
|
219
|
+
// ---------------------------------------------------------------------------
|
|
220
|
+
|
|
71
221
|
console.log('\n✅ Zexus installed successfully!\n');
|
|
72
222
|
console.log('Get started:');
|
|
73
223
|
console.log(' zexus --help # Show help');
|
|
74
224
|
console.log(' zx --version # Check version');
|
|
225
|
+
console.log(' zx run file.zx # Run a Zexus file');
|
|
75
226
|
console.log(' zexus examples/ # Explore examples\n');
|
|
76
227
|
console.log('Documentation: https://github.com/Zaidux/zexus-interpreter\n');
|
package/src/zexus/__init__.py
CHANGED
package/src/zexus/cli/main.py
CHANGED
|
@@ -156,7 +156,7 @@ def show_all_commands():
|
|
|
156
156
|
console.print("\n[bold green]💡 Tip:[/bold green] Use 'zx <command> --help' for detailed command options\n")
|
|
157
157
|
|
|
158
158
|
@click.group(invoke_without_command=True)
|
|
159
|
-
@click.version_option(version="1.8.
|
|
159
|
+
@click.version_option(version="1.8.2", prog_name="Zexus")
|
|
160
160
|
@click.option('--syntax-style', type=click.Choice(['universal', 'tolerable', 'auto']),
|
|
161
161
|
default='auto', help='Syntax style to use (universal=strict, tolerable=flexible)')
|
|
162
162
|
@click.option('--advanced-parsing', is_flag=True, default=True,
|
package/src/zexus/cli/zpm.py
CHANGED
|
@@ -362,6 +362,15 @@ class UltimateParser:
|
|
|
362
362
|
if match_brace_depth == 0:
|
|
363
363
|
in_match_brace = False
|
|
364
364
|
elif t.type == LAMBDA and getattr(t, 'literal', None) == '=>':
|
|
365
|
+
# Exclude watch => patterns — those are reactive watchers, not lambdas
|
|
366
|
+
if idx > 0 and all_tokens[idx - 1].type == IDENT:
|
|
367
|
+
# Check if this is a watch statement: watch <expr> =>
|
|
368
|
+
# Walk back past identifiers and dots (e.g. watch order.status =>)
|
|
369
|
+
watch_idx = idx - 2
|
|
370
|
+
while watch_idx >= 0 and all_tokens[watch_idx].type in (IDENT, DOT):
|
|
371
|
+
watch_idx -= 1
|
|
372
|
+
if watch_idx >= 0 and getattr(all_tokens[watch_idx], 'type', None) == WATCH:
|
|
373
|
+
continue # Skip watch arrows
|
|
365
374
|
has_non_match_arrow = True
|
|
366
375
|
break
|
|
367
376
|
if has_non_match_arrow:
|
|
@@ -4606,8 +4615,16 @@ class UltimateParser:
|
|
|
4606
4615
|
|
|
4607
4616
|
return UsingStatement(resource_name=resource_name, resource_expr=resource_expr, body=body)
|
|
4608
4617
|
|
|
4618
|
+
def parse_type_expression(self):
|
|
4619
|
+
"""Parse a simple type expression (identifier)."""
|
|
4620
|
+
if self.cur_token_is(IDENT):
|
|
4621
|
+
type_node = Identifier(self.cur_token.literal)
|
|
4622
|
+
self.next_token()
|
|
4623
|
+
return type_node
|
|
4624
|
+
return None
|
|
4625
|
+
|
|
4609
4626
|
def parse_channel_statement(self):
|
|
4610
|
-
"""Parse channel declaration: channel<type> name
|
|
4627
|
+
"""Parse channel declaration: channel<type>[capacity] name"""
|
|
4611
4628
|
token = self.cur_token
|
|
4612
4629
|
self.next_token() # consume CHANNEL
|
|
4613
4630
|
|
|
@@ -4625,25 +4642,33 @@ class UltimateParser:
|
|
|
4625
4642
|
|
|
4626
4643
|
self.next_token()
|
|
4627
4644
|
|
|
4645
|
+
# Optional capacity in brackets: [10]
|
|
4646
|
+
capacity = None
|
|
4647
|
+
if self.cur_token_is(LBRACKET):
|
|
4648
|
+
self.next_token()
|
|
4649
|
+
capacity = self.parse_expression(LOWEST)
|
|
4650
|
+
if not self.cur_token_is(RBRACKET):
|
|
4651
|
+
self.errors.append(f"Line {token.line}:{token.column} - Expected ']' after channel capacity")
|
|
4652
|
+
return None
|
|
4653
|
+
self.next_token()
|
|
4654
|
+
|
|
4628
4655
|
# Parse channel name
|
|
4629
4656
|
if not self.cur_token_is(IDENT):
|
|
4630
4657
|
self.errors.append(f"Line {token.line}:{token.column} - Expected channel name")
|
|
4631
4658
|
return None
|
|
4632
4659
|
|
|
4633
|
-
name = self.cur_token.literal
|
|
4660
|
+
name = Identifier(self.cur_token.literal)
|
|
4634
4661
|
self.next_token()
|
|
4635
4662
|
|
|
4636
|
-
# Optional capacity
|
|
4637
|
-
capacity
|
|
4638
|
-
if self.cur_token_is(ASSIGN):
|
|
4663
|
+
# Optional capacity via assignment: = expr
|
|
4664
|
+
if capacity is None and self.cur_token_is(ASSIGN):
|
|
4639
4665
|
self.next_token()
|
|
4640
4666
|
capacity = self.parse_expression(LOWEST)
|
|
4641
4667
|
|
|
4642
|
-
|
|
4643
|
-
|
|
4644
|
-
|
|
4668
|
+
# Semicolons are optional in modern Zexus
|
|
4669
|
+
if self.cur_token_is(SEMICOLON):
|
|
4670
|
+
self.next_token()
|
|
4645
4671
|
|
|
4646
|
-
self.next_token()
|
|
4647
4672
|
return ChannelStatement(name=name, element_type=element_type, capacity=capacity)
|
|
4648
4673
|
|
|
4649
4674
|
def parse_send_statement(self):
|
|
@@ -16,7 +16,8 @@ STATEMENT_STARTERS = {
|
|
|
16
16
|
SIMD, DEFER, PATTERN, ENUM, STREAM, WATCH, LOG, CAPABILITY, GRANT,
|
|
17
17
|
REVOKE, VALIDATE, SANITIZE, IMMUTABLE, INTERFACE, TYPE_ALIAS, MODULE,
|
|
18
18
|
PACKAGE, USING, MIDDLEWARE, AUTH, THROTTLE, CACHE, REQUIRE,
|
|
19
|
-
EMIT, PROTOCOL, SEAL
|
|
19
|
+
EMIT, PROTOCOL, SEAL,
|
|
20
|
+
CHANNEL, SEND, RECEIVE, ATOMIC, ASYNC,
|
|
20
21
|
}
|
|
21
22
|
|
|
22
23
|
_MEANINGFUL_TOKEN_TYPES = {
|
|
@@ -1267,7 +1268,8 @@ class ContextStackParser:
|
|
|
1267
1268
|
EXPORT, USE, DEBUG, ENTITY, CONTRACT, AUDIT, RESTRICT, SANDBOX, TRAIL, NATIVE, GC, INLINE,
|
|
1268
1269
|
BUFFER, SIMD, DEFER, PATTERN, ENUM, STREAM, WATCH, CAPABILITY, GRANT,
|
|
1269
1270
|
REVOKE, VALIDATE, SANITIZE, IMMUTABLE, INTERFACE, TYPE_ALIAS, MODULE,
|
|
1270
|
-
PACKAGE, USING
|
|
1271
|
+
PACKAGE, USING,
|
|
1272
|
+
CHANNEL, SEND, RECEIVE, ATOMIC, ASYNC,
|
|
1271
1273
|
}
|
|
1272
1274
|
j = assign_idx + 1
|
|
1273
1275
|
nesting_depth = 0
|
|
@@ -3983,6 +3985,35 @@ class ContextStackParser:
|
|
|
3983
3985
|
i = j
|
|
3984
3986
|
continue
|
|
3985
3987
|
|
|
3988
|
+
elif token.type == CHANNEL:
|
|
3989
|
+
# Parse CHANNEL declaration: channel<type>[capacity] name
|
|
3990
|
+
j = i + 1
|
|
3991
|
+
channel_tokens = [token]
|
|
3992
|
+
# Collect until we hit a statement starter or semicolon at nesting 0
|
|
3993
|
+
nesting = 0
|
|
3994
|
+
while j < len(tokens):
|
|
3995
|
+
tj = tokens[j]
|
|
3996
|
+
if tj.type in (LBRACKET, LT):
|
|
3997
|
+
nesting += 1
|
|
3998
|
+
elif tj.type in (RBRACKET, GT):
|
|
3999
|
+
nesting -= 1
|
|
4000
|
+
elif nesting == 0 and tj.type == SEMICOLON:
|
|
4001
|
+
j += 1 # skip semicolon
|
|
4002
|
+
break
|
|
4003
|
+
elif nesting == 0 and tj.type in statement_starters and len(channel_tokens) > 1:
|
|
4004
|
+
break
|
|
4005
|
+
channel_tokens.append(tj)
|
|
4006
|
+
j += 1
|
|
4007
|
+
|
|
4008
|
+
# Use the context handler to parse the channel tokens
|
|
4009
|
+
block_info = {'tokens': channel_tokens}
|
|
4010
|
+
stmt = self._parse_channel_statement(block_info, tokens)
|
|
4011
|
+
if stmt:
|
|
4012
|
+
statements.append(stmt)
|
|
4013
|
+
|
|
4014
|
+
i = j
|
|
4015
|
+
continue
|
|
4016
|
+
|
|
3986
4017
|
elif token.type == ATOMIC:
|
|
3987
4018
|
# Parse ATOMIC statement: atomic { statements }
|
|
3988
4019
|
j = i + 1
|
|
@@ -4015,6 +4046,48 @@ class ContextStackParser:
|
|
|
4015
4046
|
continue
|
|
4016
4047
|
|
|
4017
4048
|
elif token.type == ASYNC:
|
|
4049
|
+
# Check if this is "async action" — treat as action with async modifier
|
|
4050
|
+
if i + 1 < len(tokens) and tokens[i + 1].type == ACTION:
|
|
4051
|
+
# Collect entire async action declaration including brace body
|
|
4052
|
+
j = i + 1 # skip to ACTION token
|
|
4053
|
+
stmt_tokens = [tokens[j]] # Start with ACTION token
|
|
4054
|
+
j += 1
|
|
4055
|
+
brace_nest = 0
|
|
4056
|
+
paren_nest = 0
|
|
4057
|
+
while j < len(tokens):
|
|
4058
|
+
tj = tokens[j]
|
|
4059
|
+
stmt_tokens.append(tj)
|
|
4060
|
+
if tj.type == LPAREN:
|
|
4061
|
+
paren_nest += 1
|
|
4062
|
+
elif tj.type == RPAREN:
|
|
4063
|
+
if paren_nest > 0:
|
|
4064
|
+
paren_nest -= 1
|
|
4065
|
+
elif tj.type == LBRACE:
|
|
4066
|
+
brace_nest += 1
|
|
4067
|
+
elif tj.type == RBRACE:
|
|
4068
|
+
brace_nest -= 1
|
|
4069
|
+
if brace_nest == 0:
|
|
4070
|
+
j += 1
|
|
4071
|
+
break
|
|
4072
|
+
j += 1
|
|
4073
|
+
|
|
4074
|
+
# Parse as action statement and set is_async flag
|
|
4075
|
+
block_info = {'tokens': stmt_tokens}
|
|
4076
|
+
stmt = self._parse_action_statement(block_info, tokens)
|
|
4077
|
+
if stmt:
|
|
4078
|
+
stmt.is_async = True
|
|
4079
|
+
try:
|
|
4080
|
+
existing_modifiers = list(getattr(stmt, 'modifiers', []) or [])
|
|
4081
|
+
if 'async' not in existing_modifiers:
|
|
4082
|
+
existing_modifiers.append('async')
|
|
4083
|
+
stmt.modifiers = existing_modifiers
|
|
4084
|
+
except Exception:
|
|
4085
|
+
stmt.modifiers = ['async']
|
|
4086
|
+
statements.append(stmt)
|
|
4087
|
+
|
|
4088
|
+
i = j
|
|
4089
|
+
continue
|
|
4090
|
+
|
|
4018
4091
|
# Parse ASYNC expression: async <expression>
|
|
4019
4092
|
j = i + 1
|
|
4020
4093
|
|
|
@@ -6157,6 +6230,22 @@ class ContextStackParser:
|
|
|
6157
6230
|
# If this is "async function", let _parse_function_statement_context handle it
|
|
6158
6231
|
if len(tokens) > 1 and tokens[1].type == FUNCTION:
|
|
6159
6232
|
return self._parse_function_statement_context(block_info, all_tokens)
|
|
6233
|
+
|
|
6234
|
+
# If this is "async action", let _parse_action_statement handle it with async flag
|
|
6235
|
+
if len(tokens) > 1 and tokens[1].type == ACTION:
|
|
6236
|
+
action_tokens = tokens[1:] # Strip ASYNC
|
|
6237
|
+
action_block = {'tokens': action_tokens}
|
|
6238
|
+
stmt = self._parse_action_statement(action_block, all_tokens)
|
|
6239
|
+
if stmt:
|
|
6240
|
+
stmt.is_async = True
|
|
6241
|
+
try:
|
|
6242
|
+
existing_modifiers = list(getattr(stmt, 'modifiers', []) or [])
|
|
6243
|
+
if 'async' not in existing_modifiers:
|
|
6244
|
+
existing_modifiers.append('async')
|
|
6245
|
+
stmt.modifiers = existing_modifiers
|
|
6246
|
+
except Exception:
|
|
6247
|
+
stmt.modifiers = ['async']
|
|
6248
|
+
return stmt
|
|
6160
6249
|
|
|
6161
6250
|
# The tokens are [ASYNC, ...expression tokens...]
|
|
6162
6251
|
# We can just call parse_async_expression from the main parser!
|