懶得看文章直接找repo的話點這里
前言
最近想到了文言文編程這個腦洞,正好又看到Haskell里面有Parsec
這個包,然后就決定試一下了orz
所以打算從最簡單的開始入手,也就是將有一定格式的文言文翻譯成Python的代碼,比如像下面這個(插入排序):
有略名 排序 其參名 數(shù)列 其文曰
凡 巡 之于 數(shù)數(shù)自 1 至 求長于 數(shù)列 之中 所得之數(shù) 是也 為
媒 者 數(shù)列 諸 巡 位之數(shù) 也
今 者 巡 減 1 也
復為之,方 今 非小于 0 與 媒 小于 數(shù)列 諸 今 位之數(shù) 乃止
數(shù)列 諸 今 加 1 所得之數(shù) 位之數(shù) 換作 數(shù)列 諸 今 位之數(shù)
今 謫 1 也
數(shù)列 諸 今 加 1 位之數(shù) 換作 媒 也
可以看到整體的樣式是和Python的代碼是一致的,這樣的話可以減少我們寫的解釋器的工作量,但缺點的話,很明顯,并不像自然語言。。。
一開始看上去很簡單
首先我們來看看Text.ParserCombinators.Parsec
這一Package,它可以將一大段文字的每一行和每一個單詞提取出來建成一個二維數(shù)組,這歌其實和
parse :: String -> [[String]]
parse x = map words $ lines x
這樣的一段代碼差不多,不過有一個區(qū)別就是Parsec
里面的parse會把每一個空格也記錄起來,這樣我們家可以寫出這樣的一個函數(shù)將輸入的內(nèi)容分成每一個詞:
wyFile = endBy line eol
line = sepBy cell (char ' ')
cell = many (noneOf " \n")
eol = char '\n'
-- convert to single word
parseTo :: String -> Either ParseError [[String]]
parseTo = parse wyFile "(unknown)"
然后我們要建立一個文言文和Python代碼之間對應的一個表,這個在Words.hs
里面,簡單來說就是一個tuple的數(shù)組而已(像下面這個)
keywords :: [(String, String)]
keywords =
[
("者", "="),
("今乃", "="),
("換作", "="),
("也", " ")
-- etc
]
然后我們要做的就是寫一個將識別到的文言指令替換成Python代碼的函數(shù):
-- replace Wenyan sytax with Python syntax
replace :: (Eq a) => a -> [(a, a)] -> a
replace x ((a, b):ys)
| x == a = b
| x /= a && ys /= [] = replace x ys
| otherwise = x
replaceList :: (Eq a) => [a] -> [(a, a)] -> [a]
replaceList [] _ = []
replaceList xs ys = map (`replace` ys) xs
這段代碼也不難理解,簡單來說就是歷盡里面的每一個詞語然后將符合的詞進行替換。
最后我們只要將這個List轉換回有格式的String再輸出就可以了
-- convert back to normal format
parseBack :: [[String]] -> String
parseBack x = unlines $ map unwords x
但是事實并非如此
因為Python并不支持中文的變量名和函數(shù)名,所以我們還要進一步操作。
首先是變量名, 因為Python定義變量并不需要在前面加上如let
之類的,所以一個折中的辦法就是讓寫代碼的人先在一行提前聲明之后需要用到的中文變量名,也就是所提到的:有參者 <變量1> <變量2> <...>
這個語句。尋找這個語句的方式其實也是歷遍。。。
findVar :: [[String]] -> [String]
findVar (x:xs)
| null x = findVar xs
| head x == varkey = tail x
| otherwise = findVar xs
transVar :: [String] -> [(String,String)]
transVar [] = []
transVar (x:xs) = let l = length xs in (x, "var" ++ show l) : transVar xs
接下來便是將中文的變量名轉換成英文,我選擇的辦法是數(shù)出有多少個變量然后統(tǒng)一以var為開頭命名,后面加上編號。
同樣會遇到問題的就是函數(shù)和類的命名,這里的話方法也是類似的,感興趣的話可以參考一下源代碼這里就不展示了。
到了這里整個程序基本上是寫完了的。
最后翻譯出來長這個樣子:
def fun0 ( var4 ):
for var3 in range( 1 , len( var4 ) ) :
var2 = var4 [ var3 ]
var1 = var3 - 1
while var1 >= 0 and var2 < var4 [ var1 ] :
var4 [ var1 + 1 ] = var4 [ var1 ]
var1 -= 1
var4 [ var1 + 1 ] = var2
結語
總的來說整個程序要的思路其實并不難,加上Haskell的Higher Order Function這一個利器,整個主程序的代碼才80行不到XD
當然這個程序還是存在相當多的不足,像是只是支持很少的函數(shù),以及非常多不符合自然語言習慣的內(nèi)容。不過最麻煩的還是縮進,這個在未來的版本肯定是要去掉的!