# go-spp **Repository Path**: str/go-spp ## Basic Information - **Project Name**: go-spp - **Description**: Golang 实现的 Spp - **Primary Language**: Go - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 4 - **Forks**: 2 - **Created**: 2017-08-26 - **Last Updated**: 2022-12-25 ## Categories & Tags **Categories**: mathlibs **Tags**: None ## README ## 语言解析的意义 研究算法,要解析语言。而语言的解析,是从设计语法开始的。Spp 语言就是描述语法的语言。 ## Spp 安装 (以 go 为例) Spp 现有 5 种语言的实现,选择一个最熟悉的(Golang 最快): https://gitee.com/str/go-spp https://gitee.com/str/lua-spp https://gitee.com/str/ruby-spp https://gitee.com/str/perl-spp https://gitee.com/str/python-spp 首先确保你的电脑里安装了 Golang 或其他语言的运行环境。 git 目录下有一个 Spp.go 的文件,下载下来, 在命令行里输入: > go run Spp.go This is Spp REPL, type enter to exit. >> 好了,可以开始学习测试了. ## Spp 基本语法 ### 字符串:有两种表示方法 >> Str -> 'abc' ["Str","'abc'"] 大家都看出来了,这似乎是 JSON, 是的,它是 JSON 的 一个子集,但没有 Object, true, false ... >> Str -> "abc" ["String","\"abc\""] >> Str -> :abc ["Kstr",":abc"] 是的,字符串有三种表达方式,主要是为了方便。 单引号包围的字符串中,不能出现单引号,如果出现,就要用就要用转义符号。 >> str -> 'abc\'\\' ["Str","'abc\\'\\\\'"] 同样双引号包围的字符串中,也不能出现双引号。 ### 字符:表示不可见字符 >> Char -> \' ["Char","\\'"] ### 任何字符(any char): >> . ### 字符集,表示一类字符。 \l lowercase 小写字符 [a-z] \u uppercase 大写字符 [A-Z] \d digit 数字 [0-9] \x xdigit 16进制数字 [\da-fA-F] \s space 空白字符 [\t\n\r\b] \v vspace 垂直空格 [\n\r] \h hspace 水平空格 [\b\t] \a alpha 普通字母 [a-zA-Z_] \w words 单词字符 [\a\d-] \t tab 制表符 chr(9) \n Newline 换行 chr(10) \r Creturn 回车 chr(13) ### 重复 rept ? 可有可无, 有一个或者是没有 + 至少有一个条件匹配,无穷多也行 * 没有也行,但多多益善 ### 分组 group: 用于重新定义规则的优先级 (a b c)+ ### 字符类 Char Class: 字符的分支 [abc] [a-fA-F] [^abc] ### blank 可以存在的空格,在规则之间的空格代表: 下面两种规则描述是相同的: a -> b c a -> b\s*c blank 解析的效率非常高,不用担心效率的问题。 Branch 中的空格会被忽略,只是为了分隔不同的 Token. ### Token 命名规则 三种命名规则: #### 捕获规则 (capture Token) 大写字母开始的规则名, 捕获模式会对匹配成功的内容,以规则名为 Key, 加入到数据结构中: Token = 'words' # => ['Token','words'] #### 匹配规则 (match mode) 小写字母开始的规则名,把捕获的文本,推送到之前生成的字符串序列之后, token 名称只是为了重用规则而命名: Token -> name name -> 'words' # => ['Token','words'] #### 丢弃模式 (reject mode) 下划线开始,会自动丢弃匹配的内容,通常处理注释: _comment -> '#'~$ ### 分支 branch: 定义可选的匹配可能,每种可能都能匹配: atom -> |Ntoken Mtoken Ctoken| Ntoken -> \u\a* Mtoken -> \l\a* Ctoken -> '_'\a* 解析规则是由许多规则组合在一起,但总有一个规则的名字叫 door, 这个规则是所有规则的开始. 为何这样设计呢? 由于多条规则会被保存成 Hash, 而大部分语言的 Hash 都没有顺序,为保证能从指定 位置开始查找规则,所以对开始的规则进行了命名: door -> 'string' rule door rule -> "alsostr" 每条规则的回车表示规则结束, 除非在分支内 |...|。 规则的名字可以包含 '-', 但不能以 '-' 开始,以下的规则名都是合法的: [\a\-]+ -a _a a-b A-B ## 关于不可见字符和转义符号的处理 在 Spp 中,字符和字符串不同,\n \r \t 的字符表示,是要用 \\ 转义符前缀, 代码中用如下形式: \n \r \t \\ 但在捕获时,获取的是两个字符,内部 estr 保存时,还原成一个字符。 在 保存成 atom 时,也是原始状态,也就是 opt 时,变成原始字符。 在写成实际代码的时候,再恢复。 door -> |\s+ _comm Spec|+ $ _comm -> '#'~$$ Spec -> Token '->' rule rule -> |Blank Branch atom|+|\v $| Blank -> \h+ atom -> | Group Token Str String Kstr Cclass Char Chclass Assert Any Rept Till | Branch -> '|' | \s+ _comm atom |+ '|' Group -> '{' | \s+ _comm Branch atom |+ '}' Token -> [\a\-]+ Kstr -> ':'[\a\-]+ Str -> \'| [^\\']+ {\\.} |+\' String -> \"| [^\\"]+ {\\.} |+\" Cclass -> \\[adhlsuvwxADHLSUVWX] Char -> \\. Chclass -> \[ Flip?|\s Cclass Char Range Cchar|+ \] Flip -> '^' Range -> \w\-\w Cchar -> [^ \s \] \# \\ ] Assert -> | '^^' '$$' '^' '$' | Any -> '.' Rept -> [?*+] Till -> '~' (end)