Skip to content

SandScript 2.5.0 document (Interpreter)

Syai(Ji DongCheng) edited this page Oct 11, 2026 · 1 revision

Welcome to the SandScript-Language wiki!

后有汉语版

SandScript (Sand) Language Reference Documentation

Version: 2.5.0 | Interpreter executable: Sandi | Source file extension: .sand SandScript is a dynamically‑typed interpreted scripting language implemented in C++. It supports arbitrary‑precision decimal arithmetic, arrays, dictionaries, functions, namespaces, object‑oriented classes with inheritance, preprocessor macros, conditional compilation and module importing.

Table of Contents

  1. Getting Started
  2. Lexical Basics
  3. Data Types
  4. Operators & Precedence
  5. Statements
  6. Expressions
  7. Functions
  8. Object‑Oriented Classes
  9. Built‑in Functions
  10. Preprocessor Directives
  11. Full BNF Grammar
  12. Code Examples
  13. Runtime Notes & Pitfalls

1 Getting Started

Command‑line usage

# Run a script [and set biggest divide presition,default presition is 50]
Sandi [--set_BD_DEV_PRECITION 50] test.sand

# Show grammar help
Sandi --help

# Show interpreter version
Sandi --version

# Show license information
Sandi --copyright
Sandi --copyright-apache

Minimal example hello.sand

outputLine("Hello SandScript!");

Run:

Sandi hello.sand

2 Lexical Basics

Comments

  • // line comment: everything from // to end‑of‑line is ignored.
// single‑line comment
output(123); // trailing comment

Identifiers

  • Start with a letter or underscore; subsequent characters can be letters, digits, underscores, non‑ASCII characters (supports Chinese identifiers).
  • Case‑sensitive.

Keywords

if else while for in func return break continue
output input range from import class static new super begin end lambda switch case default namespace emit

String literals

Double‑quoted "...". Supported escape sequences:

  • \n newline
  • \\ backslash
  • \" double quote Unknown escape sequences are output literally.
s = "hello\nworld";

Numeric literals

Decimal integer / fractional numbers backed by high‑precision BigDecimal. No floating‑point precision loss.

123
-45.67
0.0001

Code blocks

Code blocks are wrapped with begin ... end. Curly braces {} are only for dictionary literals.

3 Data Types

Type Description Literal Example
NUMBER Arbitrary‑precision decimal 123, -0.5
STRING UTF‑8 string "abc"
ARRAY Dynamic ordered array [1, 2, "hi"]
DICT Key‑value map {"a":1, 2:"b"}
FUNCTION User‑defined function func f() begin end
CLASS_META Class metadata class A begin end
OBJECT Class instance object new A()
NIL Null value; default return for void operations

Boolean semantics: there are no dedicated boolean types. Number 0 means false; any non‑zero number means true.

4 Operators & Precedence (low → high)

Precedence Operators Description
1 (lowest) `
2 &&``and Logical AND (short‑circuit)
3 == != < > <= >= Comparison
4 + - Addition / subtraction; + also concatenates strings
5 * / Multiplication / division; division‑by‑zero throws runtime error
6 ! - not Unary: logical NOT, arithmetic negation
7 (highest) () call [] index . member ++ -- Postfix operations

Assignment operators: = += -= *= /=. Assignments are statements, not expressions and cannot be nested inside expressions.

Full operator list:

++  --  +=  -=  *=  /=
+  -  *  /
==  !=  <  >  <=  >=
&&  ||  !
=  .  ...

5 Statements

Every regular statement must end with a semicolon ;. Semicolons are not required after begin / before end.

Variable assignment

a = 10;
b = a + 20;
arr[0] = 99;          // index assignment
obj.val = 123;        // object member assignment

Compound assignment

x += 5;
x -= 2;
arr[1] *= 10;

Increment / decrement

i++;
i--;
arr[2]++;

If conditional

if(cond)
begin
    output("yes");
end
else
begin
    output("no");
end

Chained else if is supported:

if(a == 1)
begin end
else if(a == 2)
begin end
else
begin end

switch Conditional Statement

switch ([expression]) {
    case Pattern1: Statements
    case Pattern2: Statements
    ......
}

Supported Data Types for switch Numeric type: number String type: string

Type Identifier: Matches the type of the expression if Guard Condition: An additional boolean condition. The branch executes only when the condition evaluates to true (the switch match must be satisfied first). Example: case val if n % 2 == 0:

default Wildcard Pattern It is recommended to always include default to handle unknown cases. default can be placed anywhere, but it is usually put at the end. Any case that comes after it will never be matched.

Patterns can be combined with or / and.

Restrictions on case: Duplicate case entries are not allowed.

While loop

while(i < 10)
begin
    output(i);
    i++;
end

For‑in loop (array iteration only)

Iterates over arrays only. Cannot iterate dictionaries directly.

for x in [10,20,30]
begin
    output(x);
end

break / continue

break exits the innermost loop; continue skips to next iteration. Valid only inside while / for‑in.

while(1)
begin
    break;
end

return

Returns a value from inside a function.

func add(a,b)
begin
    return a + b;
end

Module import

from "util.sand" import ;

Executes global‑scope code from another .sand source file; imported symbols enter the global scope.

6 Expressions

Literals

123                     // number
"test"                  // string
[1, 2, "abc"]           // array literal
{"name":"tom", 1: 1}    // dict literal
range(0,10)             // generates array [0,1 ... 9]
range(0,10,2)           // range(start, end, step)

Index access for array / dict

arr[0]
dict["key"]

Member access & method call

obj.name
obj.sayHi(1,2)
super.foo(); // call superclass method inside instance method

Function call

add(1,2);

Unary expressions

! 0
-123

7 Functions

Definition syntax: func name(paramList) begin ... end

Ordinary function

func sum(a, b)
begin
    return a + b;
end

output(sum(3,5));

Lambda expression

lambda (pamams) begin ... end

Default parameters param=defaltValue

func total(a, b=2)
begin
    return a + b;
end
output(total(1));//output:3

Variadic parameters ...rest

... marks variadic capture. Remaining arguments are collected into an array. Variadic parameter must appear last in parameter list.

func total(a, b, ...rest)

Argument count mismatch raises runtime error. Functions create a local scope; variables resolve locally first then propagate to parent scope.

8 Object‑Oriented Classes

Basic class definition

class Person
begin
    func init(name)      // constructor: auto‑invoked on new()
    begin
        self.name = name;
    end

    func sayHello()      // instance method: implicit self argument injected by parser
    begin
        output("hi, ");
        output(self.name);
    end

    static func info()   // static method: no self
    begin
        output("Person static");
    end
end

// instantiate
p = new Person("Alice");
p.sayHello();

// invoke static method
Person.info();
  • self: implicit first parameter inside instance methods, points to current object instance. Do not write self in your parameter list manually.
  • init: constructor method; automatically called when new ClassName(args) executes.
  • static func: static method belongs to the class, not instances; invoked via class name.
  • Object members can be freely read‑written with . operator.

Class inheritance : SuperClassName

class Student : Person
begin
    func init(name, grade)
    begin
        super.init(name); // invoke superclass instance method
        self.grade = grade;
    end

    func show()
    begin
        super.sayHello();
        output(self.grade);
    end
end

s = new Student("Bob", 6);
s.show();

super can only be used inside instance methods; forbidden in static methods or global scope.

Preprocessor pseudo‑private #private

Inside a class block use #private var1,var2...;. The preprocessor renames identifiers to __ClassName__varName to simulate private members which cannot be accessed externally.

class A
begin
#private secret;
    func init()
    begin
        self.secret = 123;
    end
end

Signals and Slots

(object >> func);:connect signals and slots (object !> func);:disconnect signals and slots emit object;:launch signal

9 Built‑in Functions

Function Description
output(expr) Print value without appending newline
outputLine(expr) Print value followed by newline
input() Read one line from stdin, returns string
prminput(prompt) Print prompt string then read one input line
tonum(str) Convert string to high‑precision number; error on invalid format
tostring(val) Convert any runtime value to string representation
time() Return UNIX timestamp in seconds as number
time_ms() Return timestamp in milliseconds
sleep(ms) Suspend execution for given milliseconds
args() CLI argumment (string array)
len(arrayOrString) length
conc(arr,valOrArr) Concat array
setOutputColor(str) Set color of console:Red, Blue, Green, Yellow, Cyan, White, Reset(default), Magenta, BrightRed, BrightGreen
system(str) Run Command

There are an easter egg function, you can look at the sourse.

10 Preprocessor Directives (lines starting with #)

Preprocessing runs before lexing / parsing. It handles text‑level macro substitution, file inclusion, conditional compilation and private‑member renaming.

#include filename

Include and preprocess another source file; guards against cyclic duplicate includes.

#include "util.sand"

#from ; include filename

Resolve include relative to interpreter executable directory.

#from ; include "lib.sand"

#define NAME text ... #enddef

Multi‑line text macro definition, terminated by #enddef.

#define PI 3.1415926
#enddef
output(PI);

#inline name=value

Inline costnant.

#inline DEBUG=1

Conditional compilation

#ifdef DEBUG
output("debug mode");
#else
output("release");
#endif

#ifndef RELEASE
// compiled only if RELEASE symbol is undefined
#endif

#elif SYMBOL is also supported.

#private var1,var2...;

Pseudo‑private variable renaming inside class blocks.

Preprocessor performs raw text substitution; it does not validate syntax.

11 namespace

namespace ident begin
    statement*
end

call: namespace.member

12 Full BNF Grammar

Please look at sourse.

13 Code Examples

Factorial function

func fact(n)
begin
    res = 1;
    i = 1;
    while(i <= n)
    begin
        res = res * i;
        i++;
    end
    return res;
end

outputLine(fact(6));

Class inheritance sample

class Animal
begin
    func init(name)
    begin
        self.name = name;
    end
    func speak()
    begin
        outputLine("animal:");
        outputLine(self.name);
    end
end

class Dog : Animal
begin
    func init(name)
    begin
        super.init(name);
    end
    func speak()
    begin
        super.speak();
        outputLine("wang wang");
    end
end

d = new Dog("wangcai");
d.speak();

Array & dictionary usage

arr = [10, 20, 30];
arr[1] = 99;

d = {"a":100, "b":200};
outputLine(d["a"]);

14 Runtime Notes & Pitfalls

  1. Code blocks must use begin ... end. Curly braces {} are only for dictionary literals.
  2. Semicolon ; terminates every statement including bare‑expression statements.
  3. No true / false literals; use numeric 0 for false and non‑zero for true.
  4. Dictionary keys cannot be array or object values, this triggers runtime error.
  5. super is only valid inside instance methods; forbidden in static methods and global scope.
  6. Objects use reference semantics: assigning object variables copies the reference, not the instance. Multiple variables may point to one same object.
  7. Array assignment a = arr performs deep copy of array contents; object assignment copies only reference pointer.
  8. Division‑by‑zero throws runtime exception.
  9. #define performs raw text substitution; be careful with operator precedence and whitespace in macro bodies.
  10. for‑in iterates arrays only. Modifying the loop variable inside the body writes‑back to original array elements. 汉语版:

SandScript(Sand)语言参考文档

版本:2.5.0|解释器程序名:Sandi|源码后缀:.sand SandScript是一门C++实现的动态解释型脚本语言,支持大数高精度运算、数组、字典、函数、面向对象类与继承、预处理器宏、条件编译、模块导入、命名空间。

目录

  1. 快速开始
  2. 词法基础
  3. 数据类型
  4. 运算符与优先级
  5. 语句语法
  6. 表达式
  7. 函数
  8. 面向对象 Class
  9. 内置函数
  10. 预处理器指令
  11. 完整BNF语法
  12. 常见示例代码
  13. 运行时注意事项

1 快速开始

运行命令行

# 执行脚本[,设置除法商最大小数位数,默认50]
Sandi [set_BD_DIV_PRESITION 50 ]test.sand

# 查看帮助语法
Sandi --help

# 版本
Sandi --version

# 版权
Sandi --copyright
Sandi --copyright-apache

hello.sand

outputLine("Hello SandScript!");

运行:Sandi hello.sand

2 词法基础

注释

  • // 行注释,从//到行末尾全部忽略
// 这是单行注释
output(123); // 行尾注释

标识符 IDENT

  • 字母、下划线开头;后续可以是字母、数字、下划线、非ASCII字符(中文标识符可用)
  • 区分大小写

关键字

if else while for in func return break continue
output input range from import class static new super begin end switch lambda case default namespace emit

字符串字面量

双引号包裹"...",支持转义:

  • \n 换行
  • \\ 反斜杠
  • \" 双引号 其他原样输出。
s = "hello\nworld";

数字字面量

十进制,支持小数,高精度BigDecimal,无浮点数精度丢失。

123
-45.67
0.0001

块标记

代码块使用 begin ... end,不是大括号{}。

3 数据类型

类型 说明 字面量示例
NUMBER 高精度十进制大数 123, -0.5
STRING 字符串 "abc"
ARRAY 动态数组 [] [1,2,"hi"]
DICT 字典map,key支持数字/字符串,不支持数组字典做key {"a":1, 2:"b"}
FUNCTION 用户函数 func f() begin end
CLASS_META 类元数据 class A begin end
OBJECT 类实例对象 new A()
NIL 空值,函数无返回默认nil

布尔:没有单独bool类型;用数字0代表假,非0代表真。

4 运算符与优先级(低→高)

优先级 运算符 说明
1(最低) `
2 &&``and 逻辑与,短路求值
3 == != < > <= >= 比较
4 + - 加减,+支持字符串拼接
5 * / 乘除,大数除法,除零抛运行时错误
6 ! - not 一元:逻辑非、负号
7(最高) ()调用 []下标 .成员访问 ++ -- 后缀操作

赋值运算符:= += -= *= /=,赋值是语句不是表达式,不能嵌套。

运算符列表

++  --  +=  -=  *=  /=
+  -  *  /
==  !=  <  >  <=  >=
&&  ||  !
=  .  ...

5 语句语法

所有普通语句末尾必须加分号 ;,块内部语句也要分号;begin/end块不需要分号。

变量赋值

a = 10;
b = a + 20;
arr[0] = 99;          //下标赋值
obj.val = 123;        //对象成员赋值

复合赋值

x += 5;
x -= 2;
arr[1] *= 10;

自增自减

i++;
i--;
arr[2]++;

if条件语句

if(cond)
begin
    output("yes");
end
else
begin
    output("no");
end

支持else if链式

if(a == 1)
begin end
else if(a == 2)
begin end
else
begin end

switch条件语句

switch ([表达式]) {
    case 模式1:语句
    case 模式2:语句
    ......
}

switch支持的数据类型 数类型:number 字符串类型:string

类型标识 匹配表达式的类型 if 守卫条件 附加布尔条件,仅当条件为true时执行分支(需先满足switch) case val n % 2 == 0:
default通配符模式 建议总是包含 default 处理未知情况
default 位置可以在任意位置,但通常放在最后,因为位于其后的case永远不会被匹配到
模式之间可用or/and连接
case 限制: 不能有重复的 case

while循环

while(i < 10)
begin
    output(i);
    i++;
end

for‑in 遍历数组

只支持遍历数组;不支持字典直接遍历。

for x in [10,20,30]
begin
    output(x);
end

break / continue

跳出循环、继续下一轮循环,只能在while/for‑in内部。

while(1)
begin
    break;
end

return 返回

用于函数内部返回值。

func add(a,b)
begin
    return a + b;
end

import导入模块

from "util.sand" import ;

导入另一个sand源码文件,执行全局代码,符号进入当前全局作用域。

6 表达式

字面量表达式

123                     //数字
"test"                  //字符串
[1, 2, "abc"]           //数组字面量
{"name":"tom", 1: true} //字典字面量
range(0,10)             //range生成数组 [0,1...9]
range(0,10,2)           //带步长 range(start,end,step)

下标访问数组/字典

arr[0]
dict["key"]

成员访问与成员调用

obj.name
obj.sayHi(1,2)
super.foo(); //子类内部调用父类同名方法

函数调用

add(1,2);

一元表达式

! 0
-123

7 函数

定义:func 名字(参数列表) begin ... end

普通函数

func sum(a, b)
begin
    return a + b;
end

output(sum(3,5));

Lambda表达式

lambda (pamams) begin ... end

默认参数

func total(a, b=2)
begin
    return a + b;
end
output(total(1));//output:3

可变参数 ...rest

...标记可变参数,剩余全部参数收集为数组给到rest变量。可变参数必须写在参数列表最后一位。

func total(a, b, ...rest)\

参数数量不匹配会报运行时错误。函数内部拥有独立局部作用域;变量先找局部,再向外找父scope。

8 面向对象 Class

基础语法

class Person
begin
    func init(name)      //构造方法 new()时自动调用init
    begin
        self.name = name;
    end

    func sayHello()      //实例方法,第一个隐式参数self
    begin
        output("hi, ");
        output(self.name);
    end

    static func info()   //静态方法,无self
    begin
        output("Person static");
    end
end

//实例化
p = new Person("Alice");
p.sayHello();

//调用静态方法
Person.info();
  • self:实例方法隐式第一个参数,代表当前对象实例,不要手动写self参数,parser自动插入self参数。
  • init:构造函数;调用new ClassName(args)自动执行init(args)。
  • static func:静态方法,不属于实例,通过类名调用,没有self。
  • 对象使用.访问成员;对象成员可以任意读写。

类继承 :父类名

class Student : Person
begin
    func init(name, grade)
    begin
        super.init(name); //调用父类方法
        self.grade = grade;
    end

    func show()
    begin
        super.sayHello();
        output(self.grade);
    end
end

s = new Student("Bob", 6);
s.show();

super只能在实例方法内部使用,用于调用父类的实例方法。

#private伪私有(预处理器)

类内部写#private a,b...;,预处理器自动把标识符重命名为__ClassName__a,实现简单私有变量,外部无法直接访问。

class A
begin
#private secret;
    func init()
    begin
        self.secret = 123;
    end
end

信号槽语法

(object >> func);:连接信号与槽 (object !> func);:断开信号与槽 emit object;:发射信号

9 内置函数

函数 说明
output(expr) 输出内容,不自动换行
outputLine(expr) 输出并换行
input() 读取一行标准输入,返回字符串
prminput(prompt) 输出提示字符串,读取一行输入
tonum(str) 字符串转高精度数字;失败报错
tostring(val) 任意值转为字符串
time() 返回当前系统时间戳(秒,数字)
time_ms() 毫秒级时间戳
sleep(ms) 休眠指定毫秒数
args() 命令行参数
len(arrayOrString) 序列长度
conc(arr,valOrArr) 拼接数组
setOutputColor(str) 设置控制台颜色:Red, Blue, Green, Yellow, Cyan, White, Reset(default), Magenta, BrightRed, BrightGreen
system(str) 系统命令

有一个彩蛋函数,去看源代码吧!

10 预处理器指令(行首#开头)

预处理在词法解析之前执行,处理宏替换、include、条件编译、private重命名。

#from path include filename

导入文件,递归预处理,防止循环重复导入。

#from ; include filename

从exe所在目录导入;;代表使用exe目录作为基准。

#from ; include "lib.sand"

#define NAME text ... #enddef

多行文本宏替换;#enddef结束宏定义。

#define PI 3.1415926
#enddef
output(PI);

#inline name=value

内联常量。

#inline DEBUG=1

条件编译

#ifdef DEBUG
output("debug mode");
#else
output("release");
#endif

#ifndef RELEASE
//未定义RELEASE才编译
#endif

支持#elif SYMBOL。

#private var1,var2...;

类内伪私有,预处理器改名。

预处理器只做文本替换,不做语法检查。

11 命名空间

namespace 名字 begin
    语句*
end

使用: namspace.member

12 完整BNF语法

看源码

13 常见示例代码

示例1:阶乘函数

func fact(n)
begin
    res = 1;
    i = 1;
    while(i <= n)
    begin
        res = res * i;
        i++;
    end
    return res;
end

outputLine(fact(6));

示例2:类继承

class Animal
begin
    func init(name)
    begin
        self.name = name;
    end
    func speak()
    begin
        outputLine("animal:");
        outputLine(self.name);
    end
end

class Dog : Animal
begin
    func init(name)
    begin
        super.init(name);
    end
    func speak()
    begin
        super.speak();
        outputLine("wang wang");
    end
end

d = new Dog("wangcai");
d.speak();

示例3:数组字典

arr = [10, 20, 30];
arr[1] = 99;

d = {"a":100, "b":200};
outputLine(d["a"]);

14 运行时注意事项

  1. 块必须使用begin end,不要使用{};大括号仅用于字典字面量。
  2. 语句末尾分号;不可省略,表达式语句也需要分号。
  3. 布尔没有true/false字面量,0假,非0数字为真。
  4. 字典key不能是数组/对象,会报运行时错误。
  5. super只能在实例方法内部,不能在静态方法、全局作用域使用。
  6. 对象是引用语义;赋值对象变量不会拷贝对象,多个变量指向同一个实例。
  7. 数组赋值:a = arr复制整个数组;对象赋值仅复制引用。
  8. 除法0会抛出runtime异常。
  9. 预处理器#define是纯文本替换,注意宏的空格与优先级。
  10. for‑in遍历中修改循环变量会同步回原数组对应位置。

Clone this wiki locally