回到简体

This commit is contained in:
chai2010
2016-02-15 11:06:34 +08:00
parent 9e878f9944
commit 2b37b23285
177 changed files with 2354 additions and 2354 deletions

View File

@@ -1,6 +1,6 @@
## 12.6. 示例: 解S表
## 12.6. 示例: 解S表
標準庫中encoding/...下每包中提供的Marshal編碼函數都有一個對應的Unmarshal函數用於解碼。例如,我在4.5中看到的,要包含JSON編碼格式的字slice數據解碼爲我們自己的Movie§12.3),我可以這樣做:
标准库中encoding/...下每包中提供的Marshal编码函数都有一个对应的Unmarshal函数用于解码。例如,我在4.5中看到的,要包含JSON编码格式的字slice数据解码为我们自己的Movie§12.3),我可以这样做:
```Go
data := []byte{/* ... */}
@@ -8,13 +8,13 @@ var movie Movie
err := json.Unmarshal(data, &movie)
```
Unmarshal函使用了反射機製類脩改movie量的每個成員,根據輸入的內容爲Movie成員創建對應的map、結構體和slice。
Unmarshal函使用了反射机制类修改movie量的每个成员,根据输入的内容为Movie成员创建对应的map、结构体和slice。
現在讓我們爲S表達式編碼實現一個簡易的Unmarshal類似於前面的json.Unmarshal標準庫函數,對應我們之前實現的sexpr.Marshal函的逆操作。我們必須提醒一下,一個健壯的和通用的實現通常需要比例子更多的代碼,爲了便演示我采用了精簡的實現。我們隻支持S表式有限的子集,同時處理錯誤的方式也比粗暴,代的目的是了演示反射的用法,而不是造一個實用的S表式的解器。
现在让我们为S表达式编码实现一个简易的Unmarshal类似于前面的json.Unmarshal标准库函数,对应我们之前实现的sexpr.Marshal函的逆操作。我们必须提醒一下,一个健壮的和通用的实现通常需要比例子更多的代码,为了便演示我采用了精简的实现。我们只支持S表式有限的子集,同时处理错误的方式也比粗暴,代的目的是了演示反射的用法,而不是造一个实用的S表式的解器。
法分析器lexer使用了標準庫中的text/scanner包將輸入流的字節數據解析爲一個個類似註釋、標識符、字符串面值和字面值之類的標記。輸入掃描器scanner的Scan方法提前描和返下一個記號,對於rune型。大多數記號,比如“(”,對應一個單一rune可表示的Unicode字符但是text/scanner也可以用小的負數表示記號標識符、字符串等由多字符成的記號。調用Scan方法將返迴這些記號的類型,接着調用TokenText方法將返迴記號對應的文本容。
法分析器lexer使用了标准库中的text/scanner包将输入流的字节数据解析为一个个类似注释、标识符、字符串面值和字面值之类的标记。输入扫描器scanner的Scan方法提前描和返下一个记号,对于rune型。大多数记号,比如“(”,对应一个单一rune可表示的Unicode字符但是text/scanner也可以用小的负数表示记号标识符、字符串等由多字符成的记号。调用Scan方法将返回这些记号的类型,接着用TokenText方法将返回记号对应的文本容。
爲每個解析器可能需要多次使用前的記號但是Scan一直向前描,所有我們包裝了一lexer描器輔助類型,用於跟蹤最近由Scan方法返迴的記號
为每个解析器可能需要多次使用前的记号但是Scan一直向前描,所有我们包装了一lexer描器辅助类型,用于跟踪最近由Scan方法返回的记号
<u><i>gopl.io/ch12/sexpr</i></u>
```Go
@@ -34,7 +34,7 @@ func (lex *lexer) consume(want rune) {
}
```
現在讓我們轉到語法解析器。它主要包含兩個功能。第一是read函,用於讀取S表式的當前標記,然後根據S表式的當前標記更新可取地址的reflect.Value對應的變量v。
现在让我们转到语法解析器。它主要包含两个功能。第一是read函,用于读取S表式的当前标记,然后根据S表式的当前标记更新可取地址的reflect.Value对应的变量v。
```Go
func read(lex *lexer, v reflect.Value) {
@@ -67,13 +67,13 @@ func read(lex *lexer, v reflect.Value) {
}
```
的S表式使用標識符區分兩個不同型,結構體成員名和nil值的指。read函數值處理nil型的標識符。遇到scanner.Ident“nil”是使用reflect.Zero函數將變量v設置爲零值。而其它任何型的標識符,我都作爲錯誤處理。面的readList函數將處理結構體的成名。
的S表式使用标识符区分两个不同型,结构体成员名和nil值的指。read函数值处理nil型的标识符。遇到scanner.Ident“nil”是使用reflect.Zero函数将变量v设置为零值。而其它任何型的标识符,我都作为错误处理。面的readList函数将处理结构体的成名。
“(”標記對應一個列表的始。第二個函數readList將一個列表解到一聚合型中map、結構體、slice或數組),具體類型依然於傳入待填充量的型。每次遇到這種情況,循環繼續解析每元素直到遇到於開始標記匹配的結束標記“)”endList函數用於檢測結束標記
“(”标记对应一个列表的始。第二个函数readList将一个列表解到一聚合型中map、结构体、slice或数组),具体类型依然于传入待填充量的型。每次遇到这种情况,循环继续解析每元素直到遇到于开始标记匹配的结束标记“)”endList函数用于检测结束标记
最有趣的部分是遞歸。最簡單的是對數組類型的理。直到遇到“)”結束標記,我使用Index函數來獲取數組每個元素的地址,然後遞歸調用read函數處理。和其它錯誤類似,如果輸入數據導致解器的引用超出了數組的范,解碼器將拋出panic常。slice也采用似方法解析,不同的是我們將爲每個元素建新的量,然後將元素添加到slice的末尾。
最有趣的部分是递归。最简单的是对数组类型的理。直到遇到“)”结束标记,我使用Index函数来获取数组每个元素的地址,然后递归调用read函数处理。和其它错误类似,如果输入数据导致解器的引用超出了数组的范,解码器将抛出panic常。slice也采用似方法解析,不同的是我们将为每个元素建新的量,然后将元素添加到slice的末尾。
在循環處理結構體和map每元素時必須解碼一個(key value)格式的對應子列表。對於結構體key部分對於成員的名字。和數組類似,我使用FieldByName找到結構體對應成員的變量然後遞歸調用read函數處理。對於mapkey可能是任意型,元素的理方式和slice似,我們創建一新的量,然後遞歸填充它,最後將新解析到的key/value添加到map。
在循环处理结构体和map每元素时必须解码一个(key value)格式的对应子列表。对于结构体key部分对于成员的名字。和数组类似,我使用FieldByName找到结构体对应成员的变量然后递归调用read函数处理。对于mapkey可能是任意型,元素的理方式和slice似,我们创建一新的量,然后递归填充它,最后将新解析到的key/value添加到map。
```Go
func readList(lex *lexer, v reflect.Value) {
@@ -130,7 +130,7 @@ func endList(lex *lexer) bool {
}
```
,我們將解析器包裝爲導出的Unmarshal解碼函數,隱藏了一些初始化和清理等邊緣處理。部解析器以panic的方式拋出錯誤但是Unmarshal函數通過在defer語句調用recover函數來捕獲內部panic§5.10),然後返迴一個對panic對應的錯誤信息。
,我们将解析器包装为导出的Unmarshal解码函数,隐藏了一些初始化和清理等边缘处理。部解析器以panic的方式抛出错误但是Unmarshal函数通过在defer语句调用recover函数来捕获内部panic§5.10),然后返回一个对panic对应的错误信息。
```Go
// Unmarshal parses S-expression data and populates the variable
@@ -150,10 +150,10 @@ func Unmarshal(data []byte, out interface{}) (err error) {
}
```
産實現不應該對任何輸入問題都用panic形式告,而且應該報告一些錯誤相關的信息,例如出現錯誤輸入的行和位置等。管如此,我希望通過這個例子展示似encoding/json等包底層代碼的實現思路,以及如何使用反射機製來填充數據結構
产实现不应该对任何输入问题都用panic形式告,而且应该报告一些错误相关的信息,例如出现错误输入的行和位置等。管如此,我希望通过这个例子展示似encoding/json等包底层代码的实现思路,以及如何使用反射机制来填充数据结构
**練習 12.8** sexpr.Unmarshal函和json.Unmarshal一,都要求在解碼前輸入完整的字slice。定義一個和json.Decoder似的sexpr.Decoder型,支持從一個io.Reader流解碼。脩改sexpr.Unmarshal函,使用這個新的類型實現
**练习 12.8** sexpr.Unmarshal函和json.Unmarshal一,都要求在解码前输入完整的字slice。定义一个和json.Decoder似的sexpr.Decoder型,支持从一个io.Reader流解码。修改sexpr.Unmarshal函,使用这个新的类型实现
**練習 12.9** 編寫一個基於標記的API用於解碼S表式,考xml.Decoder7.14)的格。你需要五種類型的標記Symbol、String、Int、StartList和EndList。
**练习 12.9** 编写一个基于标记的API用于解码S表式,考xml.Decoder7.14)的格。你需要五种类型的标记Symbol、String、Int、StartList和EndList。
**練習 12.10** 展sexpr.Unmarshal函,支持布型、浮點數和interface型的解,使用 **練習 12.3** 的方案。(提示:要解接口,你需要name映射到每支持型的reflect.Type。
**练习 12.10** 展sexpr.Unmarshal函,支持布型、浮点数和interface型的解,使用 **练习 12.3** 的方案。(提示:要解接口,你需要name映射到每支持型的reflect.Type。