/zScript

script interpreter for zScript

Primary LanguageC++GNU General Public License v3.0GPL-3.0

编译&运行

Linux(Debian)

先安装编译依赖:

sudo apt install qt5-default flex bison g++ make

克隆代码:

git clone https://github.com/zccrs/zScript

编译:

cd zScript
mkdir build && cd build
qmake ..
make

运行:

./zScript ../demo/snake.zs

Windows

先安装编译依赖:

到 http://download.qt.io/official_releases/qt/ 下载并安装最新版的Qt

克隆代码:

git clone https://github.com/zccrs/zScript

打开项目并编译,可在“项目” --> “Build&Run” --> “Run” --> “Command line arguments:” 中添加命令行参数来执行要执行的代码文件。

zScript

变量的定义

变量使用关键字“var”进行定义。 zScript提供了两种形式的变量定义语句。

1.直接定义的形式,该语句的一般形式为:
var 标识符;

该语句的含义是:定义变量名为“标识符”的变量。例如:

var a;

该语句的作用是:定义一个名为“a”的变量。

2.定义同时给变量初始化赋值,该语句的一般形式为:
var 标识符 = 表达式;

该语句的含义是:定义变量名为“标识符”的变量,且给变量赋值为表达式的结果。例如:

var a = 1 + 2;

该语句的作用是:定义一个名为“a”的变量,且赋值为3。 “var”关键字可同时定义多个变量,每个变量之间使用符号“,”分割。

数组的定义

数组定义的一般形式为:

var a = [表达式1, 表达式2, ...];

该语句的含义是:生成一个数组,数组元素为中括号中表达式的结果。例如:

var a = [1, 2, 3];

该语句的作用是:定义一个包含三个元素的数组,元素的值分别为1、2、3,然后赋值给变量a; 数组使用“[”“]”定义,中间内容可以为空、一个表达式、多个表达式,多个表达式之间使用“,”隔开。

a = a[0];

取数组元素语法和C语言一样,都是中括号中写入数组下标。

选择控制语句

一个选择结构,包括一组或若干组操作,每组操作称为一个分支。通过选择控制语句可以实现选择结构。选择控制语句包括if语句、switch语句及起辅助控制作用的break语句。 If语句用于计算给定的表达式,根据表达式的值是否为假,决定是否执行某一组操作。 Switch语句首先求解一个表达式,然后根据计算结果的值,从哈希表中查询该从哪一组操作开始执行。 Break语句用于switch结构中,用于终止当前switch结构的执行。

if语句

zScript提供了两种形式的if语句。

1.单if子句的if语句。该if语句的一般形式为:
if (表达式) {
	语句组
}

该语句的含义是:只有表达式的值为非零值时,才执行其内部的语句组。例如:

if ( a > b ) {
	console.log(“hello”);
}

该语句的作用是:当a的值大于b的值时(此时,“a>b”的值为真,为非假值),在屏幕上显示“hello”;否则,不显示“hello”。

2.带else子句的if语句。该if语句的一般形式为:
if ( 表达式 ) {
	语句组1
} else {
	语句组2
}

该语句的含义是:当表达式的值为非假时,执行语句组1,而不执行语句组2;否则,即表达式的值为假时,执行语句组2,而不执行语句组1。例如:

if ( a > b ) {
	console.log(“hello1”);
} else {
	console.log(“hello2”);
}

该语句的作用是:若a的值大于b的值(此时“a>b”为真,为非假值),则在屏幕上显示“hello1”,而不显示“hello2”;否则,即表达式的值为假时,显示“hello2”,而不显示“hello1”。

switch语句

Switch语句与if语句一样,也可以实现分支选择。但if语句是判断一个表达式的值是否为假,决定是否执行某个分支;而switch语句是计算一个表达式的值,根据计算结果,从哈希表查询从哪个分支开始执行代码。Switch语句的一般形式为:

switch( 表达式 ) {
	case 常量1:{
		语句组1
	}
	case 常量2:{
		语句组2
	}
	...
	case 常量n:{
		语句组n
	}
	default: {
		语句组 n + 1
	}
}

switch语句的执行过程:

  • 1.求解“表达式”的值;
  • 2.如果“表达式”的值与某个case后面的“常量”的值相同,则从这里开始顺序执行语句。结果switch执行有两种形式:一是遇到break语句为止;二是未遇到break语句,则程序依次执行完所有语句组。
  • 3.如果“表达式”的值与任何一个case后面的“常量”的值都不相同,当有default子句时,则执行default后面的语句,如果没有default子句,则结束switch。

注:与C++语言不通的是,case后可跟任意常量,不仅限于整数。case语句后的代码需使用“{}”包裹

循环控制语句

while语句

while语句的一般形式为

while( 表达式 ) {
	循环体
}

while语句的执行过程:

  • 1.求解小括号中表达式的值。如果表达式的值为真,转第2步;否则转第3步。
  • 2.执行循环体,然后转第1步。
  • 3.执行while语句后面的语句。

小括号中表达式的值是否为假,决定着循环体是终止还是继续循环。因此,该表达式的值为循环条件。while循环语句的执行特点是,先判断循环条件是否成立,然后决定是否执行循环体。 当while语句的循环体只包含一条语句时,包含该循环体的“{}”可以省略。

带else的while语句
while( 表达式 ) {
	循环体
} else {
	...
}

当while语句的循环体正常结束时将执行else语句中的代码,否则不执行。由表达式的值为假时导致的循环结束视为循环体正常结束,由break语句导致的循环体结束视为非正常结束。

正常结束的示例:

var i = 0;
while( i++ < 3 ) {
	console.log(i);
} else {
	console.log("正常结束");
}

此段代码的执行结果为:

1
2
3
正常结束

非正常结束的示例:

var i = 0;
while( i++ < 3 ) {
	console.log(i);
	break;
} else {
	console.log("正常结束");
}

此段代码的执行结果为:

1

for语句

在两种循环语句中,for语句最为灵活。for语句的一般形式为:

for (表达式1; 表达式2; 表达式3)
{
	循环体
}

for语句的执行过程:

  • 1.求解表达式1的值。
  • 2.求解表达式2的值,若其值为假,则结束循环,转第4步;若其值为真,则执行循环体。
  • 3.求解表达式3。
  • 4.结束循环。

同while语句,for语句也可以存在else分支语句,表现形式也跟while语句一致。

对象的定义

对象定义的一般形式为:

{
	属性名1: 属性的值,
	属性名2: 属性的值,
	...
	属性名n: 属性的值,
	属性名n+1: 属性的值
}

例如:

var object = {
	name: "张三",
	age: 18
};

变量object即是一个对象,它包含两个属性。

注:当对一个对象不存在的属性赋值时会将此属性加入到对象中,例如:

object.sex = '男';

对象object中就会多出一个“sex”属性。

函数的定义

函数定义的一般形式为:

var fun_name = (形参列表) {
	语句组
};

该语句的含义是:生成一个函数对象,例如:

var print = (a) {
	console.log(a);
};

print(1);

此段代码执行结果为:

1

该语句的作用是:定义一个函数,它有一个名为“a”的形参,执行此函数会将a的值打印到屏幕上。 形参列表,可为空、一个、多个,多个形参之间使用“,”分割。

函数值的返回

和JavaScript类似,函数的定义中不必包含返回值的说明,直接在函数体任意位置添加 return 语句即可结束函数的执行,并且返回指定的值。

单值返回
var test = (arg1, arg2) {
	return arg1 + arg2;
};

console.log(test(1, 2));

此段代码执行结果为:

3
多值返回
var test = (arg1, arg2) {
	return arg1, arg2;
};

console.log(test(1, 2));

此段代码执行结果为:

<1, 2>

return语句在实现上其实仅支持返回一个值,能“多值返回”是因为

表达式1, 表达式2, ..., 表达式n,...

的写法是定义了一个“元组”类型的数据。

元组

定义

元组是一种特殊的数据类型,此类型无法直接被定义,通常和函数的return语句配合使用,可通过函数创建一个元组数据,如:

var tuple = () {
	return 1, 2, 3;	
}();

console.log(tuple);

此段代码的执行结果为:

<1, 2, 3>

使用console.log打印元组类型的数据时,会使用 “<”、“>”将其包括起来,并且使用“,”分割每个元素的值。

赋值操作

var tuple = () {
	return 1, 2;
}();
var a, b;
a, b = tuple;
console.log(a, b);

元组作为右值时,如果左值为 “表达式1, 表达式2, ...” 的写法,则会依次将元组中的值赋值给:表达式1、表达式2、...。否则,当左值为单个表达式时,则跟普通赋值一致,将元组整体赋值给此表达式。

作用域

作用域比较明显的标识的是:"{}",如if、while、for、函数体等语句皆是使用"{}"作为作用域限制的标识。"{}"也可以不依附于if、while、for等语句单独使用,如:

{
	var a = 1;
}

console.log(a);

此段代码的执行结果为:

[Error]: undefined reference: a

值得注意的是,多个连续的“{}”代码块之间至少要包含一句普通代码。如:

if (true) {
	{
    	console.log(1);
	}
}

这段代码无法通过语法检查,可在两个代码块间添加空语句避免这种情况,如:

if (true) {
	;
	{
    	console.log(1);
	}
}

break语句

break语句用于结束当前作用域,可用于 while for switch-case "{}" 等语句产生的作用域中。如:

{
	console.log(1);
	break;
	console.log(2);
}

console.log(3);

此段代码执行结果为:

1
3

break可以多个连用来结束多层作用域。如:

while (true) {
	console.log(1);

	{
    	console.log(2);
    	break, break;
    	console.log(3);
	}

	console.log(4);
}

console.log(5);

此段代码的执行结果为:

1
2
5

break, break, ... 的组合形式还可以配合 continue 一起使用,不过,continue 只能是列表中的最后一个。如:

for (var i = 0; i < 2; ++i) {
	console.log(1);
	{
    	console.log(2);
    	break, continue;
    	console.log(3);
	}

	console.log(4);
}

此段代码的执行结果为:

1
2
1
2