@shd101wyy/yo 0.1.21 → 0.1.22
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/.github/skills/yo-core-patterns/core-patterns-cheatsheet.md +2 -0
- package/.github/skills/yo-syntax/syntax-cheatsheet.md +2 -1
- package/out/cjs/index.cjs +552 -548
- package/out/cjs/yo-cli.cjs +671 -667
- package/out/cjs/yo-lsp.cjs +558 -554
- package/out/esm/index.mjs +485 -481
- package/out/types/src/codegen/exprs/drop-dup.d.ts +1 -0
- package/out/types/src/evaluator/trait-checking.d.ts +11 -1
- package/out/types/src/evaluator/values/impl.d.ts +1 -0
- package/out/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/scripts/install.ps1 +143 -0
- package/scripts/install.sh +139 -0
- package/std/cli/arg_parser.yo +148 -24
- package/std/collections/array_list.yo +77 -0
- package/std/collections/hash_map.yo +87 -0
- package/std/collections/hash_set.yo +44 -0
- package/std/collections/ordered_map.yo +244 -0
- package/std/{process.yo → env/index.yo} +38 -89
- package/std/fs/temp.yo +2 -1
- package/std/os/env.yo +2 -1
- package/std/prelude.yo +404 -3
- package/std/process/command.yo +303 -0
- package/std/process/index.yo +45 -0
- package/std/string/index.yo +3 -1
- package/std/string/string.yo +245 -1
- package/std/string/string_builder.yo +138 -0
package/package.json
CHANGED
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
# Yo language installer for Windows (PowerShell).
|
|
2
|
+
#
|
|
3
|
+
# Installs Yo into $env:LOCALAPPDATA\Yo and links the `yo` command into
|
|
4
|
+
# $env:LOCALAPPDATA\Yo\bin (added to user PATH).
|
|
5
|
+
#
|
|
6
|
+
# Usage:
|
|
7
|
+
# irm https://raw.githubusercontent.com/shd101wyy/yo/main/scripts/install.ps1 | iex
|
|
8
|
+
# # or, after cloning:
|
|
9
|
+
# .\scripts\install.ps1
|
|
10
|
+
#
|
|
11
|
+
# Environment variables / parameters:
|
|
12
|
+
# -InstallDir <path> Target install dir. Default: $env:LOCALAPPDATA\Yo
|
|
13
|
+
# -BinDir <path> Wrapper bin dir. Default: $InstallDir\bin
|
|
14
|
+
# -Repo <url> Git repo URL. Default: https://github.com/shd101wyy/yo.git
|
|
15
|
+
# -Ref <ref> Git ref (branch/tag). Default: main
|
|
16
|
+
# -Force Overwrite existing install dir.
|
|
17
|
+
|
|
18
|
+
[CmdletBinding()]
|
|
19
|
+
param(
|
|
20
|
+
[string]$InstallDir = (Join-Path $env:LOCALAPPDATA 'Yo'),
|
|
21
|
+
[string]$BinDir,
|
|
22
|
+
[string]$Repo = 'https://github.com/shd101wyy/yo.git',
|
|
23
|
+
[string]$Ref = 'main',
|
|
24
|
+
[switch]$Force
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
$ErrorActionPreference = 'Stop'
|
|
28
|
+
|
|
29
|
+
if (-not $BinDir) { $BinDir = Join-Path $InstallDir 'bin' }
|
|
30
|
+
|
|
31
|
+
function Info($msg) { Write-Host ">> $msg" -ForegroundColor Cyan }
|
|
32
|
+
function Warn($msg) { Write-Host "!! $msg" -ForegroundColor Yellow }
|
|
33
|
+
function Fail($msg) { Write-Host "XX $msg" -ForegroundColor Red; exit 1 }
|
|
34
|
+
|
|
35
|
+
Info "Installing Yo on Windows"
|
|
36
|
+
Info "Install dir: $InstallDir"
|
|
37
|
+
Info "Bin dir: $BinDir"
|
|
38
|
+
|
|
39
|
+
# ---------------------------------------------------------------------------
|
|
40
|
+
# Required tools
|
|
41
|
+
# ---------------------------------------------------------------------------
|
|
42
|
+
|
|
43
|
+
function Test-Cmd($name) {
|
|
44
|
+
return [bool](Get-Command $name -ErrorAction SilentlyContinue)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (-not (Test-Cmd 'git')) { Fail "Missing required command: git. Install from https://git-scm.com/" }
|
|
48
|
+
|
|
49
|
+
# C compiler (zig recommended on Windows; clang/gcc also work)
|
|
50
|
+
if (-not (Test-Cmd 'zig') -and -not (Test-Cmd 'clang') -and -not (Test-Cmd 'gcc')) {
|
|
51
|
+
Warn "No C compiler found (zig, clang, or gcc). 'yo compile' will not work."
|
|
52
|
+
Warn " Recommended on Windows: install zig via 'winget install zig.zig'"
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
# bun runtime — required to run the current TypeScript-based compiler.
|
|
56
|
+
if (-not (Test-Cmd 'bun')) {
|
|
57
|
+
Info "bun not found - installing via official PowerShell installer..."
|
|
58
|
+
Invoke-RestMethod -Uri 'https://bun.sh/install.ps1' -UseBasicParsing | Invoke-Expression
|
|
59
|
+
$bunBin = Join-Path $env:USERPROFILE '.bun\bin'
|
|
60
|
+
if (Test-Path (Join-Path $bunBin 'bun.exe')) {
|
|
61
|
+
$env:PATH = "$bunBin;$env:PATH"
|
|
62
|
+
}
|
|
63
|
+
if (-not (Test-Cmd 'bun')) {
|
|
64
|
+
Fail "bun installation failed - install manually from https://bun.sh"
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
Info "Using bun: $((Get-Command bun).Source) ($(bun --version))"
|
|
68
|
+
|
|
69
|
+
# ---------------------------------------------------------------------------
|
|
70
|
+
# Download Yo
|
|
71
|
+
# ---------------------------------------------------------------------------
|
|
72
|
+
|
|
73
|
+
if (Test-Path $InstallDir) {
|
|
74
|
+
if ($Force) {
|
|
75
|
+
Info "Removing existing install dir (-Force set)"
|
|
76
|
+
Remove-Item -Recurse -Force $InstallDir
|
|
77
|
+
} else {
|
|
78
|
+
Fail "Install dir already exists: $InstallDir. Use -Force to overwrite."
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
Info "Cloning $Repo ($Ref) into $InstallDir"
|
|
83
|
+
$parent = Split-Path -Parent $InstallDir
|
|
84
|
+
if (-not (Test-Path $parent)) { New-Item -ItemType Directory -Path $parent | Out-Null }
|
|
85
|
+
git clone --depth 1 --branch $Ref $Repo $InstallDir
|
|
86
|
+
if ($LASTEXITCODE -ne 0) { Fail "git clone failed" }
|
|
87
|
+
|
|
88
|
+
# ---------------------------------------------------------------------------
|
|
89
|
+
# Build
|
|
90
|
+
# ---------------------------------------------------------------------------
|
|
91
|
+
|
|
92
|
+
Push-Location $InstallDir
|
|
93
|
+
try {
|
|
94
|
+
Info "Installing dependencies (bun install)"
|
|
95
|
+
bun install --silent
|
|
96
|
+
if ($LASTEXITCODE -ne 0) { Fail "bun install failed" }
|
|
97
|
+
|
|
98
|
+
Info "Building Yo (bun run build)"
|
|
99
|
+
bun run build
|
|
100
|
+
if ($LASTEXITCODE -ne 0) { Fail "bun run build failed" }
|
|
101
|
+
} finally {
|
|
102
|
+
Pop-Location
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
# ---------------------------------------------------------------------------
|
|
106
|
+
# Wrapper script
|
|
107
|
+
# ---------------------------------------------------------------------------
|
|
108
|
+
|
|
109
|
+
if (-not (Test-Path $BinDir)) { New-Item -ItemType Directory -Path $BinDir | Out-Null }
|
|
110
|
+
|
|
111
|
+
# yo.cmd — calls the existing yo-cli.ps1
|
|
112
|
+
$wrapperCmd = Join-Path $BinDir 'yo.cmd'
|
|
113
|
+
$yoCli = Join-Path $InstallDir 'yo-cli.ps1'
|
|
114
|
+
@"
|
|
115
|
+
@echo off
|
|
116
|
+
powershell -NoProfile -ExecutionPolicy Bypass -File `"$yoCli`" %*
|
|
117
|
+
"@ | Set-Content -Path $wrapperCmd -Encoding ASCII
|
|
118
|
+
|
|
119
|
+
# yo.ps1 — for direct PowerShell invocation
|
|
120
|
+
$wrapperPs1 = Join-Path $BinDir 'yo.ps1'
|
|
121
|
+
@"
|
|
122
|
+
#!/usr/bin/env pwsh
|
|
123
|
+
& `"$yoCli`" @args
|
|
124
|
+
"@ | Set-Content -Path $wrapperPs1 -Encoding UTF8
|
|
125
|
+
|
|
126
|
+
Info "Installed wrappers: $wrapperCmd, $wrapperPs1"
|
|
127
|
+
|
|
128
|
+
# ---------------------------------------------------------------------------
|
|
129
|
+
# Add bin to user PATH (persistent)
|
|
130
|
+
# ---------------------------------------------------------------------------
|
|
131
|
+
|
|
132
|
+
$userPath = [Environment]::GetEnvironmentVariable('Path', 'User')
|
|
133
|
+
if ($null -eq $userPath) { $userPath = '' }
|
|
134
|
+
$pathParts = $userPath -split ';' | Where-Object { $_ -ne '' }
|
|
135
|
+
if ($pathParts -notcontains $BinDir) {
|
|
136
|
+
$newUserPath = if ($userPath -eq '') { $BinDir } else { "$userPath;$BinDir" }
|
|
137
|
+
[Environment]::SetEnvironmentVariable('Path', $newUserPath, 'User')
|
|
138
|
+
Info "Added $BinDir to user PATH (restart your shell to pick up the change)"
|
|
139
|
+
} else {
|
|
140
|
+
Info "$BinDir already on user PATH"
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
Info "Done. Open a new shell and try: yo --help"
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Yo language installer for Linux and macOS.
|
|
3
|
+
#
|
|
4
|
+
# Installs Yo into $HOME/.local/Yo and links the `yo` command into
|
|
5
|
+
# $HOME/.local/bin (or a directory of your choice via $YO_BIN_DIR).
|
|
6
|
+
#
|
|
7
|
+
# Usage:
|
|
8
|
+
# curl -fsSL https://raw.githubusercontent.com/shd101wyy/yo/main/scripts/install.sh | bash
|
|
9
|
+
# # or, after cloning:
|
|
10
|
+
# ./scripts/install.sh
|
|
11
|
+
#
|
|
12
|
+
# Environment variables:
|
|
13
|
+
# YO_INSTALL_DIR Target install dir. Default: $HOME/.local/Yo
|
|
14
|
+
# YO_BIN_DIR Wrapper bin dir. Default: $HOME/.local/bin
|
|
15
|
+
# YO_REPO Git repo URL. Default: https://github.com/shd101wyy/yo.git
|
|
16
|
+
# YO_REF Git ref (branch/tag). Default: main
|
|
17
|
+
# YO_FORCE If set, overwrite existing install dir.
|
|
18
|
+
|
|
19
|
+
set -euo pipefail
|
|
20
|
+
|
|
21
|
+
YO_INSTALL_DIR="${YO_INSTALL_DIR:-$HOME/.local/Yo}"
|
|
22
|
+
YO_BIN_DIR="${YO_BIN_DIR:-$HOME/.local/bin}"
|
|
23
|
+
YO_REPO="${YO_REPO:-https://github.com/shd101wyy/yo.git}"
|
|
24
|
+
YO_REF="${YO_REF:-main}"
|
|
25
|
+
|
|
26
|
+
color() { printf '\033[%sm%s\033[0m' "$1" "$2"; }
|
|
27
|
+
info() { printf '%s %s\n' "$(color '1;34' '>>')" "$1"; }
|
|
28
|
+
warn() { printf '%s %s\n' "$(color '1;33' '!!')" "$1" >&2; }
|
|
29
|
+
err() { printf '%s %s\n' "$(color '1;31' 'XX')" "$1" >&2; exit 1; }
|
|
30
|
+
|
|
31
|
+
# ---------------------------------------------------------------------------
|
|
32
|
+
# Detect platform
|
|
33
|
+
# ---------------------------------------------------------------------------
|
|
34
|
+
|
|
35
|
+
OS="$(uname -s)"
|
|
36
|
+
case "$OS" in
|
|
37
|
+
Linux) PLATFORM="linux" ;;
|
|
38
|
+
Darwin) PLATFORM="macos" ;;
|
|
39
|
+
*) err "Unsupported OS: $OS. This installer supports Linux and macOS only." ;;
|
|
40
|
+
esac
|
|
41
|
+
|
|
42
|
+
info "Installing Yo on $PLATFORM"
|
|
43
|
+
info "Install dir: $YO_INSTALL_DIR"
|
|
44
|
+
info "Bin dir: $YO_BIN_DIR"
|
|
45
|
+
|
|
46
|
+
# ---------------------------------------------------------------------------
|
|
47
|
+
# Required tools
|
|
48
|
+
# ---------------------------------------------------------------------------
|
|
49
|
+
|
|
50
|
+
need() {
|
|
51
|
+
command -v "$1" >/dev/null 2>&1 || err "Missing required command: $1"
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
need git
|
|
55
|
+
need curl
|
|
56
|
+
|
|
57
|
+
# C compiler (clang or gcc) — needed for `yo compile`.
|
|
58
|
+
if ! command -v clang >/dev/null 2>&1 && ! command -v gcc >/dev/null 2>&1; then
|
|
59
|
+
warn "No C compiler found (clang or gcc). 'yo compile' will not work until you install one."
|
|
60
|
+
if [ "$PLATFORM" = "macos" ]; then
|
|
61
|
+
warn " Install with: xcode-select --install"
|
|
62
|
+
else
|
|
63
|
+
warn " Install with your distro's package manager (e.g. apt install clang)"
|
|
64
|
+
fi
|
|
65
|
+
fi
|
|
66
|
+
|
|
67
|
+
# bun runtime — required to run the current TypeScript-based compiler.
|
|
68
|
+
if ! command -v bun >/dev/null 2>&1; then
|
|
69
|
+
info "bun not found — installing via official installer..."
|
|
70
|
+
curl -fsSL https://bun.sh/install | bash
|
|
71
|
+
# Try common bun install locations
|
|
72
|
+
for candidate in "$HOME/.bun/bin" "$HOME/.local/share/bun/bin"; do
|
|
73
|
+
if [ -x "$candidate/bun" ]; then
|
|
74
|
+
export PATH="$candidate:$PATH"
|
|
75
|
+
break
|
|
76
|
+
fi
|
|
77
|
+
done
|
|
78
|
+
command -v bun >/dev/null 2>&1 || err "bun installation failed — install manually from https://bun.sh"
|
|
79
|
+
fi
|
|
80
|
+
info "Using bun: $(command -v bun) ($(bun --version))"
|
|
81
|
+
|
|
82
|
+
# ---------------------------------------------------------------------------
|
|
83
|
+
# Download Yo
|
|
84
|
+
# ---------------------------------------------------------------------------
|
|
85
|
+
|
|
86
|
+
if [ -e "$YO_INSTALL_DIR" ]; then
|
|
87
|
+
if [ -n "${YO_FORCE:-}" ]; then
|
|
88
|
+
info "Removing existing install dir (YO_FORCE set)"
|
|
89
|
+
rm -rf "$YO_INSTALL_DIR"
|
|
90
|
+
else
|
|
91
|
+
err "Install dir already exists: $YO_INSTALL_DIR. Set YO_FORCE=1 to overwrite."
|
|
92
|
+
fi
|
|
93
|
+
fi
|
|
94
|
+
|
|
95
|
+
info "Cloning $YO_REPO ($YO_REF) into $YO_INSTALL_DIR"
|
|
96
|
+
mkdir -p "$(dirname "$YO_INSTALL_DIR")"
|
|
97
|
+
git clone --depth 1 --branch "$YO_REF" "$YO_REPO" "$YO_INSTALL_DIR"
|
|
98
|
+
|
|
99
|
+
# ---------------------------------------------------------------------------
|
|
100
|
+
# Build
|
|
101
|
+
# ---------------------------------------------------------------------------
|
|
102
|
+
|
|
103
|
+
info "Installing dependencies (bun install)"
|
|
104
|
+
( cd "$YO_INSTALL_DIR" && bun install --silent )
|
|
105
|
+
|
|
106
|
+
info "Building Yo (bun run build)"
|
|
107
|
+
( cd "$YO_INSTALL_DIR" && bun run build )
|
|
108
|
+
|
|
109
|
+
# ---------------------------------------------------------------------------
|
|
110
|
+
# Wrapper script
|
|
111
|
+
# ---------------------------------------------------------------------------
|
|
112
|
+
|
|
113
|
+
mkdir -p "$YO_BIN_DIR"
|
|
114
|
+
WRAPPER="$YO_BIN_DIR/yo"
|
|
115
|
+
|
|
116
|
+
cat > "$WRAPPER" <<EOF
|
|
117
|
+
#!/usr/bin/env bash
|
|
118
|
+
# Yo language CLI — auto-generated by install.sh
|
|
119
|
+
set -euo pipefail
|
|
120
|
+
exec "$YO_INSTALL_DIR/yo-cli" "\$@"
|
|
121
|
+
EOF
|
|
122
|
+
chmod +x "$WRAPPER"
|
|
123
|
+
|
|
124
|
+
info "Installed wrapper: $WRAPPER"
|
|
125
|
+
|
|
126
|
+
# ---------------------------------------------------------------------------
|
|
127
|
+
# PATH check
|
|
128
|
+
# ---------------------------------------------------------------------------
|
|
129
|
+
|
|
130
|
+
case ":$PATH:" in
|
|
131
|
+
*":$YO_BIN_DIR:"*) ;;
|
|
132
|
+
*)
|
|
133
|
+
warn "$YO_BIN_DIR is not on your PATH."
|
|
134
|
+
warn "Add this to your shell rc file (.bashrc / .zshrc / etc):"
|
|
135
|
+
warn " export PATH=\"\$HOME/.local/bin:\$PATH\""
|
|
136
|
+
;;
|
|
137
|
+
esac
|
|
138
|
+
|
|
139
|
+
info "Done. Try: yo --help"
|
package/std/cli/arg_parser.yo
CHANGED
|
@@ -49,7 +49,11 @@ ParsedArgs :: object(
|
|
|
49
49
|
_option_names : ArrayList(String),
|
|
50
50
|
_option_values : ArrayList(String),
|
|
51
51
|
_positionals : ArrayList(String),
|
|
52
|
-
_positional_names : ArrayList(String)
|
|
52
|
+
_positional_names : ArrayList(String),
|
|
53
|
+
/// Name of the chosen subcommand (`.None` if none was used).
|
|
54
|
+
_subcommand : Option(String),
|
|
55
|
+
/// Parsed arguments for the chosen subcommand (`.None` if no subcommand).
|
|
56
|
+
_subcommand_args : Option(Self)
|
|
53
57
|
);
|
|
54
58
|
|
|
55
59
|
/// Command-line argument parser.
|
|
@@ -59,7 +63,13 @@ ArgParser :: object(
|
|
|
59
63
|
/// Application description shown in help text.
|
|
60
64
|
_description : String,
|
|
61
65
|
/// Registered argument definitions.
|
|
62
|
-
_args : ArrayList(ArgDef)
|
|
66
|
+
_args : ArrayList(ArgDef),
|
|
67
|
+
/// Subcommand names (parallel to `_subcommand_parsers`).
|
|
68
|
+
_subcommand_names : ArrayList(String),
|
|
69
|
+
/// Subcommand descriptions (parallel to `_subcommand_parsers`).
|
|
70
|
+
_subcommand_descriptions : ArrayList(String),
|
|
71
|
+
/// Subcommand parsers (parallel to `_subcommand_names`).
|
|
72
|
+
_subcommand_parsers : ArrayList(Self)
|
|
63
73
|
);
|
|
64
74
|
|
|
65
75
|
impl(ParsedArgs,
|
|
@@ -69,10 +79,18 @@ impl(ParsedArgs,
|
|
|
69
79
|
_option_names: ArrayList(String).new(),
|
|
70
80
|
_option_values: ArrayList(String).new(),
|
|
71
81
|
_positionals: ArrayList(String).new(),
|
|
72
|
-
_positional_names: ArrayList(String).new()
|
|
82
|
+
_positional_names: ArrayList(String).new(),
|
|
83
|
+
_subcommand: .None,
|
|
84
|
+
_subcommand_args: .None
|
|
73
85
|
)
|
|
74
86
|
),
|
|
75
87
|
|
|
88
|
+
/// Returns the name of the chosen subcommand, if any.
|
|
89
|
+
get_subcommand : (fn(self: Self) -> Option(String))(self._subcommand),
|
|
90
|
+
|
|
91
|
+
/// Returns the parsed arguments of the chosen subcommand, if any.
|
|
92
|
+
get_subcommand_args : (fn(self: Self) -> Option(Self))(self._subcommand_args),
|
|
93
|
+
|
|
76
94
|
get_flag : (fn(self: Self, name: String) -> bool)(
|
|
77
95
|
self._set_flags.contains(name)
|
|
78
96
|
),
|
|
@@ -116,10 +134,43 @@ impl(ArgParser,
|
|
|
116
134
|
Self(
|
|
117
135
|
_name: name,
|
|
118
136
|
_description: description,
|
|
119
|
-
_args: ArrayList(ArgDef).new()
|
|
137
|
+
_args: ArrayList(ArgDef).new(),
|
|
138
|
+
_subcommand_names: ArrayList(String).new(),
|
|
139
|
+
_subcommand_descriptions: ArrayList(String).new(),
|
|
140
|
+
_subcommand_parsers: ArrayList(Self).new()
|
|
120
141
|
)
|
|
121
142
|
),
|
|
122
143
|
|
|
144
|
+
/// Registers a subcommand with the given name and description.
|
|
145
|
+
/// Returns the new sub-parser, which can be configured with its own
|
|
146
|
+
/// flags, options, positionals, and nested subcommands.
|
|
147
|
+
add_subcommand : (fn(self: Self, name: String, description: String) -> Self)({
|
|
148
|
+
sub := Self.new(name, description);
|
|
149
|
+
self._subcommand_names.push(name);
|
|
150
|
+
self._subcommand_descriptions.push(description);
|
|
151
|
+
self._subcommand_parsers.push(sub);
|
|
152
|
+
return sub;
|
|
153
|
+
}),
|
|
154
|
+
|
|
155
|
+
/// Looks up a subcommand parser by name.
|
|
156
|
+
_find_subcommand : (fn(self: Self, name: String) -> Option(usize))({
|
|
157
|
+
i := usize(0);
|
|
158
|
+
len := self._subcommand_names.len();
|
|
159
|
+
(result : Option(usize)) = .None;
|
|
160
|
+
while ((i < len) && result.is_none()), (i = (i + usize(1))), {
|
|
161
|
+
match(self._subcommand_names.get(i),
|
|
162
|
+
.Some(n) => {
|
|
163
|
+
cond(
|
|
164
|
+
(n == name) => { result = .Some(i); },
|
|
165
|
+
true => ()
|
|
166
|
+
);
|
|
167
|
+
},
|
|
168
|
+
.None => ()
|
|
169
|
+
);
|
|
170
|
+
};
|
|
171
|
+
return result;
|
|
172
|
+
}),
|
|
173
|
+
|
|
123
174
|
add_flag : (fn(self: Self, long_name: String, short_name: String, description: String) -> unit)({
|
|
124
175
|
self._args.push(ArgDef(
|
|
125
176
|
_long_name: long_name,
|
|
@@ -265,6 +316,31 @@ impl(ArgParser,
|
|
|
265
316
|
);
|
|
266
317
|
|
|
267
318
|
text = text.concat(` --help, -h\tShow this help message\n`);
|
|
319
|
+
|
|
320
|
+
sub_count := self._subcommand_names.len();
|
|
321
|
+
cond(
|
|
322
|
+
(sub_count > usize(0)) => {
|
|
323
|
+
text = text.concat(`\nSubcommands:\n`);
|
|
324
|
+
s := usize(0);
|
|
325
|
+
while (s < sub_count), (s = (s + usize(1))), {
|
|
326
|
+
match(self._subcommand_names.get(s),
|
|
327
|
+
.Some(sn) => {
|
|
328
|
+
match(self._subcommand_descriptions.get(s),
|
|
329
|
+
.Some(sd) => {
|
|
330
|
+
text = text.concat(` ${sn}\t${sd}\n`);
|
|
331
|
+
},
|
|
332
|
+
.None => {
|
|
333
|
+
text = text.concat(` ${sn}\n`);
|
|
334
|
+
}
|
|
335
|
+
);
|
|
336
|
+
},
|
|
337
|
+
.None => ()
|
|
338
|
+
);
|
|
339
|
+
};
|
|
340
|
+
},
|
|
341
|
+
true => ()
|
|
342
|
+
);
|
|
343
|
+
|
|
268
344
|
return text;
|
|
269
345
|
})
|
|
270
346
|
);
|
|
@@ -326,30 +402,78 @@ impl(ArgParser,
|
|
|
326
402
|
);
|
|
327
403
|
},
|
|
328
404
|
true => {
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
405
|
+
// Check if this is a subcommand (first non-flag positional only).
|
|
406
|
+
is_subcmd := false;
|
|
407
|
+
cond(
|
|
408
|
+
(self._subcommand_names.len() > usize(0)) => {
|
|
409
|
+
match(self._find_subcommand(current_arg),
|
|
410
|
+
.Some(sub_idx) => {
|
|
411
|
+
match(self._subcommand_parsers.get(sub_idx),
|
|
412
|
+
.Some(sub_parser) => {
|
|
413
|
+
// Build new args list: [program_name, ...remaining args after subcommand name].
|
|
414
|
+
sub_args := ArrayList(String).new();
|
|
415
|
+
match(args.get(usize(0)),
|
|
416
|
+
.Some(prog) => { sub_args.push(prog); },
|
|
417
|
+
.None => { sub_args.push(self._name); }
|
|
418
|
+
);
|
|
419
|
+
j := (idx + usize(1));
|
|
420
|
+
while (j < args_len), (j = (j + usize(1))), {
|
|
421
|
+
match(args.get(j),
|
|
422
|
+
.Some(a) => { sub_args.push(a); },
|
|
423
|
+
.None => ()
|
|
424
|
+
);
|
|
425
|
+
};
|
|
426
|
+
// Recursively parse the subcommand.
|
|
427
|
+
match(recur(sub_parser, sub_args),
|
|
428
|
+
.Ok(sub_parsed) => {
|
|
429
|
+
parsed._subcommand = .Some(current_arg);
|
|
430
|
+
parsed._subcommand_args = .Some(sub_parsed);
|
|
431
|
+
idx = args_len;
|
|
432
|
+
is_subcmd = true;
|
|
433
|
+
},
|
|
434
|
+
.Err(sub_err) => {
|
|
435
|
+
err_msg = .Some(sub_err);
|
|
436
|
+
is_subcmd = true;
|
|
437
|
+
}
|
|
438
|
+
);
|
|
439
|
+
},
|
|
440
|
+
.None => ()
|
|
441
|
+
);
|
|
442
|
+
},
|
|
443
|
+
.None => ()
|
|
444
|
+
);
|
|
445
|
+
},
|
|
446
|
+
true => ()
|
|
447
|
+
);
|
|
448
|
+
cond(
|
|
449
|
+
(!is_subcmd) => {
|
|
450
|
+
parsed._positionals.push(current_arg);
|
|
451
|
+
p_idx := usize(0);
|
|
452
|
+
p_count := usize(0);
|
|
453
|
+
while (p_idx < arg_defs_len), (p_idx = (p_idx + usize(1))), {
|
|
454
|
+
match(self._args.get(p_idx),
|
|
455
|
+
.Some(p_def) => {
|
|
456
|
+
match(p_def._kind,
|
|
457
|
+
.Positional => {
|
|
458
|
+
cond(
|
|
459
|
+
(p_count == positional_idx) => {
|
|
460
|
+
parsed._positional_names.push(p_def._long_name);
|
|
461
|
+
},
|
|
462
|
+
true => ()
|
|
463
|
+
);
|
|
464
|
+
p_count = (p_count + usize(1));
|
|
340
465
|
},
|
|
341
|
-
|
|
466
|
+
.Flag => (),
|
|
467
|
+
.Opt => ()
|
|
342
468
|
);
|
|
343
|
-
p_count = (p_count + usize(1));
|
|
344
469
|
},
|
|
345
|
-
.
|
|
346
|
-
.Opt => ()
|
|
470
|
+
.None => ()
|
|
347
471
|
);
|
|
348
|
-
}
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
472
|
+
};
|
|
473
|
+
positional_idx = (positional_idx + usize(1));
|
|
474
|
+
},
|
|
475
|
+
true => ()
|
|
476
|
+
);
|
|
353
477
|
}
|
|
354
478
|
);
|
|
355
479
|
},
|
|
@@ -696,9 +696,86 @@ impl(forall(T : Type), where(T <: Ord(T)), ArrayList(T),
|
|
|
696
696
|
})
|
|
697
697
|
);
|
|
698
698
|
|
|
699
|
+
/// Clone implementation for ArrayList — deep-clones each element.
|
|
700
|
+
impl(forall(T : Type), where(T <: Clone), ArrayList(T), Clone(
|
|
701
|
+
clone : (fn(self: *(Self)) -> Self)(
|
|
702
|
+
{
|
|
703
|
+
n := self.*.len();
|
|
704
|
+
result := Self.with_capacity(n);
|
|
705
|
+
i := usize(0);
|
|
706
|
+
while (i < n), {
|
|
707
|
+
item_opt := self.*.get(i);
|
|
708
|
+
match(item_opt,
|
|
709
|
+
.Some(item) => { result.push((&item).clone()); },
|
|
710
|
+
.None => ()
|
|
711
|
+
);
|
|
712
|
+
i = (i + usize(1));
|
|
713
|
+
};
|
|
714
|
+
result
|
|
715
|
+
}
|
|
716
|
+
)
|
|
717
|
+
));
|
|
718
|
+
|
|
699
719
|
export
|
|
700
720
|
ArrayList,
|
|
701
721
|
ArrayListError,
|
|
702
722
|
ArrayListIter,
|
|
703
723
|
ArrayListIterPtr
|
|
704
724
|
;
|
|
725
|
+
|
|
726
|
+
// =============================================================================
|
|
727
|
+
// array_list! literal macro
|
|
728
|
+
// =============================================================================
|
|
729
|
+
|
|
730
|
+
/// Internal helper: build a list of `tmp.push(elem)` statements from `elems`.
|
|
731
|
+
__array_list_build_pushes :: (fn(comptime(tmp) : Expr, comptime(elems) : ExprList) -> comptime(ExprList))(
|
|
732
|
+
cond(
|
|
733
|
+
(elems.len() == usize(0)) => ExprList(),
|
|
734
|
+
true => {
|
|
735
|
+
first :: elems.car();
|
|
736
|
+
rest :: elems.cdr();
|
|
737
|
+
stmt :: quote((unquote(tmp).push(unquote(first))));
|
|
738
|
+
stmt.cons(recur(tmp, rest))
|
|
739
|
+
}
|
|
740
|
+
)
|
|
741
|
+
);
|
|
742
|
+
|
|
743
|
+
/// `array_list` macro — construct an `ArrayList(T)` literal.
|
|
744
|
+
///
|
|
745
|
+
/// The element type `T` is inferred from the first element via `typeof`, so
|
|
746
|
+
/// at least one element is required.
|
|
747
|
+
///
|
|
748
|
+
/// # Example
|
|
749
|
+
/// ```rust
|
|
750
|
+
/// xs := array_list(i32(1), i32(2), i32(3));
|
|
751
|
+
/// names := array_list(`alice`, `bob`);
|
|
752
|
+
/// ```
|
|
753
|
+
///
|
|
754
|
+
/// Expands to:
|
|
755
|
+
/// ```rust
|
|
756
|
+
/// {
|
|
757
|
+
/// __first := <first elem>;
|
|
758
|
+
/// __tmp := ArrayList(typeof(__first)).new();
|
|
759
|
+
/// __tmp.push(__first);
|
|
760
|
+
/// __tmp.push(<elem 2>);
|
|
761
|
+
/// ...
|
|
762
|
+
/// __tmp
|
|
763
|
+
/// }
|
|
764
|
+
/// ```
|
|
765
|
+
array_list :: (fn(...(quote(elems))) -> unquote(Expr)) {
|
|
766
|
+
__ :: comptime_assert((elems.len() > usize(0)),
|
|
767
|
+
"array_list requires at least one element to infer the element type");
|
|
768
|
+
first :: elems.car();
|
|
769
|
+
rest :: elems.cdr();
|
|
770
|
+
tmp :: gensym("array_list");
|
|
771
|
+
first_tmp :: gensym("array_list_first");
|
|
772
|
+
rest_pushes :: __array_list_build_pushes(tmp, rest);
|
|
773
|
+
quote {
|
|
774
|
+
unquote(first_tmp) := unquote(first);
|
|
775
|
+
unquote(tmp) := ArrayList(typeof(unquote(first_tmp))).new();
|
|
776
|
+
unquote(tmp).push(unquote(first_tmp));
|
|
777
|
+
unquote_splicing(rest_pushes);
|
|
778
|
+
unquote(tmp)
|
|
779
|
+
}
|
|
780
|
+
};
|
|
781
|
+
export array_list;
|