# 命令行参数

木兰在启动时将扫描提供的命令行参数用于获取各种设置信息和进入不同的模式。

## 命令行调用形式

可以对木兰按以下形式发起命令行调用：

```
木兰 [选项] [-树p码兰调交反执溯版助] [源码文件] [其他参数]

选项：
 --语法树　　　　　 -树　　将木兰源码转换为 Python 语法树
 --木兰变python　　 -p 　　将木兰源码转换为 Python 源码
 --生成字节码　　　 -码　　将木兰源码转换为 donsok 字节码 (实验性)
 --python变木兰　　 -兰　　将 Python 源码转换为木兰源码
 --调试　　　　　　 -调　　使用 Pdb 环境调试代码
 --交互　　　　　　 -交　　以交互式审查脚本
 --反汇编　　　　　 -反　　将生成的 Python 字节码反汇编
 --执行代码=<代码>　-执　　执行来自命令行参数的代码
 --显示回溯　　　　 -溯　　显示异常的栈回溯信息
 --版本　　　　　　 -版　　显示版本
 --帮助　　　　　　 -助　　显示帮助信息
```

下面是原始木兰的命令行帮助文本：

```
usage: ulang-0.2.2.exe [-apbcidsDth] input_file
Options and arguments:
 --dump-ast,        -a   dump ast info
 --dump-python,     -p   dump python source code
 --dump-blockly,    -b   dump blockly xml (experimental)
 --dump-bytecode,   -c   dump donsok bytecode (experimental)
 --python-to-ulang, -s   convert python to ulang
 --debug,           -D   debug with Pdb (experimental)
 --interact,        -i   inspect interactively after running script
 --disassemble,     -d   disassemble the python bytecode
 --exec-code=<code> -e   run code from cli argument
 --show-backtrace,  -t   show backtrace for errors
 --version,         -v   show the version
 --help,            -h   show this message
```

下面是重现项目与原始木兰的命令行选项的对照表：

| 重现项目         | 原始木兰             |
|--------------|------------------|
| 语法树          | dump-ast         |
| 木兰变python    | dump-python      |
| 生成字节码        | dump-bytecode    |
| python变木兰    | python-to-ulang  |
| 调试           | debug            |
| 交互           | interact         |
| 反汇编          | disassemble      |
| 执行代码=<代码>    | exec-code=<code> |
| 显示回溯         | show-backtrace   |
| 版本           | version          |
| 帮助           | help             |
| 【待调研】        | dump-blockly     |

### 源码文件

提供给木兰运行的代码文件。

当传递给此选项的值为 `-` 时，将从标准输入流读取文件，直到 EOF：

例如有如下代码 `你好世界.ul`：
```
println('Hello world')
```

```shell
$ cat 你好世界.ul | 木兰 - 
Hello world
```

### 其他参数

其他参数是用户传递给脚本文件的参数。开发者可以通过导入 `sys` 模块的 `argv` 成员访问到其他参数。

例如下面的代码文件`打印命令行参数.ul`：
```
using argv in sys

println(argv)
```

```
$ 木兰 打印命令行参数.ul a b c 1 2 3
[木兰, 打印命令行参数.ul, a, b, c, 1, 2, 3]
```

`sys.argv` 获取到的结果是一个**全部由字符串构成**的列表： [`木兰执行文件名`, `脚本名`, `参数1`, `参数2`, ..., `参数n`]。需要注意的是，**用户提供的命令行参数是从第三个元素开始的**。

其中脚本名以后的部分，就是提供给脚本的参数，脚本程序可以根据这些参数，执行不同的功能。

#### 遍历参数列表

通过遍历 `sys.argv` 成员，可以遍历所给的命令行参数列表。如下面这段代码 `命令行参数相加.ul`，它将所给的命令行参数的数字相加：

```
using argv in sys

结果 = 0
所给参数 = argv[2:]  // 命令行参数从下标 2 开始

for 参数 in 所给参数 {
	结果 += int(参数)
}

println(结果)
```

```
$ 木兰 命令行参数相加.ul 1 3 5
9
$ 木兰 命令行参数相加.ul 9
9
$ 木兰 命令行参数相加.ul
0
```

#### 处理单参数程序的实例

下面的代码文件`十进制转换.ul`是一个处理单个命令行参数的例子，这个程序将不断接受用户输入，然后根据提供的命令行参数输出不同进制的数字：

```
using argv, exit in sys
using bin, hex, oct in builtins

帮助 = '十进制转换.ul [二进制 | 八进制 | 十六进制]'

if len(argv) < 3 {
    println(帮助)
    exit(1)
}

所给参数 = argv[2]

模式函数 = {
    '二进制': bin,
    '八进制': oct,
    '十六进制': hex
}.get(所给参数, 0)

if 模式函数 == 0 {
    println(帮助)
    exit(1)
}

while true {
    数字 = input('输入数字(输入 exit 退出): ')
    if 数字 == 'exit' {
        break
    }

    if !数字.isnumeric() {
        continue
    }

    println(模式函数(int(数字)))
}
```

```
$ 木兰 十进制转换.ul 十六进制
输入数字(输入 exit 退出): 2021727
0x1ed95f
输入数字(输入 exit 退出): 20200928
0x1343de0
输入数字(输入 exit 退出): exit
$ 木兰 十进制转换.ul 二进制
输入数字(输入 exit 退出): 20220926
0b1001101001000101111111110
输入数字(输入 exit 退出): 2047
0b11111111111
输入数字(输入 exit 退出): exit
```

#### 处理多参数程序实例

下面的代码文件`纯数字参数.ul`是一个处理多个命令行参数的例子。此程序将扫描提供的命令行参数，计算由纯数字构成的参数的数量。

```
using argv in sys

数量 = 0

for 参数 in argv[2:] {
    if 参数.isnumeric() {
        数量 += 1
    }
}

println('命令行参数中由数字组成的有 %s 个' % 数量)
```

```
$ 木兰 纯数字参数.ul 1 a 2 b c d 3 e
命令行参数中由数字组成的有 3 个
$ 木兰 纯数字参数.ul mx ma mc md 
命令行参数中由数字组成的有 0 个
```



## 命令行选项介绍

### --语法树 (-树)

打印给定木兰源码文件的语法树。

例如有如下代码文件`code1.ul`：

```
x = 1
```

```
$ 木兰 --语法树 code1.ul
Module(body=[Assign(targets=[Name(id='x', ctx=Store(), lineno=1, col_offset=1)], value=Num(n=1, lineno=1, col_offset=5), lineno=1, col_offset=1)])
```

*注：此输出是在 Python3.7 版本下运行的结果，在 Python3.8 下的运行结果见* [Py3.8支持文档](/文档/功能/py3.8支持.md#语法树节点差异)

*更多测试用例请见:*  `测试/unittest/语法树.py`

### --木兰变python (-p)

将用户提供的 **木兰代码文件** 转换成等效的 Python 代码并输出。

```
$ 木兰 --木兰变python 纯数字参数.ul
import sys
from math import *
ARGV = sys.argv[1:]
from sys import argv
数量 = 0
for 参数 in argv[2:]:
  if 参数.isnumeric():
    数量 += 1
print(__rem__('命令行参数中由数字组成的有 %s 个', 数量))
```

### --生成字节码 (-码)

将木兰源码转换为 *donsok* 字节码 (实验性功能)，**由于 pygen未实现亦未找到第三方库，该选项暂时不可用**

### --python变木兰 (-兰)

将用户提供的 **Python代码文件** 转换成等效的木兰代码并输出。

例如有如下的代码文件`遍历列表.py`：
```python
for i in range(10):
	print(i * 2)
```

```
$ 木兰 --python变木兰 遍历列表.py
/* 本文件由命令 `木兰 -兰 ` 自动生成. */

for i in range(10) {
  println(i * 2)
}
```

### --调试 (-调)

使用 pdb 调试指定的文件。
例如有如下文件 `调试测试.ul`：
```
x = 1
println('x = %s' % x)
x = 2
println('x = %s' % x)
```

```commandline
$ 木兰 --调试 调试测试.ul
> 调试测试.ul(1)<module>()
-> x = 1
(Pdb) l
  1  -> x = 1
  2     println('x = %s' % x)
  3     x = 2
  4     println('x = %s' % x)
[EOF]
(Pdb) step
> 调试测试.ul(2)<module>()
-> println('x = %s' % x)
(Pdb) step
--Call--
> D:\木兰\环境.py(117)__内置_求余()
-> def __内置_求余(a, b):
(Pdb) next
> D:\木兰\环境.py(119)__内置_求余()
-> return a % b
(Pdb) next
--Return--
> D:\木兰\环境.py(119)__内置_求余()->'x = 1'
-> return a % b
(Pdb) next
--Call--
> D:\木兰\环境.py(211)<lambda>()
-> 'println': lambda *各物件: 自定义输出(*各物件, **{'终止符': '\n'}),
(Pdb) continue
x = 1
x = 2
```

### --交互 (-交)

指定了该选项后，木兰在执行完给定的源码文件时，将携带执行完毕后的**全局名称空间**进入到交互环境。例如有如下代码文件`预置.ul`：

```
println('定义 a = 1')
a = 1
println('设置完毕，程序结束。')
```

```
$ 木兰 --交互 预置.ul
定义 a = 1
设置完毕，程序结束。
木兰向您问好
更多信息请说'你好'
> a
1
> a + 2
3
```

若正常进入交互环境，则不会有 `a` 变量。

```
$ 木兰
木兰向您问好
更多信息请说'你好'
> a
（>﹏<）请先定义‘a’再使用，见第1行
> a + 2
（>﹏<）请先定义‘a’再使用，见第1行
```

### --反汇编 (-反)

通过 `dis` 模块反汇编由木兰源码编译生成的 Python 字节码。

例如，有如下的代码文件`代码片段1.ul`：

```
a = 1
b = 2
print(a + b)
```

```
$ 木兰 --反汇编 代码片段1.ul
  1           0 LOAD_CONST               0 (1)
              2 STORE_NAME               0 (a)

  2           4 LOAD_CONST               1 (2)
              6 STORE_NAME               1 (b)

  3           8 LOAD_NAME                2 (print)
             10 LOAD_NAME                0 (a)
             12 LOAD_NAME                1 (b)
             14 BINARY_ADD
             16 CALL_FUNCTION            1
             18 POP_TOP
             20 LOAD_CONST               2 (None)
             22 RETURN_VALUE
```

### --执行代码 (-执)

执行来自命令行参数的代码。如：

```
$ 木兰 --执行代码="println('Hello world!')"
Hello world!
```

### --显示回溯 (-溯)

启用此选项后，当执行的脚本发生异常时，将使用 Python 原生的异常处理器处理。例如在执行下面这段代码时，由于没有定义 `foo` 名称，此段代码将会发生错误。

假若有如下代码文件`错误测试.ul`：

```
foo()
```

下面是未指定 `--显示回溯` 选项时的运行结果：

```
$ 木兰 错误测试.ul
（>﹏<）请先定义‘foo’再使用
见第1行：foo()
```

下面是指定了 `--显示回溯` 选项的运行结果：

```
$ 木兰 --显示回溯 错误测试.ul
Traceback (most recent call last):
  File "C:\Python37\lib\runpy.py", line 193, in _run_module_as_main
    "__main__", mod_spec)
  File "C:\Python37\lib\runpy.py", line 85, in _run_code
    exec(code, run_globals)
  File "C:\木兰\__main__.py", line 4, in <module>
    中(sys.argv)
  File "C:\木兰\中.py", line 154, in 中
    exec(可执行码, 环境变量)
  File "错误测试.ul", line 1, in <module>
    foo()
NameError: name 'foo' is not defined
```

### --版本 (-版)

显示当前木兰的版本信息

### --帮助 (-助)

显示帮助信息
