用文言文写Python
懒得看文章直接找repo的话点这里
前言
最近想到了文言文编程这个脑洞,正好又看到Haskell里面有Parsec
这个包,然后就决定试一下了orz
所以打算从最简单的开始入手,也就是将有一定格式的文言文翻译成Python的代码,比如像下面这个(插入排序):
有略名 排序 其参名 数列 其文曰
凡 巡 之于 数数自 1 至 求长于 数列 之中 所得之数 是也 为
媒 者 数列 诸 巡 位之数 也
今 者 巡 减 1 也
复为之,方 今 非小于 0 与 媒 小于 数列 诸 今 位之数 乃止
数列 诸 今 加 1 所得之数 位之数 换作 数列 诸 今 位之数
今 谪 1 也
数列 诸 今 加 1 位之数 换作 媒 也
可以看到整体的样式是和Python的代码是一致的,这样的话可以减少我们写的解释器的工作量,但缺点的话,很明显,并不像自然语言。。。
一开始看上去很简单
首先我们来看看Text.ParserCombinators.Parsec
这一Package,它可以将一大段文字的每一行和每一个单词提取出来建成一个二维数组,这歌其实和
parse :: String -> [[String]]
parse x = map words $ lines x
这样的一段代码差不多,不过有一个区别就是Parsec
里面的parse会把每一个空格也记录起来,这样我们家可以写出这样的一个函数将输入的内容分成每一个词:
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的数组而已(像下面这个)
keywords :: [(String, String)]
keywords =
[
("者", "="),
("今乃", "="),
("换作", "="),
("也", " ")
-- etc
]
然后我们要做的就是写一个将识别到的文言指令替换成Python代码的函数:
-- 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并不支持中文的变量名和函数名,所以我们还要进一步操作。
首先是变量名, 因为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
接下来便是将中文的变量名转换成英文,我选择的办法是数出有多少个变量然后统一以var为开头命名,后面加上编号。
同样会遇到问题的就是函数和类的命名,这里的话方法也是类似的,感兴趣的话可以参考一下源代码这里就不展示了。
到了这里整个程序基本上是写完了的。
最后翻译出来长这个样子:
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
当然这个程序还是存在相当多的不足,像是只是支持很少的函数,以及非常多不符合自然语言习惯的内容。不过最麻烦的还是缩进,这个在未来的版本肯定是要去掉的!