首页 > 开源 > zlang v0.12.2.0 发布,支持中文编程、支持Web开发/桌面应用开发的国产全自研编程语言

zlang v0.12.2.0 发布,支持中文编程、支持Web开发/桌面应用开发的国产全自研编程语言

OSChina资讯 2026-08-26 09:09 2 阅读 查看原文

目录

一、zlang简介

二、更新日志

三、技术专栏

3.1. 《zlang不需要getter、setter八股文》

3.2. 《构造和解析二进制数据包》


zlang是一门完全国产自研的、追求干净观感的、基于对象克隆支持中文编程的编程语言:

  • 完全从底层开始编写,语言引擎(词法 / 语法分析等)、对象库等均未基于 / 采用任何已有语言进行改造、包装或扩展,保证了代码和权利的干净、清晰与自主可控。

  • 定位:简洁、高效、易上手、全栈能力,兼顾开发效率与运行性能。

  • 设计理念:解决主流语言痛点(如 C 语言字符串处理繁琐、Java 冗余的 Getter/Setter),融合优势并加入创新设计。

  • 对标语言:Python。

  • LOGO:中华狸花猫,象征独立、坚韧、不畏挑战。

一、Hello world

import stdtypes stdio ;

function int main( array args )
{
        stdout.Println( "Hello world" );

        return 0;
}

二、核心亮点(5 大特色)

1. 完全从底层自研,自主可控,国产编程语言

  • 不基于任何其它语言/框架/引擎的改造或扩展,核心技术100%自研,真正自主可控;

  • 产品起源于中国,完全中国制造,填补了国内原创编程语言领域的空白,和世界一起推动编程语言发展;

2. 原生中文编程,降低门槛

  • 原生支持中文关键字和标识符,提供对中文编程的良好支持,打破英文编程的传统壁垒,适配国人的文字习惯,降低编程入门门槛,助力编程教育普及,让软件技术更贴合本土场景;

3. 创新语法设计,简洁高效

  • 源于C/C++/Java又发展创新:C/Java的语法已经设计的相当完美了,新语言根本没必要完全设计一套新的语法,zlang语法继承自C/C++/Java,能让最广大同学们快速学习掌握;

  • 极简哲学,万物皆对象:摒弃class,进化面向对象思想,声明变量即复制对象,简化和统一对象体系;

  • 语义直观,减少冗余注解:支持带空格、符号、汉字等的标识符命名;在Web开发中,允许以HTTP 方法与 URL来定义函数名(如 function GET /api/user(){ ... }),无需额外注解,减少命名工作,让代码语义一目了然,大幅降低开发量;

  • 以后不用写Getter/Setter繁琐代码了:没必要为了将来1%的扩展性导致99%都遵循繁琐的写法,zlang提供了属性拦截器解决扩展性问题;

  • 对异常处理体系的重新思考和实践

  • 吸取了很多语言的优秀精华:比如atomicdefer等;

4. 完整生态,开箱即用

  • 官方标准库内置微整型、短整型、整形、长整型、浮点型、双浮点型、布尔型、字符串型、函数指针型等基础对象;

  • 官方标准库内置数组、链表、映射、队列、栈、有序堆、树、缓冲区等数据容器;

  • 官方标准库内置大数运算、日期时间、文件、输入输出、随机数、编码、缓存、JSON、XML、MSOffice、线程、日志等对象库;

  • 官方标准库内置了加解密对象库,支持常用MD5SHA1SHA256SHA512DES3DESAESRSASM2SM3SM4等算法,以及秘钥管理;

  • 官方标准库内置了互斥量、读写锁、条件变量、内存屏障等线程同步机制;

  • 官方标准库内置数据库对象库支持MySQLPostgreSQLSqlite数据库;

  • 官方标准库内置网络对象ipnetaddresstcpudphttphttpclienthttpserverhtmlsection,自带高性能HTTP服务+网页模板引擎套装,支持Web应用开发;

  • 官方还提供了跨平台图形界面库开源项目gtk-zlang,支持WindowsLinux桌面应用开发;

5. 高性能 + 跨平台

  • 语言内核+对象库用C实现,耗时极低,压测验证性能稳定,静态网页 TPS 达 25000(5 并发),动态网页 RT 5~30ms(单个网页后端含 5 条结果集查询 SQL、23 条单记录查询 SQL、1 条插入 SQL)。

  • 支持 Windows、Linux (glibc>=2.17) 跨平台编译与运行。

三、快速开始

  1. 源码仓库地址:https://gitee.com/calvinwilliams/zlang (仓库内含整套源代码、测试用例与详细介绍)

  2. 环境要求:Windows、Linux (glibc>=2.17)

四、关于作者

zlang由独立开发者calvinwilliams从零研发,历时多年打造。

zlang文档传送门

zlang技术专栏传送门


0.12.2.0 2026-08-13 calvin

调整调试宏架构

对象zruntime新增函数PrintObjectBase(object)、PrintObjectFunctions(object)、PrintObjectProperties(object)

解决对象内的属性集合实体的循环引用的缺陷

0.12.1.0 2026-08-09 calvin

对象poller新增支持定时器

修复了原子机制的一个问题

0.12.0.0 2026-08-07 calvin

对象eventfd新增函数SetFd、IsValid、GetFd

对象thread、queue新增函数BindEventFd(eventfd)、GetFd;多路复用适用对象扩充到线程和消息队列了

zlang启动参数新增"--child--level"

0.11.13.2 2026-08-05 calvin

性能优化

0.11.13.1 2026-08-03 calvin

内部做了小调整

回归测试脚本输出带颜色了

0.11.13.0 2026-07-31 calvin

同步了network文档中的返回值和异常说明

新增对象eventfd

现在子线程返回如果带致命错误会传递给主线程了

0.11.12.0 2026-07-30 calvin

ip对象新增函数CreateFromDefaultGateway、CreateFromViaConnectingGateway、SetFromDefaultGateway、SetFromViaConnectingGateway

0.11.11.0 2026-07-28 calvin

netaddress对象新增函数GetIpInteger、Create(uint,ushort)、Set(uint,ushort)

对象-缓冲区转换函数支持跟踪当前偏移量了

修复了启动线程时未把当前对象和函数传入环境上下文的问题

调整了一批全局API名字

根对象新增函数GetDirectPropertySize、GetPropertiesDirectPropertySize

0.11.10.0 2026-07-27 calvin

所有数值类对象都增加ToTiny~ToULong~ToDouble系列函数

各网络对象明确了超时类型、端口类型、数据长度类型,以及更新相关函数原型

buffer对象新增函数ResizeElementCount、GetSize

0.11.9.1 2026-07-25 calvin

重构了zexception对象,一劳永逸的解决了zexception和string加载优先级悖论

补充thread对象,如果是非守护线程,子线程结束后会把致命错误和异常传回主线程

0.11.9.0 2026-07-24 calvin

新增pipe对象

对象poller支持pipe对象

0.11.8.0 2026-07-23 calvin

TEST_RUNTIME_LEVEL对内性能优化

string对象增加内置小缓存区以减少内存块分配次数,整个string对象代码做了调整

ZlangRuntime一些成员移到ZlangGlobalEnvironment,真正把ZlangGlobalEnvironment用起来

0.11.7.0 2026-07-22 calvin

新增zlang命令行参数--builtin-parameters或-B用于显示所有内部缺省参数

修正了基础类型升级的问题

0.11.6.0 2026-07-21 calvin

解开了一些类型检查硬性约束,更有适配性和灵活度

输出格式化增加内嵌非基础变量自动调用ToString

重整C开发zlang对象库模板zlang_c_object而调整一些地方,完善文档


《zlang不需要getter、setter八股文》

gettersetter八股文

目前很多编程语言都有gettersetter八股文,本人很是反感,这也是创造zlang的主要原因之一。

gettersetter的作用是解耦 调用者代码 和 被调对象字段代码,其实现在IDE已经很智能了,换名字就是一键的事情。

Java

class MyClass
{
	private int		age ;
	public MyClass() {
    }
	public int getAge()
	{
		return age;
	}
	public void setAge( int age )
	{
		this.age = age ;
	}
}
public class Main {
    public static void main(String[] args) {
        MyClass o = new MyClass();
        o.setAge(25);
        System.out.println(o.getAge());
    }
}

zlang不需要gettersetter八股文

zlang设计成不需要gettersetter八股文,代码理解直观,代码量大幅减少,产出提效。

zlang

object MyObject
{
	int		age ;
}
function int main( array args )
{
	MyObject	o ;
	o.age = 25 ;
	stdout.FormatPrintln( o.age );
}

gettersetter允许“存取字段值时可以做一些转换”

还有人解释gettersetter允许“存取字段值时可以做一些转换”,你评估一下真实项目中有多少需要转换的需求,为了所谓的将来莫须有的扩展性,造成所有代码中都无谓充斥着大量gettersetter是一种愚蠢和低效。

Java

class MyClass
{
	private int		age ;
	public MyClass() {
    }
	public int getAge()
	{
		return age;
	}
	public void setAge( int age )
	{
		if (age < 0 || age > 300) {
			throw new IllegalArgumentException( "age invalid" );
		this.age = age ;
	}
}
public class Main {
    public static void main(String[] args) {
    	try {
			MyClass o = new MyClass();
			o.setAge(25);
			System.out.println(o.getAge());
		} catch (IllegalArgumentException e) {
			System.out.println("EXCEPTION:" + e.getMessage());
        }
    }
}

zlang有属性拦截器

你会问,那么将来有字段存取过程需要转换怎么办?zlang的回答是:用属性拦截器。1%的字段需要转换,就写1%的字段属性拦截器,而不是写100%的gettersetter,不多写一个字符,干净利落。

zlang

import stdtypes stdio ;
object MyObject
{
	int		age ;
	intercept set age( object age )
	{
		if( age < 0 || age > 300 )
		{
			zexception.SetException(-1,"age invalid");
			return;
		}
		this.age = age ;
		return;
	}
}
function int main( array args )
{
	try
	{
		MyObject	o ;
		o.age = -1 ;
		stdout.FormatPrintln( o.age );
		catch
		{
			stdout.Println( zexception.GetExceptionDesc() );
		}
	}
}

执行效果

$ zlang zlang_dont_need_getter_setter.z
age invalid

《构造和解析二进制数据包》

zlang的缓冲区对象buffer特别适合构造和解析二进制数据包,下面以IP数据包为例,介绍zlang如何编写代码来构造和解析二进制数据包。

之所以选择IP数据包并不是鼓励用zlang处理网络协议栈,而是觉得IP数据包是典型的二进制数据包的研究案例。

构造总体思路

  • 定义一个数据包对象,按顺序定义各字段(注意字段的字长;如果只取字段的几个比特,在字段名后面加上位域:(bit_fields);如果多个字段的位段映射到目标缓冲区中的一个字节,写对位域即可);

  • 用缓冲区对象函数ResizeElementCount调整好缓冲区对象内部缓冲区大小;

  • 为不影响父级对象,克隆子对象;填充子对象各字段的值;子对象缺省继承根对象zobject,调用根对象函数DumpBuffer倾倒到缓冲区对象缓冲区中;

  • IP数据包头还有附带区域,可以在倾倒缓冲区后附加倾倒一个字符串;

  • 倾倒后调用标准输出对象函数HexPrintln按十六进制打印输出缓冲区;(可选)

编写构造具体代码段

定义IP数据包对象:

object ip_package
{
	utiny	version:4 ;
	utiny	ihl:4 ;
	utiny	tos ;
	ushort	total_length ;
	ushort	identification ;
	utiny	flags:3 ;
	ushort	fragment_offset:13 ;
	utiny	ttl ;
	utiny	protocol ;
	ushort	header_checksum ;
	uint	source_ip ;
	uint	dest_ip ;
}

调整好缓冲区对象内部缓冲区大小:

		ip_packet_head_size = ip_package.GetPropertiesDirectPropertySize() ;
		ip_packet_payload_size = (payload.Length()+4)/55 ;
		ip_packet_size = ip_packet_head_size + ip_packet_payload_size ;
		stdout.FormatPrintln( "ip_packet_head_size[%d]B + ip_packet_payload_size[%d]B = ip_packet_size[%d]B" , ip_packet_head_size , ip_packet_payload_size , ip_packet_size );
		buf.ResizeElementCount( ip_packet_size );

填充子对象各字段的值:

		ipp1..;
			..version = 4b0100UT ;
			..ihl = 5UT ;
			..tos = 8b00000000UT ;
			..total_length = ip_packet_size.ToUShort() ;
			..identification = 0x1234US ;
			..flags = 3b010UT ;
			..fragment_offset = 4098US ;
			..ttl = 64UT ;
			..protocol = 6UT ;
			..header_checksum = 0US ;
			..source_ip = ip.CreateFromString("192.168.0.1").ToIntegerInHostOrder();
			..dest_ip = ip.CreateFromString("192.168.0.2").ToIntegerInHostOrder();

倾倒各字段以及附带区域到缓冲区:

		offset = 0 ;
		ipp1.DumpBuffer( buf , offset );
		buf.CopyIntoAsVString( offset , payload );

按十六进制打印输出缓冲区:

		stdout.HexPrintln( buf );

解析总体思路

  • 已经定义好了数据包对象;拿到构造好的缓冲区对象;

  • 设置解析偏移量为0,调用缓冲区对象函数ToEntityObject解析到数据包对象中各字段;偏移量自动前进;

  • IP数据包头还有附带区域,计算出附带区域大小,调用缓冲区对象函数CopyOutAsVString解析到字符串中;

编写解析具体代码段

解析缓冲区对象到数据包对象中各字段:

		offset = 0 ;
		buf.ToEntityObject( <object>ipp2 , offset );

解析附带区域:

		buf.CopyOutAsVString( offset , payload2 , buf.GetSize()-ip_package.GetPropertiesDirectPropertySize() );

构造和解析的完整源代码

源文件名:tech-notes/binary-packet.z

import stdtypes stdio network ;

object ip_package
{
	utiny	version:4 ;
	utiny	ihl:4 ;
	utiny	tos ;
	ushort	total_length ;
	ushort	identification ;
	utiny	flags:3 ;
	ushort	fragment_offset:13 ;
	utiny	ttl ;
	utiny	protocol ;
	ushort	header_checksum ;
	uint	source_ip ;
	uint	dest_ip ;
}

function void PrintlnIpPackage( ip_package ipp , string payload )
{
	stdout.FormatPrintln( "ipp.version[%{ipp.version}]" );
	stdout.FormatPrintln( "ipp.ihl[%{ipp.ihl}]" );
	stdout.FormatPrintln( "ipp.tos[%{ipp.tos}]" );
	stdout.FormatPrintln( "ipp.total_length[%{ipp.total_length}]" );
	stdout.FormatPrintln( "ipp.identification[0x%04x]" , ipp.identification );
	stdout.FormatPrintln( "ipp.flags[%{ipp.flags}]" );
	stdout.FormatPrintln( "ipp.fragment_offset[%{ipp.fragment_offset}]" );
	stdout.FormatPrintln( "ipp.ttl[%{ipp.ttl}]" );
	stdout.FormatPrintln( "ipp.protocol[%{ipp.protocol}]" );
	stdout.FormatPrintln( "ipp.header_checksum[%{ipp.header_checksum}]" );
	stdout.FormatPrintln( "ipp.source_ip[%s]" , ip.CreateFromIntegerInHostOrder(ipp.source_ip).ToString() );
	stdout.FormatPrintln( "ipp.dest_ip[%s]" , ip.CreateFromIntegerInHostOrder(ipp.dest_ip).ToString() );
	stdout.FormatPrintln( "ipp.payload[%s]" , payload );
	
	return;
}

function int main( array args )
{
	try
	{
		ip_package	ipp1 , ipp2 ;
		string		payload = "helo" ;
		string		payload2 ;
		int		ip_packet_head_size ;
		int		ip_packet_payload_size ;
		int		ip_packet_size ;
		buffer<char>	buf ;
		int		offset ;
		
		ip_packet_head_size = ip_package.GetPropertiesDirectPropertySize() ;
		ip_packet_payload_size = (payload.Length()+4)/55 ;
		ip_packet_size = ip_packet_head_size + ip_packet_payload_size ;
		stdout.FormatPrintln( "ip_packet_head_size[%d]B + ip_packet_payload_size[%d]B = ip_packet_size[%d]B" , ip_packet_head_size , ip_packet_payload_size , ip_packet_size );
		buf.ResizeElementCount( ip_packet_size );
		
		ipp1..;
			..version = 4b0100UT ;
			..ihl = 5UT ;
			..tos = 8b00000000UT ;
			..total_length = ip_packet_size.ToUShort() ;
			..identification = 0x1234US ;
			..flags = 3b010UT ;
			..fragment_offset = 4098US ;
			..ttl = 64UT ;
			..protocol = 6UT ;
			..header_checksum = 0US ;
			..source_ip = ip.CreateFromString("192.168.0.1").ToIntegerInHostOrder();
			..dest_ip = ip.CreateFromString("192.168.0.2").ToIntegerInHostOrder();
		stdout.Println( "--- ipp1 ---");
		PrintlnIpPackage( ipp1 , payload );
		
		offset = 0 ;
		ipp1.DumpBuffer( buf , offset );
		buf.CopyIntoAsVString( offset , payload );
		stdout.HexPrintln( buf );
		
		offset = 0 ;
		buf.ToEntityObject( <object>ipp2 , offset );
		buf.CopyOutAsVString( offset , payload2 , buf.GetSize()-ip_package.GetPropertiesDirectPropertySize() );
		stdout.FormatPrintln( "offset[%d]B" , offset );
		stdout.Println( "--- ipp2 ---");
		PrintlnIpPackage( ipp2 , payload2 );
		
		catch
		{
			stdout.Println( zexception.GetExceptionDesc() );
		}
	}
	
	return 0;
}

执行效果

$ zlang binary-packet.z
ip_packet_head_size[20]B + ip_packet_payload_size[5]B = ip_packet_size[25]B
--- ipp1 ---
ipp.version[4]
ipp.ihl[5]
ipp.tos[0]
ipp.total_length[25]
ipp.identification[0x1234]
ipp.flags[2]
ipp.fragment_offset[4098]
ipp.ttl[64]
ipp.protocol[6]
ipp.header_checksum[0]
ipp.source_ip[192.168.0.1]
ipp.dest_ip[192.168.0.2]
ipp.payload[helo]
           | 0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F  | 0123456789ABCDEF
-----------+-------------------------------------------------+-----------------
0x00000000 | 45 00 00 19 12 34 50 02 40 06 00 00 C0 A8 00 01 | E....4P.@.......
0x00000010 | C0 A8 00 02 68 65 6C 6F 00                      | ....helo.
offset[25]B
--- ipp2 ---
ipp.version[4]
ipp.ihl[5]
ipp.tos[0]
ipp.total_length[25]
ipp.identification[0x1234]
ipp.flags[2]
ipp.fragment_offset[4098]
ipp.ttl[64]
ipp.protocol[6]
ipp.header_checksum[0]
ipp.source_ip[192.168.0.1]
ipp.dest_ip[192.168.0.2]
ipp.payload[helo]