精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

從Java走進Scala:構建計算器 解析器組合子入門

開發 后端
本文繼續討論一個簡單的計算器 DSL,以展示函數性語言在構建“外部”DSL 的強大功能,并在此過程中解決將文本輸入轉換成用于解釋的 AST 的問題。為了解析文本輸入,作者引入了 解析器組合子(parser combinator),這是一個專門為這項任務設計的標準 Scala 庫。

回憶一下我們的英雄所處的困境:在試圖創建一個 DSL(這里只不過是一種非常簡單的計算器語言)時,他創建了包含可用于該語言的各種選項的樹結構:

◆二進制加/減/乘/除運算符

◆一元反運算符

◆數值

它背后的執行引擎知道如何執行那些操作,它甚至有一個顯式的優化步驟,以減少獲得結果所需的計算。

最后的 代碼 是這樣的:

清單 1. 計算器 DSL:AST 和解釋器

  1. package com.tedneward.calcdsl  
  2. {  
  3.   private[calcdsl] abstract class Expr  
  4.   private[calcdsl]  case class Variable(name : String) extends Expr  
  5.   private[calcdsl]  case class Number(value : Double) extends Expr  
  6.   private[calcdsl]  case class UnaryOp(operator : String, arg : Expr) extends Expr  
  7.   private[calcdsl]  case class BinaryOp(operator : String, left : Expr, right : Expr)   
  8.    extends Expr  
  9.  
  10.   object Calc  
  11.   {  
  12.     /**  
  13.      * Function to simplify (a la mathematic terms) expressions  
  14.      */ 
  15.     def simplify(e : Expr) : Expr =  
  16.     {  
  17.       e match {  
  18.         // Double negation returns the original value  
  19.         case UnaryOp("-", UnaryOp("-", x)) => simplify(x)  
  20.     
  21.         // Positive returns the original value  
  22.         case UnaryOp("+", x) => simplify(x)  
  23.     
  24.         // Multiplying x by 1 returns the original value  
  25.         case BinaryOp("*", x, Number(1)) => simplify(x)  
  26.     
  27.         // Multiplying 1 by x returns the original value  
  28.         case BinaryOp("*", Number(1), x) => simplify(x)  
  29.     
  30.         // Multiplying x by 0 returns zero  
  31.         case BinaryOp("*", x, Number(0)) => Number(0)  
  32.     
  33.         // Multiplying 0 by x returns zero  
  34.         case BinaryOp("*", Number(0), x) => Number(0)  
  35.     
  36.         // Dividing x by 1 returns the original value  
  37.         case BinaryOp("/", x, Number(1)) => simplify(x)  
  38.     
  39.         // Dividing x by x returns 1  
  40.         case BinaryOp("/", x1, x2) if x1 == x2 => Number(1)  
  41.     
  42.         // Adding x to 0 returns the original value  
  43.         case BinaryOp("+", x, Number(0)) => simplify(x)  
  44.     
  45.         // Adding 0 to x returns the original value  
  46.         case BinaryOp("+", Number(0), x) => simplify(x)  
  47.     
  48.         // Anything else cannot (yet) be simplified  
  49.         case _ => e  
  50.       }  
  51.     }  
  52.       
  53.     def evaluate(e : Expr) : Double =  
  54.     {  
  55.       simplify(e) match {  
  56.         case Number(x) => x  
  57.         case UnaryOp("-", x) => -(evaluate(x))  
  58.         case BinaryOp("+", x1, x2) => (evaluate(x1) + evaluate(x2))  
  59.         case BinaryOp("-", x1, x2) => (evaluate(x1) - evaluate(x2))  
  60.         case BinaryOp("*", x1, x2) => (evaluate(x1) * evaluate(x2))  
  61.         case BinaryOp("/", x1, x2) => (evaluate(x1) / evaluate(x2))  
  62.       }  
  63.     }  
  64.   }  
  65. }  

#p#

前一篇文章的讀者應該還記得,我布置了一個挑戰任務,要求改進優化步驟,進一步在樹中進行簡化處理,而不是像清單 1 中的代碼那樣停留在最頂層。Lex Spoon 發現了我認為是最簡單的優化方法:首先簡化樹的 “邊緣”(每個表達式中的操作數,如果有的話),然后利用簡化的結果,再進一步簡化頂層的表達式,如清單 2 所示:

清單 2. 簡化、再簡化

  1. /*  
  2.  * Lex's version:  
  3.  */ 
  4. def simplify(e: Expr): Expr = {  
  5.   // first simplify the subexpressions  
  6.   val simpSubs = e match {  
  7.     // Ask each side to simplify  
  8.     case BinaryOp(op, left, right) => BinaryOp(op, simplify(left), simplify(right))  
  9.     // Ask the operand to simplify  
  10.     case UnaryOp(op, operand) => UnaryOp(op, simplify(operand))  
  11.     // Anything else doesn't have complexity (no operands to simplify)  
  12.     case _ => e  
  13.   }  
  14.  
  15.   // now simplify at the top, assuming the components are already simplified  
  16.   def simplifyTop(x: Expr) = x match {  
  17.     // Double negation returns the original value  
  18.     case UnaryOp("-", UnaryOp("-", x)) => x  
  19.  
  20.     // Positive returns the original value  
  21.     case UnaryOp("+", x) => x  
  22.  
  23.     // Multiplying x by 1 returns the original value  
  24.     case BinaryOp("*", x, Number(1)) => x  
  25.  
  26.     // Multiplying 1 by x returns the original value  
  27.     case BinaryOp("*", Number(1), x) => x  
  28.  
  29.     // Multiplying x by 0 returns zero  
  30.     case BinaryOp("*", x, Number(0)) => Number(0)  
  31.  
  32.     // Multiplying 0 by x returns zero  
  33.     case BinaryOp("*", Number(0), x) => Number(0)  
  34.  
  35.     // Dividing x by 1 returns the original value  
  36.     case BinaryOp("/", x, Number(1)) => x  
  37.  
  38.     // Dividing x by x returns 1  
  39.     case BinaryOp("/", x1, x2) if x1 == x2 => Number(1)  
  40.  
  41.     // Adding x to 0 returns the original value  
  42.     case BinaryOp("+", x, Number(0)) => x  
  43.  
  44.     // Adding 0 to x returns the original value  
  45.     case BinaryOp("+", Number(0), x) => x  
  46.  
  47.     // Anything else cannot (yet) be simplified  
  48.     case e => e  
  49.   }  
  50.   simplifyTop(simpSubs)  
  51. }  

在此對 Lex 表示感謝。

#p#

解析

現在是構建 DSL 的另一半工作:我們需要構建一段代碼,它可以接收某種文本輸入并將其轉換成一個 AST。這個過程更正式的稱呼是解析(parsing)(更準確地說,是標記解釋(tokenizing)、詞法解析(lexing) 和語法解析)。

以往,創建解析器有兩種方法:

手工構建一個解析器。

通過工具生成解析器。

我們可以試著手工構建這個解析器,方法是手動地從輸入流中取出一個字符,檢查該字符,然后根據該字符以及在它之前的其他字符(有時還要根據在它之后的字符)采取某種行動。對于較小型的語言,手工構建解析器可能更快速、更容易,但是當語言變得更龐大時,這就成了一個困難的問題。

除了手工編寫解析器外,另一種方法是用工具生成解析器。以前有 2 個工具可以實現這個目的,它們被親切地稱作lex(因為它生成一個 “詞法解析器”)和 yacc(“Yet Another Compiler Compiler”)。對編寫解析器感興趣的程序員沒有手工編寫解析器,而是編寫一個不同的源文件,以此作為 “lex” 的輸入,后者生成解析器的前端。然后,生成的代碼會與一個 “grammar” 文件 —— 它定義語言的基本語法規則(哪些標記中是關鍵字,哪里可以出現代碼塊,等等)—— 組合在一起,并且輸入到 yacc 生成解析器代碼。

由于這是 Computer Science 101 教科書,所以我不會詳細討論有限狀態自動機(finite state automata)、LALR 或 LR 解析器,如果需要深入了解請查找與這個主題相關的書籍或文章。

同時,我們來探索 Scala 構建解析器的第 3 個選項:解析器組合子(parser combinators),它完全是從 Scala 的函數性方面構建的。解析器組合子使我們可以將語言的各種片段 “組合” 成部件,這些部件可以提供不需要代碼生成,而且看上去像是一種語言規范的解決方案。

解析器組合子

了解 Becker-Naur Form(BNF)有助于理解解析器組合子的要點。BNF 是一種指定語言的外觀的方法。例如,我們的計算器語言可以用清單 3 中的 BNF 語法進行描述:

清單 3. 對語言進行描述

  1. input ::= ws expr ws eoi;  
  2.  
  3. expr ::= ws powterm [{ws '^' ws powterm}];  
  4. powterm ::= ws factor [{ws ('*'|'/') ws factor}];  
  5. factor ::= ws term [{ws ('+'|'-') ws term}];  
  6. term ::= '(' ws expr ws ')' | '-' ws expr | number;  
  7.  
  8. number ::= {dgt} ['.' {dgt}] [('e'|'E') ['-'] {dgt}];  
  9. dgt ::= '0'|'1'|'2'|'3'|'4'|'5'|'6'|'7'|'8'|'9';  
  10. ws ::= [{' '|'\t'|'\n'|'\r'}];  

語句左邊的每個元素是可能的輸入的集合的名稱。右邊的元素也稱為 term,它們是一系列表達式或文字字符,按照可選或必選的方式進行組合。(同樣,BNF 語法在 Aho/Lam/Sethi/Ullman 等書籍中有更詳細的描述,請參閱 參考資料)。

用 BNF 形式來表達語言的強大之處在于,BNF 和 Scala 解析器組合子不相上下;清單 4 顯示使用 BNF 簡化形式后的清單 3:

清單 4. 簡化、再簡化

  1. expr   ::= term {'+' term | '-' term}  
  2. term   ::= factor {'*' factor | '/' factor}  
  3. factor ::= floatingPointNumber | '(' expr ')' 

其中花括號({})表明內容可能重復(0 次或多次),豎線(|)表明也/或的關系。因此,在讀清單 4 時,一個 factor 可能是一個 floatingPointNumber(其定義在此沒有給出),或者一個左括號加上一個 expr 再加上一個右括號。

在這里,將它轉換成一個 Scala 解析器非常簡單,如清單 5 所示:

清單 5. 從 BNF 到 parsec

  1. package com.tedneward.calcdsl  
  2. {  
  3.   object Calc  
  4.   {  
  5.     // ...  
  6.     
  7.     import scala.util.parsing.combinator._  
  8.     
  9.     object ArithParser extends JavaTokenParsers  
  10.     {  
  11.       def expr: Parser[Any] = term ~ rep("+"~term | "-"~term)  
  12.       def term : Parser[Any] = factor ~ rep("*"~factor | "/"~factor)  
  13.       def factor : Parser[Any] = floatingPointNumber | "("~expr~")"   
  14.         
  15.       def parse(text : String) =  
  16.       {  
  17.         parseAll(expr, text)  
  18.       }  
  19.     }  
  20.  
  21.     def parse(text : String) =  
  22.     {  
  23.       val results = ArithParser.parse(text)  
  24.       System.out.println("parsed " + text + " as " + results + " which is a type " 
  25.        + results.getClass())  
  26.     }  
  27.    
  28.  // ...  
  29.   }  
  30. }  

BNF 實際上被一些解析器組合子語法元素替換:空格被替換為 ~ 方法(表明一個序列),重復被替換為 rep 方法,而選擇則仍然用 | 方法來表示。文字字符串是標準的文字字符串。

從兩個方面可以看到這種方法的強大之處。首先,該解析器擴展 Scala 提供的 JavaTokenParsers 基類(后者本身又繼承其他基類,如果我們想要一種與 Java 語言的語法概念不那么嚴格對齊的語言的話),其次,使用 floatingPointNumber 預設的組合子來處理解析一個浮點數的細節。

這種特定的(一個中綴計算器的)語法很容易使用(這也是在那么多演示稿和文章中看到它的原因),為它手工構建一個解析器也不困難,因為 BNF 語法與構建解析器的代碼之間的緊密關系使我們可以更快、更容易地構建解析器。

#p#

解析器組合子概念入門

為了理解其中的原理,我們必須簡要了解解析器組合子的實現。實際上,每個 “解析器” 都是一個函數或一個 case 類,它接收某種輸入,并產生一個 “解析器”。例如,在最底層,解析器組合子位于一些簡單的解析器之上,這些解析器以某種輸入讀取元素(一個 Reader)作為輸入,并生成某種可以提供更高級的語義的東西(一個 Parser):

清單 6. 一個基本的解析器

  1. type Elem  
  2.  
  3. type Input = Reader[Elem]  
  4.  
  5. type Parser[T] = Input => ParseResult[T]  
  6.  
  7. sealed abstract class ParseResult[+T]  
  8. case class Success[T](result: T, in: Input) extends ParseResult[T]  
  9. case class Failure(msg: String, in: Input) extends ParseResult[Nothing]  

換句話說,Elem 是一種抽象類型,用于表示任何可被解析的東西,最常見的是一個文本字符串或流。然后,Input 是圍繞那種類型的一個 scala.util.parsing.input.Reader(方括號表明 Reader 是一個泛型;如果您喜歡 Java 或 C++ 風格的語法,那么將它們看作尖括號)。然后,T 類型的 Parser 是這樣的類型:它接受一個 Input,并生成一個 ParseResult,后者(基本上)屬于兩種類型之一:Success 或 Failure。

顯然,關于解析器組合子庫的知識遠不止這些 — 即使 ~ 和 rep 函數也不是幾個步驟就可以得到的 — 但是,這讓您對解析器組合子的工作原理有基本的了解。“組合” 解析器可以提供解析概念的越來越高級的抽象(因此稱為 “解析器組合子”;組合在一起的元素提供解析行為)。

我們還沒有完成,是嗎?

我們仍然沒有完成。通過調用快速測試解析器可以發現,解析器返回的內容并不是計算器系統需要的剩余部分:

清單 7. 第一次測試失敗?

  1. package com.tedneward.calcdsl.test  
  2. {  
  3.   class CalcTest  
  4.   {  
  5.     import org.junit._, Assert._  
  6.    
  7.  // ...  
  8.       
  9.     @Test def parseNumber =  
  10.     {  
  11.       assertEquals(Number(5), Calc.parse("5"))  
  12.       assertEquals(Number(5), Calc.parse("5.0"))  
  13.     }  
  14.   }  

這次測試會在運行時失敗,因為解析器的 parseAll 方法不會返回我們的 case 類 Number(這是有道理的,因為我們沒有在解析器中建立 case 類與解析器的產生規則之間的關系);它也沒有返回一個文本標記或整數的集合。

相反,解析器返回一個 Parsers.ParseResult,這是一個 Parsers.Success 實例(其中有我們想要的結果);或者一個 Parsers.NoSuccess、Parsers.Failure 或 Parsers.Error(后三者的性質是一樣的:解析由于某種原因未能正常完成)。

假設這是一次成功的解析,要得到實際結果,必須通過 ParseResult 上的 get 方法來提取結果。這意味著必須稍微調整 Calc.parse 方法,以便通過測試。如清單 8 所示:

清單 8. 從 BNF 到 parsec

  1. package com.tedneward.calcdsl  
  2. {  
  3.   object Calc  
  4.   {  
  5.     // ...  
  6.     
  7.     import scala.util.parsing.combinator._  
  8.     
  9.     object ArithParser extends JavaTokenParsers  
  10.     {  
  11.       def expr: Parser[Any] = term ~ rep("+"~term | "-"~term)  
  12.       def term : Parser[Any] = factor ~ rep("*"~factor | "/"~factor)  
  13.       def factor : Parser[Any] = floatingPointNumber | "("~expr~")"   
  14.         
  15.       def parse(text : String) =  
  16.       {  
  17.         parseAll(expr, text)  
  18.       }  
  19.     }  
  20.  
  21.     def parse(text : String) =  
  22.     {  
  23.       val results = ArithParser.parse(text)  
  24.       System.out.println("parsed " + text + " as " + results + " which is a type " 
  25.          + results.getClass())  
  26.    results.get  
  27.     }  
  28.    
  29.  // ...  
  30.   }  
  31. }  

成功了!真的嗎?

對不起,還沒有成功。運行測試表明,解析器的結果仍不是我前面創建的 AST 類型(expr 和它的親屬),而是由 List 和 String 等組成的一種形式。雖然可以將這些結果解析成 expr 實例并對其進行解釋,但是肯定還有另外一種方法。

確實有另外一種方法。為了理解這種方法的工作原理,您將需要研究一下解析器組合子是如何產生非 “標準” 的元素的(即不是 String 和 List)。用適當的術語來說就是解析器如何才能產生一個定制的元素(在這里,就是 AST 對象)。這個主題下一次再討論。

在下一期中,我將和您一起探討解析器組合子實現的基礎,并展示如何將文本片段解析成一個 AST,以便進行求值(然后進行編譯)。

結束語

顯然,我們還沒有結束(解析工作還沒有完成),但是現在有了基本的解析器語義,接下來只需通過擴展解析器產生元素來生成 AST 元素。

對于那些想領先一步的讀者,可以查看 ScalaDocs 中描述的 ^^ 方法,或者閱讀 Programming in Scala 中關于解析器組合子的小節;但是,在此提醒一下,這門語言比這些參考資料中給出的例子要復雜一些。

當然,您可以只與 String 和 List 打交道,而忽略 AST 部分,拆開返回的 String 和 List,并重新將它們解析成 AST 元素。但是,解析器組合子庫已經包含很多這樣的內容,沒有必要再重復一遍。

【相關閱讀】

  1. Scala編程語言專題
  2. 從Java走進Scala:簡單的計算器 case類和模式匹配
  3. 從Java走進Scala:包和訪問修飾符
  4. 從Java走進Scala:使用元組、數組和列表
  5. 從Java走進Scala:當繼承中的對象遇到函數
責任編輯:yangsai 來源: IBMDW
相關推薦

2009-06-19 13:16:36

Scala計算器解析器組合子

2009-06-19 11:13:47

Scalacase類模式匹配

2009-09-28 11:01:39

從Java走進Scal

2009-08-21 16:17:25

ScalaTwitter API

2009-06-17 11:44:22

Scala控制結構

2009-07-15 10:14:25

Scala并發性

2009-12-09 09:15:47

從Java走進ScalTwitter API

2009-02-04 17:32:03

ibmdwJavaScala

2019-07-05 08:39:39

GoSQL解析器

2020-12-02 10:13:45

JacksonJDK解析器

2011-09-16 14:13:15

Windows7計算器

2009-01-03 14:39:00

ibmdwSpirit

2009-10-14 11:14:38

ScitterScalaTwitter

2009-06-16 17:54:38

Scala類語法語義

2009-03-19 09:26:05

RSS解析器MagpieRSS

2009-06-17 13:57:25

Scala元組數組

2009-06-16 17:09:17

Scala面向對象函數編程

2022-09-09 00:25:48

Python工具安全

2022-09-08 11:35:45

Python表達式函數

2010-02-22 16:51:03

Python 解析器
點贊
收藏

51CTO技術棧公眾號

久久精品伊人| 清纯唯美激情亚洲| 中文字幕精品—区二区四季| 成人激情黄色网| 青青草成人免费| 偷窥自拍亚洲色图精选| 欧美色视频在线观看| 国产1区2区3区中文字幕| 亚洲欧洲综合在线| 极品尤物av久久免费看| 97在线视频免费播放| 99久久99久久精品免费看小说.| 日韩激情精品| 欧美影视一区在线| 青春草国产视频| 日本亚洲精品| 91丨porny丨蝌蚪视频| 成人有码在线播放| 免费观看日批视频| 精品96久久久久久中文字幕无| 伊人成人开心激情综合网| 香蕉视频1024| 久久人体av| 欧美视频中文在线看| av动漫在线播放| 在线观看免费黄色| 久久蜜桃av一区二区天堂| 国产精品久久精品视| 国产女人18毛片水18精| 奇米精品一区二区三区在线观看一| 九九热这里只有精品6| 婷婷丁香综合网| 亚洲亚洲免费| 亚洲激情视频在线| 99久久久无码国产精品性波多| 欧美日产一区二区三区在线观看| 粉嫩av一区二区三区| 日本黄色免费在线| 51精品免费网站| 久久一区二区精品| 91精品欧美综合在线观看最新| 午夜精品婷婷| 久久国产电影| 欧美精品 日韩| 日韩 欧美 高清| 麻豆蜜桃在线| 伊人开心综合网| 精品少妇人妻av一区二区| www亚洲人| 国产欧美视频一区二区| 欧美精品二区三区四区免费看视频 | 成人黄色av网址| 日韩免费视频一区二区| 青青草精品在线| 亚洲欧洲日韩精品在线| 欧美蜜桃一区二区三区| 美女在线视频一区二区| 日本a人精品| 欧美精品在线观看播放| 日本中文字幕影院| www.久久99| 日韩一区二区视频在线观看| 欧美国产在线一区| 永久免费精品视频| 精品99一区二区三区| 喷水视频在线观看| 日韩理论电影中文字幕| 亚洲欧洲日韩国产| 中文字幕精品亚洲| 性xxxx欧美老肥妇牲乱| 欧美大片免费看| 国产无遮挡裸体免费视频| 99精品热视频只有精品10| 97欧美精品一区二区三区| 特黄视频免费看| 日韩在线一二三区| 成人国产亚洲精品a区天堂华泰| 国产一区二区三区四区视频| 国产精品一区二区视频| 国产一级特黄a大片99| 欧美日韩国产综合视频| 国产精品久久久久一区二区三区 | 国产经品一区二区| 天堂v在线观看| 久久久精品蜜桃| 国产又爽又黄ai换脸| 大香伊人中文字幕精品| 色综合久久综合网97色综合| 粉色视频免费看| 成人影院中文字幕| 国产亚洲精品久久久久久777| 欧美一区免费观看| 亚洲三级电影在线观看| 国产精品情侣自拍| 亚洲奶汁xxxx哺乳期| 久久久久久久久免费| 福利在线小视频| 在线最新版中文在线| 欧美一区二区三区四区视频| 人妻丰满熟妇av无码久久洗澡 | 国产中文欧美日韩在线| 亚洲国产精品成人av| 影音先锋男人在线| 悠悠资源网久久精品| 国产精品一区二区女厕厕| 免费观看a视频| 国产精品乱码妇女bbbb| 自拍日韩亚洲一区在线| 24小时成人在线视频| 国产视频精品xxxx| 久草成人在线视频| 欧美jizzhd欧美| 久久综合色综合88| 中文字幕の友人北条麻妃| 六月婷婷综合| 精品久久久久久久久久久久久久久久久| 五月天综合视频| 一区久久精品| 91中文精品字幕在线视频| 男人久久精品| 日韩中文首页| 亚洲精选中文字幕| 毛片a片免费观看| 日本免费新一区视频| 国产经品一区二区| av大大超碰在线| 欧美日韩精品欧美日韩精品一| 在线精品一区二区三区| 欧美三级不卡| 成人午夜在线影院| 91手机在线视频| 色七七影院综合| 国产精品视频午夜| 成人黄色三级视频| 99久久婷婷国产综合精品青牛牛| 亚洲大胆美女视频| 久久精品www人人爽人人| 美女国产一区二区| 区一区二区三区中文字幕| av中文字幕在线观看第一页| 91精品国产乱| 国产精品视频看看| 久久成人免费电影| 亚欧精品在线| 成人免费在线观看视频| 亚洲视频在线播放| 无码人妻精品一区二区50| 久久综合色天天久久综合图片| 91黄色在线看| www.久久东京| 久久免费少妇高潮久久精品99| 性少妇videosexfreexxx片| 亚洲色图在线视频| 奇米777在线视频| 91精品国偷自产在线电影 | 亚洲在线观看一区| 国产欧美va欧美va香蕉在| 日本高清视频精品| 欧美性在线观看| 久久免费少妇高潮久久精品99| 久久久精品免费| 欧美肥老妇视频| 亚洲欧美日本精品| 欧美日韩美女在线| 国产欧美精品一区| 国产日韩精品久久久| 久久久电影一区二区三区| 久久综合久久综合久久| 久久久久久毛片| 亚洲成人一区二区| 日韩欧中文字幕| 亚洲无线码在线一区观看| av网站免费在线看| 日韩中文字幕一区二区三区| 日韩一区国产在线观看| 先锋影音一区二区| 欧美人与性动交| 亚洲 欧美 激情 另类| 色综合天天综合网天天狠天天| 精品无人区无码乱码毛片国产 | 国产电影一区二区| 久久久久久久影院| 精品福利视频导航大全| 精品视频一区三区九区| 久久久久久天堂| 久久久亚洲精品一区二区三区| 亚洲国产精品三区| 午夜日韩在线| 欧美日韩在线观看一区二区三区| 亚洲国产综合在线观看| 国模视频一区二区三区| 国产日本在线观看| 日韩精品专区在线影院观看 | 欧美人体做爰大胆视频| 免费麻豆国产一区二区三区四区| 99精品欧美一区二区三区综合在线| 国产一级片黄色| 午夜精品免费| 亚州欧美一区三区三区在线| 99re8这里有精品热视频免费| 日韩美女免费线视频| 黄网站视频在线观看| 日韩电影中文字幕在线| 亚洲一级av毛片| 五月婷婷欧美视频| 久久国产高清视频| 91麻豆文化传媒在线观看| 亚洲va在线va天堂va偷拍| 日韩午夜精品| 一区二区三区四区五区视频 | 亚洲成人激情在线观看| 中文 欧美 日韩| 午夜成人在线视频| 久久久精品一区二区涩爱| 国产精品污污网站在线观看| 国产精品无码电影| 国产精品自拍一区| 无尽裸体动漫2d在线观看| 亚洲一区二区三区高清不卡| 大地资源网在线观看免费官网 | 男人草女人视频| 日韩精品午夜| 欧洲精品久久| 伦理一区二区三区| 国产精华一区| 日本一区二区三区电影免费观看| 国产精品亚发布| 欧美黑人疯狂性受xxxxx野外| 久久人人97超碰精品888| 中国av在线播放| 精品激情国产视频| 中文字幕在线视频区| 亚洲午夜av电影| 欧美少妇另类| 亚洲免费电影在线观看| 亚洲av片在线观看| 亚洲国产精品人久久电影| 亚洲第一天堂影院| 日韩一卡二卡三卡| 精品人妻一区二区三区日产乱码| 在线91免费看| 国产特级aaaaaa大片| 91精品国产一区二区| 亚洲综合网av| 欧美二区在线观看| 国产毛片一区二区三区va在线| 欧美美女一区二区在线观看| 伊人网av在线| 欧美日韩国产综合一区二区三区 | 欧美aaaaa成人免费观看视频| 国产成人综合一区| 日韩电影免费一区| 亚洲性图一区二区| 免费在线观看不卡| 亚洲免费av一区| 激情综合网av| 国产调教打屁股xxxx网站| 国产成人综合亚洲网站| 污网站免费观看| 99久久综合精品| 天天躁日日躁aaaa视频| 国产精品麻豆欧美日韩ww| 极品尤物一区二区| 1000精品久久久久久久久| 欧美成人777| 亚洲一区二区视频在线观看| 日韩欧美亚洲一区二区三区| 欧美性生交xxxxx久久久| 最近中文在线观看| 日韩一级完整毛片| 少妇人妻精品一区二区三区| 亚洲欧美精品suv| 麻豆视频免费在线观看| 欧美日韩成人在线播放| 欧亚av在线| 国产精品热视频| 日韩最新av| 日本午夜一区二区三区| 99国产**精品****| 妞干网在线视频观看| 久久夜色精品| 99九九精品视频| av资源网一区| 97在线观看视频免费| 亚洲高清视频中文字幕| 久草热在线观看| 日韩精品在线网站| 欧美色视频免费| 欧美精品在线免费观看| 亚洲深夜视频| 亚洲va欧美va国产综合剧情| 亚洲精品合集| 欧美少妇一级片| 丝袜诱惑亚洲看片| 亚洲熟妇一区二区| 国产精品视频线看| 国产视频91在线| 在线播放中文字幕一区| 日韩三级电影网| 九九视频这里只有精品 | 91网站在线免费观看| 亚州av日韩av| 国风产精品一区二区| 视频一区二区三区入口| 美女扒开腿免费视频| 成人欧美一区二区三区视频网页| www.日本精品| 日韩欧美久久久| av影片免费在线观看| 国外成人在线直播| 粉嫩一区二区三区在线观看| 欧美日本亚洲| 亚洲三级影院| 人妻精品久久久久中文字幕69| 国产亚洲视频系列| 欧美福利视频一区二区| 91精品久久久久久蜜臀| eeuss影院www在线观看| 66m—66摸成人免费视频| 日韩一区二区三区高清在线观看| 五月天色一区| 久久一综合视频| 玖玖爱在线精品视频| 一区二区三区四区中文字幕| 国产又黄又爽视频| 在线成人激情黄色| 日本精品网站| 欧美亚洲丝袜| 麻豆成人精品| 日本黄色网址大全| 精品欧美aⅴ在线网站| 高清一区二区三区四区| 欧美成人午夜激情在线| 图片一区二区| 一区二区精品免费视频| 男女男精品视频| 少妇太紧太爽又黄又硬又爽小说| 色老头久久综合| 视频国产在线观看| 欧美一区二区三区免费视| 色先锋久久影院av| 黄色网页免费在线观看| 91偷拍与自偷拍精品| 欧美三级一区二区三区| 亚洲精品国产suv| 欧亚在线中文字幕免费| 欧美一区二区三区成人久久片| 蘑菇福利视频一区播放| 法国伦理少妇愉情| 91激情在线视频| 91精彩在线视频| 成人欧美一区二区三区在线| 中文乱码免费一区二区三区下载| 制服丝袜中文字幕第一页| 亚洲精品高清视频在线观看| 99热这里只有精品3| 欧美激情亚洲一区| 国内精品偷拍| 99色精品视频| 中文幕一区二区三区久久蜜桃| 国产一区二区在线视频聊天| 免费av在线一区| 欧美黄色网视频| 毛片一区二区三区四区| 欧美激情综合网| 国产精品久久久久久久免费| 欧美日韩高清在线观看| 秋霞在线一区| 男女视频一区二区三区| 中文字幕永久在线不卡| www.蜜桃av.com| 91精品国产高清自在线| 少妇精品久久久一区二区三区| 岛国毛片在线播放| 亚洲一区二区三区四区中文字幕| 天天射,天天干| 国产精品成人v| 亚洲国产精品日韩专区av有中文 | 久久色免费在线视频| 91麻豆精品国产91久久久久推荐资源 | 韩国中文字幕2020精品| 国产日韩欧美在线看| 午夜国产精品视频| 亚洲av无码一区二区三区人 | 色综合免费视频| 国产精品com| 欧美另类亚洲| 亚洲理论片在线观看| 欧美成人女星排行榜| 裤袜国产欧美精品一区| 三级在线免费观看| 久久久久久夜精品精品免费| 国产黄色片网站| 国产不卡av在线免费观看| 欧美一区二区三区久久精品茉莉花| 免费中文字幕av| 欧美一区二区在线看| 国模一区二区| 成年人网站国产| 国产精品久久毛片|