awtk/docs/fscript.md
2021-02-20 12:42:20 +08:00

15 KiB
Raw Blame History

fscript 脚本引擎

1. 介绍

fscript 是一个极简的脚本引擎,借鉴了函数语言中一些思路,主要用于低端嵌入式系统,让用户轻松扩展现有系统,而不需要重新编译和下载固件。

  • 特色:
    • 小内存。最低开销小于 400 字节。
    • 小巧。核心代码 1000 行,基本扩展函数 800 行。
    • 灵活。支持多条语句、函数嵌套调用和变量定义。
    • 简单。熟悉任何一种编程语言的人5 分钟内即可学会。
    • 强大。超过 60 个内置函数,支持类似 C 语言的条件语句、循环语句和注释方式,支持复杂的表达式,支持用 C 语言扩展函数。

keil -O1 编译结果:

      Code (inc. data)   RO Data    RW Data    ZI Data      Debug   Object Name
     10380       1580       1909          4          0      86893   fscript.o
  • 限制:
    • 不支持函数定义。

2. 示例

print("hello fscript")
  • 在 PC 上测试运行:

如:

./bin/runFScript 'print("hello fscript")'
  • 在 PC 上测试运行指定文件:

第一个命令行参数以 @ 开头表示它是一个脚本文件,读取其内容执行。

如:

./bin/runFScript @tests/fscripts/demo_while1.fs

3. 语法

数据类型

  • 浮点数类型 (double)。
  • 整数类型 (int32)。支持十进制、二进制和十六进制。
  • 字符串类型 (string)。UTF-8 字符串,用英文双引号扩起来。
  • 布尔类型 (bool)。标准取值为true 和 false非 0 的数值视为 true。
  • 可以通过扩展实现混合数据类型的数组、固定类型的数组和 map。
  • 可以通过类型转换函数得到各种基本类型。如 int8/int16/int32/int64/uint8/uint16/uint32/uintg64/float/double。

注释

格式:

//line comment
/*block comment*/

示例

//this is line comment
/*this is block comment*/

函数调用

print(0xff)
print(100)
print(0b1010)
print("hello fscript")
print("hello", 123)

函数之间可以用空格、换行和英文分号分隔。

定义变量

  • 通过 set 函数设置

如:

set(a, 123)
  • 通过=赋值

如:

a = 123
b = "abc"
c = true

变量名命名规则:

  • 以字母开头,后面可用数字、英文下划线和英文点。

如:

age=123
name="fscript"
file_name="test.txt"
  • 英文点"."可以用来访问对象的成员,如果对应的对象不存在则当作普通变量。

如:

msg.payload = "hello"

在这里,如果对象 msg 存在msg.payload 为 msg 对象的 payload 成员,否则 msg.payload 则是一个普通变量。

  • a、b、c 和 d 为快速访问变量,具有更快的访问速度,建议在循环中优先使用。

如:

a=0
b=0
while(a < 100) {
  a=a+1
  b=b+a
  print(a, b)
}

获取变量

a

获取变量时,如果变量不存在,自动当成字符串处理。不希望当成字符串,可以加上$前缀。

如:

./bin/runFScript 'print($abc)' 

get var abc failed
(null) 
result:true
cost: 112 us
./bin/runFScript 'print(abc)' 

get var abc failed
abc 
result:true
cost: 112 us

函数嵌套调用

print(join(",", 1, 2, 3))
print(join(",",1,2,3,4), join(";",5,6,7,8))

提前返回(return)

var a = 1;
if(a == 1) {
  print("return");
  return;
} else {
  print("end");
}

条件执行

  • 语句方式
a=random(1, 100)
b=random(1, 100)

print("a is ", a);
print("b is ", b);

if(a < b) {
  print(a, "<", b)
} else {
  print(a, ">=", b)
}

else if

var b = "";

if(a == 1) {
  b = "a"
} else if(a == 2) {
  b = "b"
} else if(a == 3) {
  b = "c"
} else if(a == 4) {
  b = "d"
} else {
  b = "other"
}

代码块需要用 {} 扩起来else 语句可以省略。

  • 函数方式
if(true, print("a"), print("b"))

循环执行

  • 语句方式
a=0
b=0

while(a < 100) {
  a=a+1
  b=b+a
  print(a, b)
}

代码块需要用 {} 扩起来

  • 函数方式
a=0
while(a < 10, print(a), a=a+1)

表达式

为了确保优先级正确,尽量用括号明确优先级。

支持的操作符

  • 四则运算:+ - * / %
  • 逻辑运算:|| && !
  • 位运算:| & ~
  • 比较运算:< <= > >= ==
  • 条件运算:(p) ? a : b

对于加法运算 (+),参数中有字符串时,进行字符串连接。

示例

a = 10
b = 20
a<b
1+2
1-2
1*2
1/2
3%2
a||b
a&&b
a|b
a&b
!a
~a
a<b
a<=b
a>b
a>=b
(1+2)*(2+3)
sin(a) + sin(b)
a<b ? print(a) : print(b)
set(a, a+1)
set(a, a+b)
"abc" + "123"

4. 函数

4.1 基本函数

assert

断言


原型
assert(condition, crash_if_fail) => void
  • crash_if_fail 可选。为 true 时,调用系统的 assert否则只是打印警告。

示例

assert(1<2)

eval

执行另外一段脚本。


原型
eval(condition) => value

示例

eval("1+2")

has_error

检查是否之前遇到错误


原型
has_error() => bool

示例

print(has_error())

clear_error

清除之前遇到错误


原型
clear_error() => void

示例

clear_error()

print

打印调试信息到控制台。


原型
print(str,...) => void

示例

print("hello fscript")
print("hello", "fscript")
print(1)
print(true)

a=100
b=200
print(a+b)
print(join(",", a, b))

set

设置变量的值。建议使用=赋值。


原型
set(var, value) => bool

示例

set(a, 1)
a=1

unset

清除变量。

对于不再使用的变量,特别是对象和字符串,请调用本函数清除掉。

当一个变量重复赋值时,会自动清除之前的数据。


原型
unset(var) => void

示例

unset(a)

int

转换为整数类型。


原型
int(var) => int32_t

示例

int("123")

i8

转换为 int8_t 类型。


原型
i8(var) => int8_t

示例

i8("123")

i16

转换为 int16_t 类型。


原型
i16(var) => int16_t

示例

i16("123")

i32

转换为 int32_t 类型。


原型
i32(var) => int32_t

示例

i32("123")

i64

转换为 int64_t 类型。


原型
i64(var) => int64_t

示例

i64("123")

u8

转换为 unt8_t 类型。


原型
u8(var) => uint8_t

示例

u8("123")

u16

转换为 unt16_t 类型。


原型
u16(var) => value(uint16)

示例

u16("123")

u32

转换为 unt32_t 类型。


原型
u32(var) => uint32_t

示例

u32("123")

u64

转换为 unt64_t 类型。


原型
u64(var) => uint64_t

示例

u64("123")

f32

转换为单精度类型。


原型
f32(var) => float

示例

f32("123")

float

转换为浮点数类型。


原型
float(var) => double

示例

float("123")

str

转换为字符串类型。


原型
str(var [,force_pointer_as_str]) => str

force_pointer_as_str 如果输入参数是 POINTER 类型,是否将强制转换成字符串。

示例

str(int(123))
str(msg.payload, true)

iformat

对整数进行格式化(生成的字符串长度不超过 63)


原型
iformat(format, value) => str

示例

iformat("hello:%d", 123)

fformat

对浮点数进行格式化(生成的字符串长度不超过 63)


原型
fformat(format, value) => str

示例

fformat("hello:%lf", 123)

exec

执行 object 里的一个命令。


原型
exec(cmd, arg) => bool

示例

exec("clear", "all")

具体功能与 object 的实现有关。

4.2 字符串函数

join

将多个变量用指定的分隔符拼接起来,形成一个字符串。


原型
join(seperator, s1, s2, s3...) => str

示例

join(",", 1, 2, 3, "abc")

one_of

从字符串数组中取出第N个字符串


原型
one_of(str_array, index, sep) => str

sep为分隔符默认为英文分号(;)。

示例

one_of("aa;bb;cc", 0) # => aa
one_of("aa;bb;cc", 1) # => bb
one_of("aa;bb;cc", 2) # => cc

one_of("aa.bb.cc", 0, ".") # ==> aa

len

取字符串的长度。


原型
len(str) => uint32_t

示例

len("abc")

toupper

将字符串转换成大写。


原型
toupper(str) => str

示例

touppper("abc")

tolower

将字符串转换成小写。


原型
tolower(str) => str

示例

tolower("ABC")

trim

去掉字符串两端的空白字符串。


原型
trim(str) => str

示例

trm("  abc  ")

substr

取子字符串。


原型
substr(str, from, len) => str

示例

substr("abcd", 1, 2)

replace

替换子字符串。


原型
replace(str, old, new) => str

示例

replace("ab cd", "ab", "hello")

contains

检查是否包含指定的子字符串。


原型
contains(str, substr) => bool

示例

contains("ab cd", "ab")

4.3 运算函数

sum

对多个数值类型的参数求和。


原型
sum(n1,n2...)
+(n1,n2...)
n1+n2

示例

print(sum(1, 2, 3))

sub

计算两个数之差。


原型
sub(n1,n2)
或
n1-n2

示例

print(sub(2, 1))
print(2 - 1)

mul

计算两个数之积。


原型
mul(n1,n2)
或
n1*n2

示例

print(mul(2, 1))
print(2 * 1)

div

计算两个数之商。


原型
div(n1,n2)
或
n1/n2

示例

print(div(2, 1))
print(2/1)

%

计算两个数的模。


原型
n1%n2

示例

print(23%7)

and

逻辑与运算。


原型
and(n1,n2)
n1 && n2

示例

print(true && false)
print(true && true)

or

逻辑或运算。


原型
or(n1,n2)
n1 || n2

示例

print(a || b)

not

逻辑非运算。


原型
not(n1)
!(n1)

示例

print(not(true))
print(!(false))

比较函数

支持字符串比较。

<


小于。

原型
<(a,b)
less(a,b)
a<b

<=

小于等于。


原型
<=(a,b)
le(a,b)
a<=b

>

大于。


原型
>(a,b)
great(a,b)
a>b

>=

大于等于。


原型
>=(a,b)
ge(a,b)
a>=b

==

等于。


原型
==(a,b)
eq(a,b)
a==b

!=

不等于。


原型
!=(a,b)
a != b

4.4 数学函数

random

产生随机数。


原型
random() => int
random(min, max) => int

round

四舍五入。


原型
round(v) => double

示例

round(4.5)

floor

返回小于等于指定数值的最大整数。


原型
floor(v) => double

示例

floor(4.5)

ceil

返回大于等于指定数值的最小整数。


原型
ceil(v) => double

示例

ceil(4.5) 

abs

abs 函数。


原型
abs(a) => double

示例

abs(1)

min

min 函数。


原型
min(a, b) => double

max

max 函数。


原型
max(a, b) => double

示例

max(1, 2) 

示例

min(1, 2)

clamp

clamp 函数。


原型
clamp(a, min, max) => double

示例

clamp(2, 1, 3)

4.5 时间函数

已经移动到扩展模块。

5. 自定义函数

5.1 定义函数

static ret_t func_foo(object_t* obj, fscript_args_t* args, value_t* v) {
  value_set_int(v, 123);

  return RET_OK;
}

5.2 注册和使用私有函数

  value_t v;
  object_t* obj = object_default_create();
  object_set_prop_pointer(obj, "function.foo", (void*)func_foo);

  fscript_eval(obj, "foo()", &v);
  value_reset(&v);

  OBJECT_UNREF(obj);

5.3 注册全局函数

  • 初始化时调用
fscript_register_func("foo", func_foo);

5.1 定义私有函数

6. 性能测量与优化

runFScript 的第二个参数可以指定运行次数,方便测量某个函数的运行时间。

比如:

./bin/runFScript '123+234' 100000

通常设置变量和获取变量相对来说比较费时,为此 fscript 提供了 4 个寄存器变量a,b,c,d。如果需要临时保存计算结果优先使用寄存器变量。

比如:

 ./bin/runFScript 'set(a, 2); set(b, 3); print(a+b)'

7. 扩展模块

8. 更多示例

请参考 tests/fscripts 目录。