您的位置:首页 >聚焦 >

世界今日讯!里程碑!用自己的编程语言实现了一个网站

2022-09-17 17:49:46    来源:程序员客栈
前言

在上一篇《终于实现了一门属于自己的编程语言》 介绍了自己写的编程语言 GScript ,在文中提到希望最终可以使用 GScript开发一个网站。

到目前为止确实是做到了,首页地址:

https://gscript.crossoverjie.top/index


【资料图】

要称为一个网站确实有点勉强,不过也是一个动态网页,因为返回的是 HTML,所以在当前阶段只要不嫌麻烦其实也能写一个“合格”的网站,有点像以前我们学习 Java时的 servlet。

该页面的源码地址在这里:https://github.com/crossoverjie/gscript-homepage

其实总共也就40来行代码:

classGScript{stringauthor;string[]features;stringsince;GScript(stringa,string[]f,strings){author=a;features=f;since=s;}}func(HttpContext)index(HttpContextctx){string[]features={"statically","strongly"};GScriptgs=GScript("crossoverJie",features,"2022");stringj=JSON(gs);println(j);stringlocal=getCurrentTime("Asia/Shanghai","2006-01-0215:04:05");println("local="+local);stringhtml=^GScript

______________|_|___||_|.|_-|_|_||.|_||_|___|___|_||_|_|_||___||_|v0.0.7^+j+^

current^+local+^

GScript-homepacesourcecode

^;ctx.HTML(200,html);}httpHandle("GET","/index",index);string[]args=getOSArgs();if(len(args)==3){httpRun(":"+args[2]);}else{httpRun(":8000");}

全是利用 GScript所提供的标准库实现的,后文会详细聊聊内置 HTTP 包。

更新内容

下面重点来看看 v0.0.8这个版本相较于上一个更新了哪些地方。

因为我是把自己当做一个开发者的角度去实现了一个 http 服务,同时还用 GScript刷了两道简单的 LeetCode;为了让这个过程更流畅,更符合一个现代语言的使用方式,所以本次真的更新不少东西。

刷题源码:https://github.com/crossoverJie/gscript/tree/main/example/leetcode

大概如下:

any类型的支持,简化标准库的实现。可以用 ^^来声明多行字符串,方便声明复杂字符串。更完善的类型推导,修复了上个版本中某些情况推导不出类型的bug。支持运算符重载。基本的 http 包,可以开发出 http 服务,目前能响应 JSON以及 HTML。新增内置函数:根据时区获取当前时间、获取应用启动参数等。JSON的序列表以及查询,语法级适配了 XJSON。修复了在多个 block嵌套情况下不能正确 return的 bug。

其实从这些更新中也能看出,上个版本只是一个简单能用的状态,而现在这个版本已经可以拿来写复杂逻辑了,当然目前还缺乏一些更友好的编译提示以及运行时错误。

下面仔细聊聊一些更新内容。

any 类型

首先是 any通用类型,这个类似于 Java 中的 Object和 Go 中的 interface{},极大的方便了我们编写一些标准库。

以之前内置的 hash 和 len 函数为例,需要对每种类型都实现一遍,非常麻烦而且毫无必要;现在只需要定义一次即可,代码量直接省几倍。

同理,之前实现的 Map 只支持存放 string 类型,现在便能存放任何类型的数据。

对 any 的实现过程感兴趣的朋友,今后可以单独分享一下。

运算符重载

写 go 或者是 Java 的朋友应该知道,这两门语言都无法对两个对象进行运算,编译器会直接报错。

但在一些特殊场景下还是蛮好用的,于是我参考了 C#的语法在 GScript中也实现了。

classPerson{intage;Person(inta){age=a;}}Personoperator+(Personp1,Personp2){Personpp=Person(p1.age+p2.age);returnpp;}Personoperator-(Personp1,Personp2){Personpp=Person(p1.age-p2.age);returnpp;}Personp1=Person(10);Personp2=Person(20);Personp3=p1+p2;println("p3.age="+p3.age);assertEqual(p3.age,30);

声明的函数名称必须为 operator,之后跟上运算符便实现了重载。

支持的运算符有:+-*/ < >= <= > ==。

JSON支持

当前版本中支持将对象、基本类型进行序列化,暂不支持反序列化为对象,但可以根据 JSON字符串通过一定的语法查询数据。

内置了两个 JSON 相关函数:

//returnJSONstringstringJSON(anya){}//JSONquerywithpathanyJSONGet(stringjson,stringpath){}

classPerson{intage;stringname;floatweight;boolman;Person(stringn,inta,floatw,boolm){name=n;age=a;weight=w;man=m;}}Personp1=Person("abc",10,99.99,true);Personp2=Person("a",11,999.99,false);stringjson=JSON(p1);println(json);//output:{"age":10,"man":true,"name":"abc","weight":99.99}

以这段代码为例,调用 JSON函数可以将对象序列化为 JSON字符串。


classPerson{intage;stringname;floatweight;boolman;Person(stringn,inta,floatw,boolm){name=n;age=a;weight=w;man=m;}}Personp1=Person("abc",10,99.99,true);stringjson=JSON(p1);println(json);intage=JSONGet(json,"age");println(age);assertEqual(age,10);

使用 JSONGet函数可以在一个 JSON 字符串中查询任意的数据,这个功能是通过适配 XJSON 实现的,所以 XJSON支持的查询语法都能实现。

stringj=^{"age":10,"abc":{"def":"def"},"list":[1,2,3]}^;Stringdef=JSONGet(j,"abc.def");println(def);assertEqual(def,"def");intl1=JSONGet(j,"list[0]");println(l1);assertEqual(l1,1);stringstr=^{"name":"bob","age":20,"skill":{"lang":[{"go":{"feature":["goroutine","channel","simple",true]}}]}}^;Stringg=JSONGet(str,"skill.lang[0].go.feature[0]");println(g);assertEqual(g,"goroutine");

比如这样复杂的嵌套 JSON,也能通过查询语法获取数据。

HTTP 包

HTTP 包是本次升级的重点,标准库中提供了以下函数和类:

//httplib//ResponsejsonFprintfJSON(intcode,stringpath,stringjson){}//ResonsehtmlFprintfHTML(intcode,stringpath,stringhtml){}//path(relativepathsmayomitleadingslash)stringQueryPath(stringpath){}stringFormValue(stringpath,stringkey){}classHttpContext{stringpath;JSON(intcode,anyv){stringjson=JSON(v);FprintfJSON(code,path,json);}HTML(intcode,anyv){stringhtml=v;FprintfHTML(code,path,html);}stringqueryPath(){stringp=QueryPath(path);returnp;}stringformValue(stringkey){stringv=FormValue(path,key);returnv;}}//BindroutehttpHandle(stringmethod,stringpath,func(HttpContext)handle){//println("path="+path);HttpContextctx=HttpContext();handle(ctx);}//Runhttpserver.httpRun(stringaddr){}

具体的使用流程:

通过定义一个函数变量实现自己的业务逻辑。注册路由。启动 HTTP 服务。

在自己的 handle中可以通过 HttpContext对象拿到请求上下文,可以获取请求参数以及响应数据。具体使用示例可以参考这份代码。

总结

本次更新比我预期的要顺利一些,因为语法树和编译器已经基本实现完毕,不会怎么改了,现在新增的特性无非就是运行时实现一些语法糖,大部分都是体力劳动;可能是新鲜感带来的兴奋剂效果,大部分时间都是痛并快乐着。

比如这两天主要就是在修复多层 block嵌套时遇到 return语句无法正确返回的 bug,死活折腾了两夜;终于在无数次分析 AST 找到了解决方案,现在想想确实还是相关经验太少。

对这个 Bug 感兴趣的朋友可以点个赞,后面可以分享一下。

下一阶段重点就是将编译信息好好整理,让开发体验更好。之后抽空再把 SQL标准库实现了,这样就能愉快的 CURD了。

最后希望对该项目或者是编译原理感兴趣的朋友可以下载使用,提出宝贵意见,欢迎加我微信交流。

v0.0.8 下载地址:https://github.com/crossoverJie/gscript/releases/tag/v0.0.8

往期推荐

终于实现了一门属于自己的编程语言

一门语言的作用域和函数调用是如何实现的

用 Antlr 重构脚本解释器

用位运算为你的程序加速

几百行代码实现一个 JSON 解析器

点分享

点点赞

点在看

关键词: 编程语言 感兴趣的 属于自己的

相关阅读