初識解釋器

前言

近幾年, 隨着前後端 (或者說整個應用程序開發技術) 的提升. Low-code 概念開始冒出來了. 

Low 的意思是 low level, 也就是懂一些 coding 能力就可以做出很有用的程序. 

這不容易, 只有工業化到一定程度纔會出現這種現象. 比如很多公司現在都可以做手機了. 因爲行業分工細, 你做一個手機類似與組裝一個手機. 

軟件開發也是同理, 只要把各種 SASS 組裝到 PAAS 上, 一個程序就開始貢獻了.

於是我遇到了一些這樣的需求. 用戶想通過簡單的表達式來實現對系統的權限限制. 

比如說, 一個 create invoice 的權限, 但是 invoice amount 必須小於 1000.

以前處理這類需求一般上都是 hardcode. 但最近我接觸比較多表達式樹概念後. 我發現這些需求並不需要 hardcode.

讓用戶輸入一個簡單的表達式, 然後解釋執行就可以做到驗證權限的效果了. 

比如上面這個 case, 表達式就是 'invoice amount' < 1000, 類似與 excel advanced filter 的使用難度. 

有了這樣一個功能, 系統就不需要擔心後續的擴展了. 這個表達式還可以用在許多地方. 比如 notification. 細節我就不多說了. 

 

解析器與編輯器

參考: 

編譯器和解釋器之間有什麼區別

編譯器與解釋器的區別和工作原理

先聊聊什麼是解析器, 和我們日常用到的哪些東西有關聯.

c, c++ 是語言, 寫完以後要怎麼在 OS 裏跑起來呢 (控制 CPU 等)? 

需要一個編輯過程, 針對不同的 OS, CPU 需要編程成不同的可執行物 (機器碼). OS 只認機器碼. 

javascript 是語言, 你寫完以後不需要去編輯它, 直接丟給遊覽器就可以了. 因爲遊覽器負責了把它變成可執行物, 職責轉移了, 這種就叫解析.

所以它們很多時候很難去分辨, 比如對於 Angular 來說, 你寫好了 Angular 是需要有一個編輯過程的, 編輯成 javascript.

不用糾結太多, 我們寫業務的懂個大概就可以了. 

 

解析器

參考: 

如何編寫簡單的parser(基礎篇)

如何編寫簡單的parser

手寫一個詞法分析器

寫一個簡單的解釋器 - 簡介以及詞法分析

Let’s Build A Simple Interpreter. Part 1

如何編寫一個簡單的 parser

解析就是把一個語言, 變成可執行物. 比如 'invoice amount' < 100, 它就可以是一個語言, 任何一個可以表達事物的東西都可以稱作語言 (當然這裏講的是字符串而不是聲音).

解析語言就是把 string 變成可執行物. 我們讀過書的都知道, 一門語言它一定是有詞彙, 語法 (grammer) 這些鼕鼕的, 抽象點說就是它有一些定義, 然後有一些規則. 

‘invoice amount’ < 100 裏面就出現了 symbol, string, number. Symbol 就有它的定義, 它們被一些規則所限制, 比如順序, 分割等等.

因爲有了規則, 才變得可以被解析, 因爲有了語義才知道要怎麼執行.

上面這段想表達的就是判斷 invoice amount (這個屬性值), < 小於, 100 一百塊錢

術語和流程

第一步 – 詞法分析 (lexical analysis)

先把 string 切分出來, 變成一個一個有意義的東西 (它們叫 token), 這個過程叫詞法分析, 做這件事的人叫詞法分析器 (lexer)

它的工作就是一個一個字符串就 read 出來 (通常是配上正則), 然後把它們歸類. 比如 invoice amount 是一個讀屬性的意思, < 是一個操作符, 100 是一個數目

這樣就分成了 3 塊有點意義的東西了, 而不只是一個 string.

第二部 – 語法分析 (syntactic analysis, or parsing)

做這件事的人叫語法分析器 (parser), 有了那些 token 還不足夠執行, 因爲執行順序並不是左到右的. 它還有其它規則, 比如先乘除後加減, 先 and 後 or, 括弧等 參考: Order of operations

於是 parser 需要把 token 變成一個有順序, 可以容易被遍歷的結構. 

這就是大名鼎鼎的 Abstract syntax tree (AST 抽象語法樹)

在之前我寫 C# 反射 & 表達式樹 的時候介紹過 Expression Tree, 它就算是一種 AST.

 

總結

於是...都連起來了.

EF Core

Ef Core 的職責是吧 Expression Tree 變成 SQL query string. 一般上我們都是 hardcode 去寫這些 expression 的.

所以它沒有用到任何解析器的東西. 它是 Expression Tree -> SQL query string

Dynamic LINQ

Dynamic LINQ 的職責是把 string expression 變成 Expression Tree, 然後交由 LINQ 去執行. 它就用到了解析器, string expression to Expression Tree

OData

我介紹過 OData – Query to Expression, 它的職責是把 odata query string 變成 AST (它自己的) 然後在轉成 Expression Tree 讓 EF Core 去執行. 

它也用到了解析器, odata query string -> AST -> Expression Tree -> EF Core -> SQL query string.

OData.NxT 還希望也把反向給做了, Expression Tree -> AST -> odata query string.

一開始我搞不同爲什麼 odata 不把 query string 直接解析成 Expression Tree, 而是搞了一個自己的 AST. 但後來我發現這樣的好處是不依賴 C#。

如果想在其它平臺, 比如 Node.js 去做解釋, 就可以比較容易的 port 過去了.

目前的計劃

本來想用 odata 作爲 string expression, 這樣就可以利用它的解析器. 但後來想想, 這套方案在前端就不起作用了. 想找一個 string expression 符合我的業務需求 (不復雜, 但又有一些些 customize, odata 不支持 addDays 這種操作)

支持 C# 和 Javascript 環境的, 最終沒有找到. 於是纔有了自己寫的念頭, research 完了以後發現, 確實是工程不小啊. 還是再觀察看看吧.

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章