里程碑!用自己的編程語言實(shí)現(xiàn)了一個(gè)網(wǎng)站

前言
在上一篇《終于實(shí)現(xiàn)了一門屬于自己的編程語言》 介紹了自己寫的編程語言 GScript ,在文中提到希望最終可以使用 GScript 開發(fā)一個(gè)網(wǎng)站。
到目前為止確實(shí)是做到了,首頁地址:
https://gscript.crossoverjie.top/index

要稱為一個(gè)網(wǎng)站確實(shí)有點(diǎn)勉強(qiáng),不過也是一個(gè)動(dòng)態(tài)網(wǎng)頁,因?yàn)榉祷氐氖?HTML,所以在當(dāng)前階段只要不嫌麻煩其實(shí)也能寫一個(gè)“合格”的網(wǎng)站,有點(diǎn)像以前我們學(xué)習(xí) Java 時(shí)的 servlet。
該頁面的源碼地址在這里:https://github.com/crossoverjie/gscript-homepage
其實(shí)總共也就40來行代碼:
class GScript{
string author;
string[] features;
string since;
GScript(string a, string[] f, string s){
author = a;
features = f;
since = s;
}
}
func (HttpContext) index(HttpContext ctx){
string[] features = {"statically", "strongly"};
GScript gs = GScript("crossoverJie",features, "2022");
string j = JSON(gs);
println(j);
string local = getCurrentTime("Asia/Shanghai","2006-01-02 15:04:05");
println("local=" + local);
string html = ^
<html>
<title>GScript</title>
<pre>
_ _
___ ___ ___ ___|_|___| |_
| . |_ -| _| _| | . | _|
|_ |___|___|_| |_| _|_|
|___| |_| v0.0.7
^+ j +^
</pre>
<h1>current ^+ local +^</h1>
<p><a href="https://github.com/crossoverjie/gscript-homepage">GScript-homepace source code</a></p>
</html>
^;
ctx.HTML(200, html);
}
httpHandle("GET", "/index", index);
string[] args = getOSArgs();
if (len(args) ==3){
httpRun(":" + args[2]);
}else {
httpRun(":8000");
}
全是利用 GScript 所提供的標(biāo)準(zhǔn)庫實(shí)現(xiàn)的,后文會(huì)詳細(xì)聊聊內(nèi)置 HTTP 包。
更新內(nèi)容
下面重點(diǎn)來看看 v0.0.8 這個(gè)版本相較于上一個(gè)更新了哪些地方。
因?yàn)槲沂前炎约寒?dāng)做一個(gè)開發(fā)者的角度去實(shí)現(xiàn)了一個(gè) http 服務(wù),同時(shí)還用 GScript 刷了兩道簡單的 LeetCode;為了讓這個(gè)過程更流暢,更符合一個(gè)現(xiàn)代語言的使用方式,所以本次真的更新不少東西。
刷題源碼:https://github.com/crossoverJie/gscript/tree/main/example/leetcode
大概如下:
any類型的支持,簡化標(biāo)準(zhǔn)庫的實(shí)現(xiàn)。可以用 ^^來聲明多行字符串,方便聲明復(fù)雜字符串。更完善的類型推導(dǎo),修復(fù)了上個(gè)版本中某些情況推導(dǎo)不出類型的bug。 支持運(yùn)算符重載。 基本的 http 包,可以開發(fā)出 http 服務(wù),目前能響應(yīng) JSON以及HTML。新增內(nèi)置函數(shù):根據(jù)時(shí)區(qū)獲取當(dāng)前時(shí)間、獲取應(yīng)用啟動(dòng)參數(shù)等。 JSON的序列表以及查詢,語法級(jí)適配了 XJSON。修復(fù)了在多個(gè) block嵌套情況下不能正確return的 bug。
其實(shí)從這些更新中也能看出,上個(gè)版本只是一個(gè)簡單能用的狀態(tài),而現(xiàn)在這個(gè)版本已經(jīng)可以拿來寫復(fù)雜邏輯了,當(dāng)然目前還缺乏一些更友好的編譯提示以及運(yùn)行時(shí)錯(cuò)誤。
下面仔細(xì)聊聊一些更新內(nèi)容。
any 類型
首先是 any 通用類型,這個(gè)類似于 Java 中的 Object 和 Go 中的 interface{},極大的方便了我們編寫一些標(biāo)準(zhǔn)庫。

以之前內(nèi)置的 hash 和 len 函數(shù)為例,需要對(duì)每種類型都實(shí)現(xiàn)一遍,非常麻煩而且毫無必要;現(xiàn)在只需要定義一次即可,代碼量直接省幾倍。
同理,之前實(shí)現(xiàn)的 Map 只支持存放 string 類型,現(xiàn)在便能存放任何類型的數(shù)據(jù)。
對(duì) any 的實(shí)現(xiàn)過程感興趣的朋友,今后可以單獨(dú)分享一下。
運(yùn)算符重載
寫 go 或者是 Java 的朋友應(yīng)該知道,這兩門語言都無法對(duì)兩個(gè)對(duì)象進(jìn)行運(yùn)算,編譯器會(huì)直接報(bào)錯(cuò)。
但在一些特殊場景下還是蠻好用的,于是我參考了 C# 的語法在 GScript 中也實(shí)現(xiàn)了。
class Person{
int age;
Person(int a){
age = a;
}
}
Person operator + (Person p1, Person p2){
Person pp = Person(p1.age+p2.age);
return pp;
}
Person operator - (Person p1, Person p2){
Person pp = Person(p1.age-p2.age);
return pp;
}
Person p1 = Person(10);
Person p2 = Person(20);
Person p3 = p1+p2;
println("p3.age="+p3.age);
assertEqual(p3.age, 30);
聲明的函數(shù)名稱必須為 operator,之后跟上運(yùn)算符便實(shí)現(xiàn)了重載。
支持的運(yùn)算符有:+-*/ < >= <= > ==。
JSON支持
當(dāng)前版本中支持將對(duì)象、基本類型進(jìn)行序列化,暫不支持反序列化為對(duì)象,但可以根據(jù) JSON 字符串通過一定的語法查詢數(shù)據(jù)。
內(nèi)置了兩個(gè) JSON 相關(guān)函數(shù):
// return JSON string
string JSON(any a){}
// JSON query with path
any JSONGet(string json, string path){}
class Person{
int age;
string name;
float weight;
bool man;
Person(string n, int a, float w, bool m){
name = n;
age = a;
weight = w;
man =m;
}
}
Person p1 = Person("abc",10,99.99,true);
Person p2 = Person("a",11,999.99,false);
string json = JSON(p1);
println(json);
// output:{"age":10,"man":true,"name":"abc","weight":99.99}
以這段代碼為例,調(diào)用 JSON 函數(shù)可以將對(duì)象序列化為 JSON 字符串。
class Person{
int age;
string name;
float weight;
bool man;
Person(string n, int a, float w, bool m){
name = n;
age = a;
weight = w;
man =m;
}
}
Person p1 = Person("abc",10,99.99,true);
string json = JSON(p1);
println(json);
int age = JSONGet(json, "age");
println(age);
assertEqual(age,10);
使用 JSONGet 函數(shù)可以在一個(gè) JSON 字符串中查詢?nèi)我獾臄?shù)據(jù),這個(gè)功能是通過適配 XJSON 實(shí)現(xiàn)的,所以 XJSON 支持的查詢語法都能實(shí)現(xiàn)。
string j=^{"age":10, "abc":{"def":"def"},"list":[1,2,3]}^;
String def = JSONGet(j, "abc.def");
println(def);
assertEqual(def,"def");
int l1 = JSONGet(j, "list[0]");
println(l1);
assertEqual(l1,1);
string str=^
{
"name": "bob",
"age": 20,
"skill": {
"lang": [
{
"go": {
"feature": [
"goroutine",
"channel",
"simple",
true
]
}
}
]
}
}
^;
String g = JSONGet(str, "skill.lang[0].go.feature[0]");
println(g);
assertEqual(g,"goroutine");
比如這樣復(fù)雜的嵌套 JSON,也能通過查詢語法獲取數(shù)據(jù)。
HTTP 包
HTTP 包是本次升級(jí)的重點(diǎn),標(biāo)準(zhǔn)庫中提供了以下函數(shù)和類:
// http lib
// Response json
FprintfJSON(int code, string path, string json){}
// Resonse html
FprintfHTML(int code, string path, string html){}
// path (relative paths may omit leading slash)
string QueryPath(string path){}
string FormValue(string path, string key){}
class HttpContext{
string path;
JSON(int code, any v){
string json = JSON(v);
FprintfJSON(code, path, json);
}
HTML(int code, any v) {
string html = v;
FprintfHTML(code, path, html);
}
string queryPath() {
string p = QueryPath(path);
return p;
}
string formValue(string key){
string v = FormValue(path, key);
return v;
}
}
// Bind route
httpHandle(string method, string path, func (HttpContext) handle){
// println("path="+path);
HttpContext ctx = HttpContext();
handle(ctx);
}
// Run http server.
httpRun(string addr){}
具體的使用流程:
通過定義一個(gè)函數(shù)變量實(shí)現(xiàn)自己的業(yè)務(wù)邏輯。 注冊(cè)路由。 啟動(dòng) HTTP 服務(wù)。
在自己的 handle 中可以通過 HttpContext 對(duì)象拿到請(qǐng)求上下文,可以獲取請(qǐng)求參數(shù)以及響應(yīng)數(shù)據(jù)。具體使用示例可以參考這份代碼。
總結(jié)
本次更新比我預(yù)期的要順利一些,因?yàn)檎Z法樹和編譯器已經(jīng)基本實(shí)現(xiàn)完畢,不會(huì)怎么改了,現(xiàn)在新增的特性無非就是運(yùn)行時(shí)實(shí)現(xiàn)一些語法糖,大部分都是體力勞動(dòng);可能是新鮮感帶來的興奮劑效果,大部分時(shí)間都是痛并快樂著。
比如這兩天主要就是在修復(fù)多層 block 嵌套時(shí)遇到 return 語句無法正確返回的 bug,死活折騰了兩夜;終于在無數(shù)次分析 AST 找到了解決方案,現(xiàn)在想想確實(shí)還是相關(guān)經(jīng)驗(yàn)太少。
對(duì)這個(gè) Bug 感興趣的朋友可以點(diǎn)個(gè)贊,后面可以分享一下。
下一階段重點(diǎn)就是將編譯信息好好整理,讓開發(fā)體驗(yàn)更好。之后抽空再把 SQL 標(biāo)準(zhǔn)庫實(shí)現(xiàn)了,這樣就能愉快的 CURD了。
最后希望對(duì)該項(xiàng)目或者是編譯原理感興趣的朋友可以下載使用,提出寶貴意見,歡迎加我微信交流。
v0.0.8 下載地址:https://github.com/crossoverJie/gscript/releases/tag/v0.0.8
往期推薦

點(diǎn)分享

點(diǎn)點(diǎn)贊

點(diǎn)在看
