| | 31 | val entrance = phrase(uriParser) |
| | 32 | |
| | 33 | def parse(text: String): ParseResult[_] = { |
| | 34 | val input = new PackratReader(new CharSequenceReader(text)) |
| | 35 | entrance(input) |
| | 36 | } |
| | 37 | |
| | 38 | def extractResult(r : ParseResult[_]) = r match { |
| | 39 | case Success(a, _) => a |
| | 40 | case NoSuccess(a, _) => a |
| | 41 | } |
| | 42 | |
| | 43 | /** the type of input elements defined in the Parsers is <code>Char</code> */ |
| | 44 | override type Elem = Char |
| | 45 | |
| | 46 | implicit def toString(cs: List[Char]) = cs.mkString |
| | 47 | |
| | 48 | // def accept[ES](es: ES)(implicit arg0: (ES) ? List[Elem]): Parser[List[Elem]] |
| | 49 | implicit def toCharList(s: String) = s.toList // |
| | 50 | implicit def stringToParser(s: String) = accept(s) |
| | 51 | |
| | 52 | def uriParser = (pages | |
| | 53 | commands | |
| | 54 | submit_comment | |
| | 55 | post_comment | |
| | 56 | delete_comment | |
| | 57 | delete_comments | |
| | 58 | edit_comment | |
| | 59 | rep(any) ^^ {s => NoSuchUri(s)}) |
| | 60 | |
| | 61 | def pages = get_method ~> (single_article | |
| | 62 | articles_by_tags | |
| | 63 | articles | |
| | 64 | feeds | |
| | 65 | review_pending_comments | |
| | 66 | review_comment | |
| | 67 | add_comment_form) |
| | 68 | |
| | 69 | |
| | 70 | def get_method = "GET " ^^^ () |
| | 71 | def post_method = "POST " ^^^ () |
| | 72 | |
| | 73 | def commands = |
| | 74 | post_method ~ ("/commands" | "/c") ~> ingest_draft |
| | 75 | |
| | 76 | def submit_comment = |
| | 77 | post_method ~ "/e/add-comment/" ~> p_plink_title <~ eof ^^ { |
| | 78 | t => CommentSubmission(t) |
| | 79 | } |
| | 80 | |
| | 81 | def edit_comment = |
| | 82 | post_method ~ "/x/edit-comment/" ~> p_int <~ eof ^^ { |
| | 83 | n => AlterComment(n) |
| | 84 | } |
| | 85 | |
| | 86 | def post_comment = |
| | 87 | post_method ~ "/x/post-comment" ~ eof ^^^ PostComment |
| | 88 | |
| | 89 | def delete_comment = |
| | 90 | post_method ~ "/x/delete-comment" ~ eof ^^^ DeleteComment |
| | 91 | |
| | 92 | def delete_comments = |
| | 93 | post_method ~ "/x/delete-comments" ~ eof ^^^ DeleteComments |
| | 94 | |
| | 95 | def review_pending_comments = |
| | 96 | "/z/review-comments" ~> page <~ eof ^^ { |
| | 97 | p => ReviewComments(p) |
| | 98 | } |
| | 99 | |
| | 100 | def review_comment = |
| | 101 | "/z/review-comment/" ~> p_int <~ eof ^^ { |
| | 102 | n => ReviewComment(n) |
| | 103 | } |
| | 104 | |
| | 105 | def add_comment_form = |
| | 106 | "/c/add-comment/" ~> p_plink_title <~ eof ^^ { |
| | 107 | t => CommentFormView(t) |
| | 108 | } |
| | 109 | |
| | 110 | def ingest_draft = |
| | 111 | "/post-draft/" ~> rep(any) ^^ { |
| | 112 | x => Command(A.Ingest(x)) |
| | 113 | } |
| | 114 | |
| | 115 | // ----- feeds |
| | 116 | |
| | 117 | def feeds = |
| | 118 | ("/feeds" | "/f") ~> (articles_feed | |
| | 119 | comments_feeds | |
| | 120 | tag_feed | |
| | 121 | tags_feed | |
| | 122 | article_comments_feed) |
| | 123 | |
| | 124 | def articles_feed = |
| | 125 | ("/articles" | "/a") ~> atom_xml ^^^ AtomFeed(F.AllPosts) |
| | 126 | |
| | 127 | def comments_feeds = |
| | 128 | all_comments_feed | article_comments_feed |
| | 129 | |
| | 130 | def all_comments_feed = |
| | 131 | ("/comments" | "/c") ~> atom_xml ^^^ AtomFeed(F.AllComments) |
| | 132 | |
| | 133 | def article_comments_feed = |
| | 134 | ("/comments/p/" | "/c/p/") ~> p_plink_title <~ atom_xml ^^ { |
| | 135 | t => AtomFeed(F.PostComments(t)) |
| | 136 | } |
| | 137 | |
| | 138 | def tag_feed = |
| | 139 | "/tag/" ~> p_tag_name <~ atom_xml ^^ { |
| | 140 | t => AtomFeed(F.ByTag(t)) |
| | 141 | } |
| | 142 | |
| | 143 | def tags_feed = |
| | 144 | ("/tags/" | "/t/") ~> repsep(p_tag_name, ',') <~ atom_xml ^^ { |
| | 145 | ts => AtomFeed(F.ByTags(ts sortWith (_ < _))) |
| | 146 | } |
| | 147 | |
| | 148 | def atom_xml = |
| | 149 | "/atom.xml" ~> eof ^^^ () |
| | 150 | |
| | 151 | |
| | 152 | // ----- articles |
| | 153 | |
| | 154 | def articles = |
| | 155 | ("/articles" | "/a") ~> (all_articles | |
| | 156 | single_article | |
| | 157 | articles_by_date | |
| | 158 | articles_by_tag | |
| | 159 | articles_by_tags) |
| | 160 | |
| | 161 | def all_articles = |
| | 162 | page ^^ { |
| | 163 | p => XhtmlView(V.All(p)) |
| | 164 | } |
| | 165 | |
| | 166 | def single_article = |
| | 167 | "/p/" ~> p_plink_title <~ eof ^^ { |
| | 168 | t => XhtmlView(V.ByPermatitle(t)) |
| | 169 | } |
| | 170 | |
| | 171 | def articles_by_tag = |
| | 172 | "/tag/" ~> p_tag_name ~ page ^^ { |
| | 173 | case t ~ p => XhtmlView(V.ByTag(t, p)) |
| | 174 | } |
| | 175 | |
| | 176 | def articles_by_tags = |
| | 177 | ("/t/" | "/tags/") ~> repsep(p_tag_name, ',') ~ page ^^ { |
| | 178 | case ts ~ p => XhtmlView(V.ByTags(ts sortWith (_ < _), p)) |
| | 179 | } |
| | 180 | |
| | 181 | def articles_by_date_forRef = |
| | 182 | '/' ~> p_int ~ (page ^^ {case x => } | |
| | 183 | '/' ~> p_int ~ (page | |
| | 184 | '/' ~> p_int ~ (page | |
| | 185 | '/' ~> p_plink_title |
| | 186 | ) |
| | 187 | ) |
| | 188 | ) |
| | 189 | |
| | 190 | def articles_by_date = articles_by_date_1 | articles_by_date_2 | articles_by_date_3 | articles_by_date_4 |
| | 191 | def articles_by_date_1 = memo('/' ~ p_int) ~ page ^^ { |
| | 192 | case '/' ~ y ~ p => XhtmlView(V.ByYear(y, p)) |
| | 193 | } |
| | 194 | def articles_by_date_2 = memo('/' ~ p_int ~ '/' ~ p_int) ~ page ^^ { |
| | 195 | case '/' ~ y ~ '/' ~ m ~ p => XhtmlView(V.ByMonth(y, m, p)) |
| | 196 | } |
| | 197 | def articles_by_date_3 = memo('/' ~ p_int ~ '/' ~ p_int ~ '/' ~ p_int) ~ page ^^ { |
| | 198 | case '/' ~ y ~ '/' ~ m ~ '/' ~ d ~ p => XhtmlView(V.ByDay(y, m, d, p)) |
| | 199 | } |
| | 200 | def articles_by_date_4 = memo('/' ~ p_int ~ '/' ~ p_int ~ '/' ~ p_int) ~ '/' ~ p_plink_title ^^ { |
| | 201 | case '/' ~ y ~ '/' ~ m ~ '/' ~ d ~ '/' ~ t => XhtmlView(V.ByYMDPermatitle(y, m, d, t)) |
| | 202 | } |
| | 203 | |
| | 204 | |
| | 205 | // ----- elements |
| | 206 | |
| | 207 | def page = (eof ^^^ None | ("/page/" | "/p/") ~> p_int <~ eof ^^ (Some(_))) |
| | 208 | def p_int = rep1(digit) ^^ (toString(_).toInt) |
| | 209 | def p_plink_title = rep1(alphaNum | '-' | '_') ^^ toString |
| | 210 | def p_tag_name = rep1(alphaNum | '-' | '_' | '.' ) ^^ toString |
| | 211 | |
| | 212 | // ----- basic |
| | 213 | |
| | 214 | def alphaNum = elem("digit", Character.isLetterOrDigit) |
| | 215 | def digit = elem("digit", Character.isDigit) |
| | 216 | def upper = elem("upper", Character.isUpperCase) |
| | 217 | def lower = elem("lower", Character.isLowerCase) |
| | 218 | def letter = elem("letter", Character.isLetter) |
| | 219 | def eof = elem("end of file", _ == '\032') ^^^ () |
| | 220 | def any = elem("any char", _ != '\032') |
| | 221 | |
| | 222 | |
| | 223 | // ----- simple test |
| | 224 | |
| | 225 | def main(args: Array[String]) { |
| | 226 | val text = "GET /a/p/a_blog_title" |
| | 227 | //val text = "GETapaaa" |
| | 228 | val r = parse(text) |
| | 229 | |
| | 230 | r match { |
| | 231 | case Success(x, _) => x |
| | 232 | case NoSuccess(x, _) => x |
| | 233 | } |
| | 234 | |
| | 235 | println(r) |
| | 236 | } |