第一章 关于Menthol

1.1. 什么是Menthol?

Menthol 是一种解释型的、面向函数的编程语言。它采用C++开发而成,并且开放源码。配合简便的扩展方式,它可以满足你任何的开发需求 不过,Menthol也有其局限性。你不能使用 Menthol开发出独立的使用操作系统运行的程序,所有Menthol的程序都必须使用一个宿主进行解析后才可正常运行 Menthol 是一种无类型的语言。无类型意味着不必显式定义变量的数据类型。实际上你也无法上明确地定义数据类型。因为所有的类型都是在运行时才会确定的。为了开发方便,Menthol还引入了包和模块的概念,包就是一堆模块,模块就是全局变量,函数的集合体,有点类似面向对象语言里的类的概念,但又不太相同。模块可以是你自己开发的变量函数集合体,也可以是用C/C++开发出来的类库,不过在引用的时候它们没有差别

1.2.准备工作

Menthol是一个使用简单,易于学习的编程语言,如果你有其他编程语言,例如C,javascript,Python等编程语言的编写经验的话,本手册对你来说可能有点多余,你看看程序包或者源码包里的example中的实例就可以开始干了。其实我想说的是,Menthol就是如此简单的一个编程语言,下面我将详细说明

首先,你要获得menthol,访问www.ltplayer.com/menthol.html

这个是menthol项目的页面,它部署于github上,如果你不想研究menthol的源码,直接点击read more会进入github menthol是项目页,在点击release就进入了下载页面

点第一个就可以下载release版本就可以了。

这个是个压缩包,解压后你会看到大概像如下的结构

Compile.exe是编译程序,源码编写都要经过此程序编译后才能正常使用.Run.exe,为运行程序,编译后的源码使用此程序运行,便可以运行menthol程序。menthol.dll是动态链接库,是支持menthol运行的核心库,menthol.lib是静态库,如果你要开发menthol程序的话,需要使用这个静态库,menthol.h,头文件。

lib文件夹下,是menthol自带的一些基本的类库。example是一些实例,关于基本语法等,还包括一个使用zplay开发而成的简单音乐播放器

但是,如果你想研究或者自己编译menthol程序,就需要下载menthol源代码,同样进入menthol页面,点击sourcecode按钮,便会进入menthol在github上的项目仓库,不过前提需要你的电脑上安装过vs2013或者更高版本,因为menthol是在vs2013上开发而成,所以vs2013以下版本是否能正常编译,无法确定

libsrc目录为系统自带包,在编译完menthol以后,需要用menthol编译程序编译它们,并将编译后的文件存入lib文件夹里,example1为实例测试使用的库,如果你要运行测试实例,需要将它放在你运行的程序目录中,或者在程序中指定它的路径,具体可以参考release中的文件安排

可以点击download zip下载源码,这个最简单,如果有你要clone 的话,需要git工具,这需要你有操作git工具的知识,本文档不讨论git文档的使用,不过git工具的使用并不复杂,如果使用GUI工具的话,使用会更加简便,读者只需简单学习便可以使用。

获得源码后,点击menthol.sln将会用打开。然后可以选择release或者debug编译,最后将在debug或者release文件夹中产生编译后的文件

1.3.开始写第一个程序

一个完整的Menthol程序应该包括的是一个__mmain函数、导入的程序包、模块调用、模块定义。__mmain函数很重要,这是启动函数,在程序被调用时,系统会自动执行这个函数,作为整个程序调用的入口,所以,它是必须要有的,否则程序无法正常启动。在menthol中,因为所有的函数定义必须要基于模块开发,但是,__mmain函数是唯一个不需要定义模块就能单独使用的函数,仅此一个函数。

一个最小的Menthol程序不要包含任何的包、模块、全局变量,便可以正常启动。但这似乎并没有什么意义。下面的代码就是一个最小的menthol程序

    _mmain:$a,$c
    {   

    }

但是他什么都不执行,仅仅是程序编写上的正常。

你可以把它复制到记事本或其他编辑器内,将文件保存为main.me类型。me类型为menthol的可执行文件原文件名,取menthol单词的前两个字母。

现在打开控制台,并进入刚才文件所存储目录中,打开Compile.exe,将刚才你存的文件作为参数跟在后面,回车执行,会生成一些调试信息,包括发生错误时,原文件的行数,以及源文件的完整路径

好了,可以看到编译完成,你会发现在main.me下面多出来个main.mee。这个便是编译以后生成的文件,mee是menthol可执行文件的缩写(me execute)

然后我们就可以运行这个程序了,同样的操作方式,只不过要执行run.exe ,将刚才生成的main.mee 作为参数跟在后面执行即可。因为程序内什么都没有执行,所以没有任何输出。

现在把上述程序该一下。

import "console";
use Conosle;
module test{
  def func:$str
  {
      Console.Out($str);
  }
}
_mmain:$a,$b
{
    test.func("牛逼!!!!!!");
}

然后重复上面的编译过程,然后运行

可以看看到了输出文字。Console是控制输入输出与文件读写的库,我们会在后面讲到。

1.4.关键字

“关键字”是对menthol具有特殊含义的单词。标识符不能具有与 menthol关键字相同的拼写和大小写,同时你也不能重新定义关键字, 也不能将关键字用于定义函数名。Menthol目前拥有19个关键字,下面是所有关键字

if else for break true false try except throw continue return while null import _mmain def var in typeof module use

1.5.程序注释

“注释”就是你不舍得删除又嫌它碍眼,或者你可能又不知道在啥时候会继续使用的一段代码的临时废除符号,使用注释符号,可以把你代码中被注释的代码在编译时候忽略掉,但你还能看见它,但编译器不会处理它,它的存在不会对程序运行起任何作用

“注释”是一个以正斜杠/星号组合(/)开头的字符序列,到分隔符 (/) 结束的一段表示。 注释可以占用多行,但无法嵌套。如果你要注释单行也可以采用两个正斜杠//

import "console";
use Conosle;
/*def func:$str
{
    Console.Out($str);
}*/
_mmain:$a,$b
{
    //func("!!!!!!");
}

第二章 数据类型

2.1.基础

Menthol支持目前以下几种类型:

数值 Null 字符串 数组 字典 Object 布尔类型 函数类型

import "console";
use Console;

module test
{
def func:
{
}
}
_mmain:$a,$c
{ 
    var $p = 123; //数值
    Console.Out(typeof($p)); //print M_NUMBER
    $p = null;//null
    Console.Out(typeof($p));//print M_NULL
    $p = "string"; //字符串
    Console.Out(typeof($p));//print M_STRING
    $p = {1,2,3}; //数组
    Console.Out(typeof($p));//print M_ARRAY
    $p = (key1::"value1",key2::"value2"); //字典
    Console.Out(typeof($p));//print M_DICT
    $p = true; //bool
    Console.Out(typeof($p));//print M_BOOL
    $p = test.func;//函数
    Console.Out(typeof($p));//print M_FUN  
}

Menthol作为无类型语言,变量类型可以随时转换类型,而不需其它任何操作

2.2. 布尔类型

布尔类型是数据类型中最简单的类型了,就两个值,true,false,它区分大小写

声明一个布尔类型如下

_mmain:$a,$c
{   
  Var $p = true;
}

布尔类型的值通常用在流程判断里,例如while,if语句中

_mmain:$a,$c
{   
  Var $p = true;
  If($p==true){

  }
}

但是布尔值也是可以隐式转换的,比如:

用在判断中的时候,0表示为false,非0表位为true 用在加法时候,false将转换为数值0,true将转换为数值1 用在字符串连接的时候,false将转换为字符串”false”,true将转换为字符串”true”

2.3. 数值类型

数值可以使用十进制,十六进制,八进制表示,前面可以加上可选的符号(- 或者 +)。 要使用八进制表达,数字前必须加上 0(零)。要使用十六进制表达,数字前必须加上 0x。

_mmain:$a,$c
{   
  var $p = 066;//八进制
  $p = 0xFF;//十六进制
  $p = 123;
  $p = -123; //负数
  $p = 33.648;
}

数值类型的表示范围为-2^1024 ~ +2^1024,如果超过这个范围,程序将会报错。

在表示布尔判断的时候.0将表示为false,非0将表示为true

import "console";
use Console;
_mmain:$a,$c
{   
    if(-1223){
      Console.Out("true");
    }
    if(0){
      Console.Out("false");
    }
}

2.4. 字符串类型

一个字符串 string 就是由一系列的字符组成,其中每个字符等同于一个字节

定义一个字符串的最简单的方法是用双引号(“)把它包围起来。 如果””中没有任何字符,则称之为空字符串,它代表“”中只有一个没有字符,从计算机本身的角度来讲空字符串中也有要给字符是\0,ascii为0

import "console";
use Console;
_mmain:$a,$c
{   
Console.Out("this is a string");
  var $p = "this is a string";
  Console.Out($p);
}

要表达一个单引号自身,需在它的前面加个反斜线(\)来转义。要表达一个反斜线自身,则用两个反斜线(\)。其它任何方式的反斜线都会被当成反斜线本身:也就是说如果想使用其它转义序列例如 \r 或者 \n,并不代表任何特殊含义,就单纯是这两个字符本身。

转义符列表

\n 换行

\r 回车

\b 退格

\” 双引号

字符串的连接通过加号(+)来实现,可以将两个或多个字符串连接在一起成为一个新的字符串

import "console";
use Console;
_mmain:$a,$c
{   
Console.Out("this is a string"++"1"+"2");//print this is a string12
}

事实上。字符串也是一个数组,关于数组的操作可以应用于字符串中,有关数组的定义及操作方式,下一节将会介绍

2.5. 数组类型

所谓数组,是有序的元素序列。若将有限个类型相同的变量的集合命名,那么这个名称为数组名。组成数组的各个变量称为数组的分量,也称为数组的元素

Menthol定义数组使用括号定义([]),在括号中,包含若干用逗号(,)分给的元素。如果括号中没有元素,则这个数组为0个元素的数组。数组元素可以是Menthol中的任何类型、表达式。

_mmain:$a,$c
{   
var $p = [];
  $p = [1,2,3,3+2,true];
}

如果要获取数组元素或设置数组元素的新值,可以使用[]来实现,[]是填写数组的索引值,索引值也称为下标,从0开始,0表示数组的第一个元素

_mmain:$a,$c
{   
  var $arr = ["a","b","c","d","e","f"];
  $a = $arr[1..];  //$a is ["b","c","d","e","f"]
  $a = $arr[..3];//$a is ["a","b","c","d"]
  $a = $arr[2..4];//$a is ["c","d","e"]
  $a = $arr[1];$a is b
}

如果要设置也可以用[]方式来表示,如果设置一个大于当前数组索引的值,则这个索引以前的元素会被自动填充为null

_mmain:$a,$c
{   
  var $arr = [];
  $arr[0] = 1; //$arr is [1]
  $[3] = 2;// $arr is [1,null,null,2]
}

在上一节讲道字符串也是数组的时候说过,数组的操作方式也适用于字符串

_mmain:$a,$c
{   
  var $arr = "abcdef";
  $a = $arr[1..];  //$a is "bcdef"
  $a = $arr[..3];//$a is "abcd"
  $a = $arr[2..4];//$a is "cde"
  $a = $arr[1];$a is b
}

2.6. 字典类型

字典是另一种可变容器模型,且可存储任意类型对象。

字典的每个键值 key,value 对用双冒号 :: 分割,每个键值对之间用逗号 , 分割,整个字典包括在括号()中 , 在字典中,键是唯一的,但值可以重复,键必须是字符串类型,但值可以是任意类型

_mmain:$a,$c
{   
  var $str = "this is a string";
  var $dict = (key1::-111,key2::$str);
}

值可以设置任何数据类型,但键必须是不可变的。

字典的取值和设置方式同样也是通过key,value 用双冒号分割”::”

_mmain:$a,$c
{   
    var $str = "this is a string";
    var $dict = (key1::-111,key2::$str);
    Var $v = $dict::key1; //print -111
    $dict::key1 = -222;
    $v = $dict::key1; //print -222
}

字典的便利可以通过for in 方式

import "console";
use Console;
_mmain:$a,$c
{   
  Var $arr =(key1::"value1",key2::"value2");
  for(var $key,$value in $arr)
  {
    Console.Out($key+":"+$value);
  }
}

如果要设置一个不存在的key,则会将新设置的key加入到字典中,如果取一个不存在的值则会返回null

import "console";
use Console;
_mmain:$a,$c
{   
  Var $arr =(key1::"value1",key2::"value2");
Console.Out($arr::key3); //print null
  $arr::key3=333;
  Console.Out($arr::key3);//print 333
}

2.7. object

Object 通常用在和外部扩展库的交互上,例如C/C++返回了指针类型,则在menthol程序总表示为Object类型,但在menthol源码中,不会使用这种类型

2.8. null类型

null是一个特殊的数据类型,null 值表示一个变量没有值。其实际含义就是部署于任何类型

在下列情况下一个变量被认为是 NULL:

1.被赋值为 NULL。

2.未被赋值。

_mmain:$a,$c
{   
  Var $arr =null;
}

2.9. 函数类型

函数类型不是说定义一个函数,而是说把函数看做一个变量类型,例如把函数当做另一个函数的参数传入,通常被称为回调函数

_mmain:$a,$c
{   
  Var $arr =null;
}

函数类型类型可以是模块函数,本模块内函数,但需要在使用前就已经定义,否则编译将会报错

import "console";
use Console;
module test
{
def callback:$i
{ 
  Console.Out($i);
}

def func:$fun,$i
{
  $fun($i);
}
}
_mmain:$a,$c
{ 
  test.func(test.callback,1000);
}

第三章 变量基础

3.1.基础

menthol中的变量用一个var关键字空格,变量名来表示一个变量。在menthol中变量分为全局变量和局部变量两种,全局变量为在一个模块内可以发生作用的变量用@为前缀,局部变量为一个函数内或者一个作用域内发生作用的变量,用$符号后面跟变量名来表示。变量名是区分大小写的。一个有效的变量名由字母或者下划线开头,后面跟上任意数量的字母,数字,或者下划线

module test
{
var @global; 
}
_mmain:$a,$c
{   
    var $v1=123;
    var $_v2 = “aaaa”;
    {
    Var $v3 = 666;
    }
}

变量默认总是传值赋值。那也就是说,当将一个表达式的值赋予一个变量时,整个原始表达式的值被赋值到目标变量。这意味着,例如,当一个变量的值赋予另外一个变量时,改变其中一个变量的值,将不会影响到另外一个变量

import "console";
use Console;
_mmain:$a,$c
{   
  var $arr =(key1::"value1",key2::"value2");
  var $n = $arr;
  Console.Out($n::key1); //print value1
  $n = 4;
  Console.Out($arr::key1); //print value1
}

变量的声明,可以不要初始化,如果不初始化则变量被默认声明为null,全局变量和局部变量都一样

import "console";
use Console;
_mmain:$a,$c
{   
  var $n ;
  Console.Out($n); //print null
}

变量的声明可以是一行一个,也可以在一个var 关键字后声明多个变量,并且遵循可以初始化也可以不初始化的原则

import "console";
use Console;
_mmain:$a,$c
{   
  var $n=333,$f,$t = 666 ;
  Console.Out($n); //print 333
  Console.Out($f);//print NULL
  Console.Out($t);//print 666 
}

一个重要原则,任何变量的使用(调用或给其赋值),在使用前必须优先被声明,如果未声明时而使用或者在使用后再声明,则编译程序将会报错

_mmain:$a,$c
{   
    $n  = 666; //error
    Var $n; 
}

3.2.作用域

对于menthol来说作用域一般指一个模块、运行程序,以及花括号”{}”,变量的范围即它定义的上下文背景(也就是它的生效范围)。

import "console";
use Console;
_mmain:$a,$c
{ 
var $f;
  {
    var $g;
  }
  {
    Console.Out($g); //错误,在本作用域内没有发现$g
    Console.Out($f); //ok
  } 
}

对于全部变量,他的作用域在整个程序运行范围内,也就是说,在程序启动后,全部变量不论是不是在本模块内,都是可见的,他的生存期是在整个程序启动到结束

对于函数参数,它的作用域就是本函数内,出了本函数,参数就失效了.

对于for in 中的临时变量生命,它只在for in 的循环体内

mmain:$a,$c
{ 
  for(var $g in [1,2,3]){
    $g;   
}
  $g;//错误,$g为临时变量
}

2.3表达式

对大多数语言来说,程序就是一堆表达式的集合.在menthol中,任何的语句基本都叫做表达式,最简单的表达式就是语句的结束符”;”,这就是个表达式,没有任何意思。再比如随便一个数组,数字,字符串,声明、赋值语句等,都可以叫做表达式

_mmain:$a,$c
{ 
  123456;
  "asdfasdf";
  $a = 666;
  var $t = "fffff";
  1+2;
  1*2;
  "aaaaaaa"+"bbbbbbbbbb";
  $a+$c;
  [];
  [1,2,3];
}

但是,一定要注意,任何一个表达式,它的结尾都要有结束符 “;”,否则会被编译器认为存在语法错误。表达式一行可以写多个,但需要在每个表达式后面写结束符

不过表达式本身是可以用”,”分割的,例如

$a = 1,2,3;

类似这种的也是合法的表达式,但是上边的表达式,只会返回1赋值给$a.所以对于声明变量,一下声明都是正确的

module test
{
var @g1,@g2=1,@g3;//ok
}
_mmain:$a,$c
{ 
  var $g1,$g2=1,$g3;//ok
  test.@g1=test.@g2=test.$g3=2;
}

第四章 运算符

4.1运算符基础

运算符是可以通过给出的一或多个值(用编程行话来说,表达式)来产生另一个值(因而整个结构成为一个表达式)的东西。

运算符可按照其能接受几个值来分组。一元运算符只能接受一个值,例如 !(逻辑取反运算符)二元运算符可接受两个值,例如熟悉的算术运算符 +(加)和 -(减),大多数 PHP 运算符都是这种。最后是唯一的三元运算符 ? :,可接受三个值;通常就简单称之为“三元运算符”(尽管称之为条件运算符可能更合适)。

Menthol支持目前以下运算符:

    • / () ; , | & ? [] ! % ^ : :: .. = < > != <> || && >= <= == += -= /= = %= &= |= ^= << >> * typeof

4.2.加法运算符

将数字表达式的值加到另一数字表达式上,或连接两个字符串,数组等

result = expression1 + expression2

import "console";
use Console;
_mmain:$a,$c
{
    var $arr = [1,2,3,4,5,6];
    $arr = 888+$arr;
    $a = null;
    $a = $a+true;
    Console.Out($a);
}

expression1,expression2为表达式。

1.expression1,expression2同为数字时,相加

2.expression1,expression2同为bool时,先转换bool值为数字,true转换为1,false转换为0,然后相加

3.expression1,expression2同为string时,连接

4.expression1,expression2有一个为array时,组合为新的array

5.expression1,expression2有一个为string,另一个数字转字符串,bool转true,false,null转空

6.expression1,expression2 有一个为bool时,先转换bool值为数字,true转换为1,false转换为0,然后相加

7.expression1,expression2 有一个为null时,另一个如果是字符,null转换为空字符串,如果为数字时,true转换为1,false转换为0

4.3减法运算符

从一个表达式的值中减去另一个表达式的值,只有一个表达式时取其相反数。

result = expression1 - expression2

result = -expression1

import "console";
use Console;
_mmain:$a,$c
{
    $a =-6;
    Console.Out($a);
    $a = null;
    Console.Out($a-3);
}

expression1,expression2为表达式。null时,转换为0,true转换为1,false转换为0

除以上类型外,其他类型进行详见操作,则会报错

4.4.乘(*)运算符

两个表达式的值相乘。

result = expression1 * expression2

import "console";
use Console;
_mmain:$a,$c
{
    Console.Out(3*3);
}

expression1,expression2为表达式。null时,转换为0,true转换为1,false转换为0

除以上类型外,其他类型进行详见操作,则会报错

4.5.除法(/)运算符

两个表达式的值相除。

result = expression1 / expression2

import "console";
use Console;

_mmain:$a,$c
{
    $a = 3;
    Console.Out($a/3);
}

expression1,expression2为表达式。null时,转换为0,true转换为1,false转换为0

除以上类型外,其他类型进行详见操作,则会报错

4.6.三目(?:)运算符

根据条件执行两个语句中的其中一个。

test ? expression1 : expression2

test:bool表达式语句expression1,expression2为表达式.

当test为true则执行expression1,否则执行expression2

import "console";
use Console;
_mmain:$a,$c
{
  Console.Out(3>1?3:1); //print 3
  Console.Out(3<1?3:1);//print 1
}

4.7.按位“或”运算符 (|)

对两个表达式执行按位“或”

result = expression1 | expression2

expression1,expression2为表达式

运算符查看两个表达式的二进制表示法的值,并执行按位“或”操作。该操作的结果如下所示:

0101 (expression1)

1100 (expression2)


1101 (结果)

任何时候,只要任一表达式的一位为 1,则结果的该位为 1。否则,结果的该位为 0。

4.8.按位“与”运算符 (&)

对两个表达式执行按位“与”

result = expression1 & expression2

expression1,expression2为表达式

运算符查看两个表达式的二进制表示法的值,并执行按位“与”操作。该操作的结果如下所示:

0101 (expression1)

1100 (expression2)


0100 (结果)

任何时候,只要任一表达式的一位为 1,则结果的该位为 1。否则,结果的该位为 0。

4.9.逻辑“非”运算符 (!)

对一个表达式执行逻辑非。

result = !expression

Expression 任何表达式。

import "console";
use Console;
_mmain:$a,$c
{
  Console.Out(!false); //print true
  Console.Out(!true);//print false
}

Expression为null则转换为false,如果是数字,则0为false,非0为true,其他类型则true

4.10.取余运算符 (%)

一个表达式的值除以另一个表达式的值,返回余数。

result = number1 % number2

expression1,expression2为表达式。null时,转换为0,true转换为1,false转换为0

除以上类型外,其他类型进行详见操作,则会报错

import "console";
use Console;

_mmain:$a,$c
{
  Console.Out(19.9%7); //print 5
}

取余(或余数)运算符用 number1 除以 number2 ,会将两个操作数做取整操作后,再取余数,然后只返回余数作为 result。

4.11.按位“异或”运算符 (^)

对两个表达式执行按位“异或”

result = expression1 ^ expression2

expression1,expression2为表达式

运算符查看两个表达式的二进制表示法的值,并执行按位“异或”操作。该操作的结果如下所示:

0101 (expression1)

1100 (expression2)


1001 (结果)

任何时候,只要任一表达式的一位为 1,则结果的该位为 1。否则,结果的该位为 0。

4.12.赋值运算符 (=)

给变量赋值

result = expression

expression任何表达式。

= 运算符和其他运算符一样,除了把值赋给变量外,使用它的表达式还有一个值。这就意味着可以象下面这样把赋值操作连起来写:

j = k = l = 0;

执行完该例子语句后,j、k、和 l 的值都等于零。 但上面的写法,仅限于赋值,初始化不可以

4.13.小于运算符 (<)

两个表达式的小于比较。

result = expression1 < expression2

import "console";
use Console;
_mmain:$a,$c
{
  Console.Out(3<true)
}

expression1,expression2为表达式。null时,转换为0,true转换为1,false转换为0.两个字符串比较,遵循C的strcmp原则

除以上类型外,其他类型进行详见操作,则会报错

4.14.大于运算符 (>)

两个表达式的大于比较。

result = expression1 >expression2

import "console";
use Console;
_mmain:$a,$c
{
    Console.Out(3>true);
}

expression1,expression2为表达式。null时,转换为0,true转换为1,false转换为0.两个字符串比较,遵循C的strcmp原则

除以上类型外,其他类型进行详见操作,则会报错

4.15.比较运算符 (!=)

两个表达式的大于比较。

result = expression1 <>(!=) expression2

import "console";
use Console;
_mmain:$a,$c
{
    Console.Out(3!=true);
}

expression1,expression2为表达式。null时,转换为0,true转换为1,false转换为0.两个字符串比较,按照逐字符比较

除以上类型外,其他类型进行详见操作,则会报错

4.16.逻辑或运算符 (||)

对两个表达式执行逻辑或。

result = expression1 || expression2

expression1,expression2为表达式。null时,转换为false,数字0转换为false,非0为true,其他类型为true

True True True

True False True

False True True

False False False

4.17.逻辑“与”运算符 (&&)

对两个表达式执行逻辑“与”。

import "console";
use Console;
_mmain:$a,$c
{
    Console.Out(3&&true);
}

result = expression1 && expression2

expression1,expression2为表达式。null时,转换为false,数字0转换为false,非0为true,其他类型为true

当且仅当两个表达式的值都等于 True 时, result 才是 True。如果任一表达式的值等于 False, 则 result 为 False。

4.18.逻辑大于或等于运算符 (>=)

result = expression1 >= expression2

import "console";
use Console;
_mmain:$a,$c
{
    Console.Out(3>=true);
}

expression1,expression2为表达式。null时,转换为0,true转换为1,false转换为0.两个字符串比较,按照逐字符比较

4.19.逻辑“小于或等于”运算符 (<=)

result = expression1 <= expression2

import "console";
use Console;
_mmain:$a,$c
{
    Console.Out(3<=true);
}

expression1,expression2为表达式。null时,转换为0,true转换为1,false转换为0.两个字符串比较,按照逐字符比较

4.20.逻辑“等于”运算符 (==)

result = expression1 == expression2

import "console";
use Console;
_mmain:$a,$c
{
    Console.Out(3==true);
}

expression1,expression2为表达式。null时,转换为0,true转换为1,false转换为0.两个字符串比较,按照逐字符比较

4.21.加法赋值运算符 (+=)

将变量值与表达式值相加,并将和赋给该变量。

result += expression

import "console";
use Console;
_mmain:$a,$c
{
    Var $p = 1;
    $p+=1;
    Console.Out($p);//print 2
}

expression1,expression2为表达式。null时,转换为0,true转换为1,false转换为0.两个字符串,则进行连接操作

使用本运算符与这样指定完全相同:

result = result + expression

4.22.减法赋值运算符 (-=)

将变量值与表达式值相减,并将和赋给该变量。

result -= expression

import "console";
use Console;
_mmain:$a,$c
{
Var $p = 1;
$p-=1;
Console.Out($p);//print 0
}

expression1,expression2为表达式。null时,转换为0,true转换为1,false转换为0.使用本运算符与这样指定完全相同:

result = result - expression

4.23.除法赋值运算符 (/=)

将变量值与表达式值相除,并将和赋给该变量。

result /= expression

import "console";
use Console;
_mmain:$a,$c
{
    Var $p = 9;
    $p/=3;
    Console.Out($p);//print 3
}

expression1,expression2为表达式。null时,转换为0,true转换为1,false转换为0.使用本运算符与这样指定完全相同:

result = result / expression

4.24.乘法赋值运算符 (*=)

将变量值与表达式值相乘,并将和赋给该变量。

result *= expression

import "console";
use Console;
_mmain:$a,$c
{
    Var $p = 9;
    $p*=3;
    Console.Out($p);//print 27
}

expression1,expression2为表达式。null时,转换为0,true转换为1,false转换为0.

使用本运算符与这样指定完全相同:

result = result * expression

4.25.取余赋值运算符 (%=)

result %= expression

import "console";
use Console;
_mmain:$a,$c
{
    Var $p = 9;
    $p%=3;
    Console.Out($p);//print 0
}

expression1,expression2为表达式。null时,转换为0,true转换为1,false转换为0.

使用本运算符与这样指定完全相同:

result = result % expression

4.26.按位“与”赋值运算符 (&=)

result &= expression

import "console";
use Console;
_mmain:$a,$c
{
    var $p =1;
    $p&=1;
    Console.Out($p);//print 1
}

expression1,expression2为表达式。null时,转换为0,true转换为1,false转换为0.

使用本运算符与这样指定完全相同:

result = result & expression

4.27.按位“或”赋值运算符 (|=)

result |= expression

expression1,expression2为表达式。null时,转换为0,true转换为1,false转换为0.

import "console";
use Console;
_mmain:$a,$c
{
    var $p =1;
    $p|=1;
    Console.Out($p);//print 1
}

使用本运算符与这样指定完全相同:

result = result | expression

4.28.按位“异或”赋值运算符 (^=)

result ^= expression

import "console";
use Console;
_mmain:$a,$c
{
    var $p =1;
    $p^=1;
    Console.Out($p);//print false
}

expression1,expression2为表达式。null时,转换为0,true转换为1,false转换为0.其他类型则转换为0

使用本运算符与这样指定完全相同:

result = result ^ expression

4.29.按位左移运算符 (<<)

result = expression1 >> expression2

import "console";
use Console;
_mmain:$a,$c
{
    Console.Out(1<<1);//print 2
}

expression1,expression2为表达式。null时,转换为0,true转换为1,false转换为0.其他类型则转换为0

运算符把 expression1 的所有位向右移 expression2 指定的位数。expression1 的符号位被用来填充右移后左边空出来的位。向右移出的位被丢弃

4.30.按位右移运算符 (>>)

result = expression1 >> expression2

import "console";
use Console;
_mmain:$a,$c
{
    Console.Out(1>>1);//print 0
}

expression1,expression2为表达式。null时,转换为0,true转换为1,false转换为0.其他类型则转换为0

<< 运算符把 expression1 的所有位向左移 expression2 指定的位数。

4.31.幂运算符 ()**

result = expression1 ** expression2

expression1,expression2为表达式。null时,转换为0,true转换为1,false转换为0.

import "console";
use Console;
_mmain:$a,$c
{
    Console.Out(3**3);//print 27
}

除以上类型外,其他类型进行详见操作,则会报错

4.32.typeof

result = typeof(expression)

返回expression的类型

第五章 流程控制

5.1.基础介绍

menthol中的语句一般是按照写的顺序来运行的。这种运行称为顺序运行,是程序流的默认方向。

与顺序运行不同,另一种运行将程序流转换到脚本的另外的部分。也就是,不按顺序运行下一条语句,而是运行另外的语句。

要使脚本可用,该控制的转换必须以逻辑方式执行。程序控制的转换是基于一个“决定”,这个“决定”结果是真或假(返回 Boolean 型 true 或 false)。 创建一个表达式,然后测试其是否为真。主要有两种程序结构实现本功能。

Menthol使用三种流程控制方式

If ,if else ,while,for in

5.2. if else

根据一个表达式的值,有条件地执行一组语句。

if (condition){ statement1

} [else{ statement2}]

condition必选项。一个 Boolean 表达式

statement1可选项。condition 是 true 时要执行的语句。可以是复合语句。statement2可选项。condition 是 false 时要被执行的语句。可以是复合语句。

import "console";
use Console;
_mmain:$a,$c
{
if(1>2){
  Console.Out(1);
}else
{
  Console.Out(2);
}
}

If else 流程的else部分是可选的,可以不写,只写if 也是可以的

import "console";
use Console;
_mmain:$a,$c
{
if(1>2){
  Console.Out(1);
}

5.3. while

执行一个语句,直到指定的条件为 false。

while (expression){ statements

}

expression必选项。Boolean 表达式,在循环的每次迭代前被检查。 如果 expression 是 true,则执行循环。如果 expression 是 false,则结束循环。

statements可选项。expression 是 true 时要执行的一个或多个语句。

import "console";
use Console;
_mmain:$a,$c
{
  $a = 0;
  while($a<10){
    Console.Out($a);
    $a = $a+1;
  }
}

表达式的值在每次开始循环时检查,所以即使这个值在循环语句中改变了,语句也不会停止执行,直到本次循环结束。有时候如果 while 表达式的值一开始就是 FALSE,则循环语句一次都不会执行。

5.4.for in

对应于一个对象的每个,或一个数组的每个元素,执行一个或多个语句。

for (var variable,variable.... in [dict | array]){

}

variable必选项。一个变量,它可以是 object 的任一属性或 array 的任一元素。

dict, array要在其上遍历的字典或数组。

statement可选项。相对于 object 的每个属性或 array 的每个元素,都要被执行的一个或多个语句。

import "console";
use Console;
_mmain:$a,$c
{
  for(var $t,$s in (key1::1,key2::2))
  {
    Console.Out($t + ":" + $s);
  }
  for(var $t in [0,1,2])
  {
    Console.Out($t);
  }
}

在循环的每次迭代前,variable 被赋予 object 的下一个属性或 array 的下一个元素。然后可以在循环内的任一语句中使用它,就好像正在使用 object 的该属性或 array 的该元素一样。

如果结合是dict则variable至少需要两个,一个是key,一个是值

5.5. try except

错误处理 try { statements1

}except:$arg1...{

statements2

}

statements可选.在执行statements1 内容时,如果有throw抛出,则跳转至statements2,$arg1...为抛出错误时的参数可有多个

import "console";
use Console;
_mmain:$a,$c
{
  Try{
Throw 111,222
}
Except:$arg1,$arg2
{
Console.Out($arg1) //print 111
Console.Out($arg1) //print 222
}
}

5.6 throw

抛出错误 Thorw $arg,$arg....

import "console";
use Console;
_mmain:$a,$c
{
  Try{
Throw 111,222
}
Except:$arg1,$arg2
{
Console.Out($arg1) //print 111
Console.Out($arg1) //print 222
}
}

5.7 break

跳出一个循环,用在for in 和while中

Break是无条件的,碰到break就会跳出循环

_mmain:$a,$c
{
  $a = 0;
  while($a<10){
    Break;
  }
}

5.8 continue

继续一个循环,用在for in 和while中

continue是无条件的,碰到continue就会跳会循环开始位置

_mmain:$a,$c
{
  $a = 0;
  while($a<10){
    continue
  }
}

第六章 包、模块、函数

6.1包的基本概念

在menthol中,包分为两个类型,一个类型是用menthol写的包,另一个是用C/C++写的扩展包

menthol包的就是一个编译后的menthol文件或者是一个用C/C++写的库,Menthol包的扩展名是.mep,不过一个包从代码上来说和.me文件写法完全一样。包被编译好后,生成的文件扩展名为.med.用C/C++写的包编译后也需要将扩展名改为.med

6.2.导入包

无论是menthol写的包还是C/C++写的扩展包,包名就是文件名,例如有个包叫做a.med则在程序使用他的时候使用

Improt “a”;

就可以导入这个包了,包名并不区分大小写,也就是说imprt “a”和improt “A”同样都是导入了a.med文件,在语法上,包是可以重复导入的,不过重复导入的包会被忽略掉。

6.3.模块的概念

事实上,一个包中可以包含的只有另外的包,和模块了。模块相当于全局变量和函数的合集,全局变量和函数被封装在一个模块中,可以供其他的模块中的函数或者启动函数调用。

一个包中,可以包含无数个模块,每个模块中又能包含无数个全局变量和函数,而且不能模块中的全局变量和函数相互并不冲突,即使他们叫了相同的名字。

模块使用关键字module开始,后跟模块名{},{}中就是全局变量和函数了

Module test
{
    Var @g;
    def func:
    {
    }
}

一个最简单的模块可以没有全局变量或者函数,只有模块名就够了

Module test
{

}

虽然在语法上是正确的,但是这样的模块实际上并没有什么意义

6.4.模块的使用

在一个包被导入后,你就可以使用这个包中所包含的所有模块了,不过要使用一个模块,要使用关键字use 模块名,来定义

use 关键字后面的模块名可以是一个,也可以是用逗号隔开的多个模块名,这些模块名可以是多个包内的,不要求完全从一个包内引入

Import “FileSystem”;
use File,Drives;
use Directory

模块名是区分大小写的,在使用时一定要了解,被使用的模块名是大写还是小写,否则将找不到。

模块也可以被多次使用,但是如果存在的话,在此被使用的,将被忽略

Import “FileSystem”;
use File,Drives,Directory;
use Directory

6.5.函数的基本知识

函数执行操作,也可以返回值。某些时候是计算或比较的结果。

一个函数中包含有几个操作。这样可使得代码更合理化。可以写一组语句并给其命名,然后通过调用它并传递其需要的信息来运行整组语句。

给函数传递信息可以把信息放在函数名称后面的圆括号中。传递给函数的信息称作参数。某些函数根本不带任何参数,而其他函数带一个或者多个参数。在某些函数中,参数的个数取决于如何使用该函数。

6.6.函数定义和调用

无论是menthol写的包还是C/C++写的扩展包,包名就是文件名,例如有个包叫做a.med则在程序使用他的时候使用

module moduletest
{
    def fucn:$arg1,$arg2...
    {
      return 
    }
}

函数必须在调用之前被定义,否则编译程序将无法使用。 所有的函数都是基于包的概念存在的,他们全都在一个包内。

在同一包内,调用函数不需要加包名,只需要函数名(参数列表)就可以,但是代用外部包函数则需要报名.函数名(参数列表).在本手册中多次使用的Console.Out就是Console模块中的out函数

import “conosle”;
use Console;
_mmain:$a,$c
{
  funcn(1);
  Console.Out(1);
}

6.7.函数参数

通过参数列表可以传递信息到函数,即以逗号作为分隔符的表达式列表。参数是从左向右求值的,并且在menthol中参数值传递

1.函数的参数可能有多个,但在调用时候出入的参数比函数定义的参数要少,则默认传入NULL

import “conosle”;
use Console;
module test
{
def fucn:$a,$s
{
  Console.Out($a);  //print 1
  Console.Out($s); //print null
}
}
_mmain:$a,$c
{
  test.fucn(1);
}

2.函数参数如果少于传入的参数,则多余的将被忽略

3.函数的参数列表使用C++风格的默认参数,不过默认参数都是在最后,即有个默认参数后,后面要么是没有参数,要么剩下的都是默认参数

import “conosle”;
use Console;
module test
{
def fucn:$a,$f,$s = 2,$t = 3
{
  Console.Out($a); //print 1
  Console.Out($f); //print null
  Console.Out($s); //print 2
  Console.Out($t); //print 3
}
}
_mmain:$a,$c
{
  test.fucn(1);
}

6.8.函数返回值

值通过使用可选的返回语句返回。可以返回包括数组和对象的任意类型。返回语句会立即中止函数的运行,并且将控制权交回调用该函数的代码行

import “conosle”;
use Console;
module test
{
    def fucn:
    {
      return 666;
    }
}
_mmain:$a,$c
{
     Console.Out(test.fucn(1));
}

如果省略了 return,则返回值为 NULL。

第七章 API列表

所有的API都在menthol.h中可以看到

1.enum ValueType: 表示所有menthol所使用的数据类型,有些你可能永远不会使用到,但你可以了解

enum ValueType{
    M_NUMBER,
    M_LONG,
    M_DOUBLE,
    M_STRING,
    M_SSTRING,//system string,and it's not remove forever
    M_FUN,
    M_PFUN,
    M_BPMARK,
    M_BOOL,
    M_ARRAY,
    M_DICT,
    M_NULL,
    M_TRYMARK,
    M_FORMARK,
    M_MODULE,
    M_HASH,
    M_UNKONWN,
    M_OBJECT
};

2.StackState

struct StackState
{

  union{
    double d;//数值
    int i;
    hashValue hash;//hash值
    pDict pdict;//字典值
    pArray parray;//数组值
    pString str;//字符串
    StackMark m;
    bool b;//布尔值
    ModuleState* ms;
  };
  pInst p;//表示一个object或者一个指针
  char* name;
  hashValue namehash;//变量或者函数名的hash值
  ValueType v; //数据类型
};

3.typedef int (PrintErrorFunc)(char str,char* cf,int line);

错误调用的函数指针,str 参数为错误信息,cf为发生错误的文件,line 为错误的行数

4.typedef StackState (*funcallback)();

扩展函数函数的函数指针

5.MentholPackMethod void SetPrintCompileErrorFunc(PrintErrorFunc _pef);

指示编译时如果发生错,将调用哪个函数显示错误信息,_per为函数指针

6.MentholPackMethod void SetPrintRunTimeErrorFunc(PrintErrorFunc _pef);

指示运行时如果发生错,将调用哪个函数显示错误信息,_per为函数指针

7.MentholPackMethod int Compile(char* cfile,bool isdebug);

8.MentholPackMethod int Run(char files,char arg1,char* arg2);

执行可执行文件,files为可执行文件.mee的完整文件名,arg1,为__mmain的第一个参数,arg2,为__mmain的第二个参数

9.MentholPackMethod void RegisterModuleFunciton(RunTimeState moduleinst,UserFunctionAtter functionlist);

向系统注册需要扩展的函数名,moduleinst为模块实例,functionlist为函数定义数组

10.MentholPackMethod StackState GetParam(int index);

获取函数参数,index为参数的索引位置,从1开始

11.MentholPackMethod RunTimeState CreateModuleRunTime(char modulename);

创建一个运行模块

12.MentholPackMethod StackState Array_CreateArray();

创建一个数组

3.MentholPackMethod StackState Array_Get(pArray sk1,int index);

获取数组元素,sk1为数组,index为数组位置索引,从0开始

14.MentholPackMethod void Array_Set(pArray sk1,StackState sk2,int index);

设置数组元素,sk1为数组,index为数组位置索引,从0开始,sk2为要这只的新值

15.MentholPackMethod int Array_Length(pArray p);

获取数组元素个数,p为数组

16.MentholPackMethod void Array_Push(pArray sk1,StackState sk2);

增加数组元素,sk1为数组,sk2为要增加的元素

17.MentholPackMethod pString Array_Join(pArray a1,char* link);

用link 将数组a1,连接在一起返回字符串

18.MentholPackMethod pArray Array_Reverse(pArray a1);

反转数组,返回新的数组

19.MentholPackMethod StackState Dict_CreateDict();

创建一个字典

20.MentholPackMethod void Dict_Push(char* key,pDict sk1,StackState sk2);

增加字典元素,key 为键,sk1为字典,sk2为值

21.MentholPackMethod int Dict_Length(pDict p);

获取字典键值对个数,p为字典

22.MentholPackMethod StackState Dict_Get(pDict sk1,hashValue key);

获取字典值对个数,sk1为字典,key为key的hash值

23.MentholPackMethod void Dict_Set(char* key,pDict sk1,StackState sk2);

设置字典值对个数,sk1为字典,key为键,sk2为新值

24.MentholPackMethod pString Dict_Key(pDict pdict,hashValue sk2);

获取字典值对个数,sk1为字典,key为key的hash值

25.MentholPackMethod StackState String_CreateString(char* str);

创建字符串

26.MentholPackMethod void CreateFunctionCall(int pc);

创建回调函数的运行环境,pc为压入函数的参数个数

27.MentholPackMethod void PushNumber(double d);

向栈内压入一个数值

28.MentholPackMethod void PushString(pString str);

向栈内压入一个字符串

29.MentholPackMethod void PushArray(pArray arr);

向栈内压入一个数组

30.MentholPackMethod void PushDict(pDict arr);

向栈内压入一个字典

31.MentholPackMethod StackState CallFunction(StackState fu);

执行函数

32.MentholPackMethod StackState Number_CreateNumber(double d);

创建一个数字

33.MentholPackMethod StackState Null_CreateNull();

创建一个null

34.MentholPackMethod StackState Bool_CreateBool(bool b);

创建一个bool

第八章 开发扩展程序

如果要开发自定义的扩展类库,目前menthol的扩展开发仅可使用C/C++进行开发,你需要了解的几个开发前提是:

1.所有被扩展的函数必须要是静态的函数或者是全局的函数

2.所有被扩展的函数都必须要返回StackState

3.将所有定的函数方位一个类型为UserFunctionAtter的数组当中,UserFunctionAtter是一个结构,有3个属性

struct UserFunctionAtter
{
    char* name;
    funcallback postion; 
    int paramcount;
};

name表示在menthol程序中调用的函数名称

postion为函数指针,执行你刚才定义的函数

paramcount表示你定义的函数的参数个数

4.所有的扩展函数库必须有个函数MentholModuleMethod void MP_Init(),这个函数内会调用CreateModuleRunTimehe和RegisterModuleFunciton将你的函数增加至运行环境内

5.会写或者会改C/C++源码,并且能生成DLL库

menthol扩展开发非常简单,你需要在你的C/C++页面内引入menthol.lib静态库,并且将头文件menthol.h加入到你的源代码中。我下面将以VS中开发为范例开发一个最简单的扩展库

1.在VS中创建了一个类库

引入Menthol.h或者引入menthol所在目录也可以,然后把lib文件包含在内,然后创建启动函数,启动函数的MP_Init,大小写要注意

#include "stdafx.h"

#pragma comment(lib,"Menthol.lib")
#include "Menthol.h"

MentholModuleMethod void MP_Init()
{

}

我们写一个函数,这个函数的功能是,我们输入任何字符串,他都会输出”hello”+ 刚才你输入的字符串

#include "stdafx.h"

#pragma comment(lib,"Menthol.lib")
#include "Menthol.h"


StackState Hello()
{
  StackState input = GetParam(1);
  int c = strlen(input.str->string)+7;
  char* str =(char*)malloc(c);
  memset(str,0,c);
  strcat(str,"hello ");
  strcat(str,input.str->string);
  printf("%s",str);
  free(str);
  return Null_CreateNull();
}

UserFunctionAtter test[] = {
  {"Hello",Hello,1},
  {NULL,NULL,0}
}; 


MentholModuleMethod void MP_Init()
{
  RunTimeState* aesprt = CreateModuleRunTime("mymodule");
  RegisterModuleFunciton(aesprt,test);
}

然后在代码中调用

import "mypackage";
import "mio";
use mymodule;
use Console;
_mmain:$a,$c
{

  mymodule.Hello(Console.In());
}

好了,现在到程序中测试一下,