世界今日讯!里程碑!用自己的编程语言实现了一个网站
在上一篇《终于实现了一门属于自己的编程语言》 介绍了自己写的编程语言 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+^
^;ctx.HTML(200,html);}httpHandle("GET","/index",index);string[]args=getOSArgs();if(len(args)==3){httpRun(":"+args[2]);}else{httpRun(":8000");}
GScript-homepacesourcecode
全是利用 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 解析器
点分享
点点赞
点在看
相关阅读
-
世界热推荐:今晚7:00直播丨下一个突破...
今晚19:00,Cocos视频号直播马上点击【预约】啦↓↓↓在运营了三年... -
NFT周刊|Magic Eden宣布支持Polygon网...
Block-986在NFT这样的市场,每周都会有相当多项目起起伏伏。在过去... -
环球今亮点!头条观察 | DeFi的兴衰与...
在比特币得到机构关注之后,许多财务专家预测世界将因为加密货币的... -
重新审视合作,体育Crypto的可靠关系才能双赢
Block-987即使在体育Crypto领域,人们的目光仍然集中在FTX上。随着... -
简讯:前端单元测试,更进一步
前端测试@2022如果从2014年Jest的第一个版本发布开始计算,前端开发... -
焦点热讯:刘强东这波操作秀
近日,刘强东发布京东全员信,信中提到:自2023年1月1日起,逐步为...