forked from com-lihaoyi/fastparse
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathStatements.scala
More file actions
197 lines (179 loc) · 9.68 KB
/
Copy pathStatements.scala
File metadata and controls
197 lines (179 loc) · 9.68 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
package pythonparse
import fastparse._
import Expressions.{whitespace => _, _}
import Lexical.kw
object Statements extends Statements(0)
/**
* Python's statement grammar. This can only be used in statement-blocks,
* and is sensitive to newlines and indentation to determine nesting
*
* Manually transcribed from https://docs.python.org/2/reference/grammar.html
*/
class Statements(indent: Int){
implicit def whitespace(cfg: P[_]): P[Unit] = Lexical.wscomment(cfg)
def space[_: P] = P( CharIn(" \n") )
def NEWLINE[_: P]: P0 = P( "\n" | End )
def ENDMARKER[_: P]: P0 = P( End )
def single_input[_: P]: P[Seq[Ast.stmt]] = P(
NEWLINE.map(_ => Nil) |
simple_stmt |
compound_stmt.map(Seq(_)) ~ NEWLINE
)
def indents[_: P] = P( "\n" ~~ " ".repX(indent) )
def spaces[_: P] = P( (Lexical.nonewlinewscomment.? ~~ "\n").repX(1) )
def file_input[_: P]: P[Seq[Ast.stmt]] =
P( spaces.? ~ stmt.repX(0, spaces) ~ spaces.? ).map(_.flatten)
def eval_input[_: P]: P[Ast.expr] = P( testlist ~ NEWLINE.rep ~ ENDMARKER ).map(tuplize)
def collapse_dotted_name(name: Seq[Ast.identifier]): Ast.expr = {
name.tail.foldLeft[Ast.expr](Ast.expr.Name(name.head, Ast.expr_context.Load))(
(x, y) => Ast.expr.Attribute(x, y, Ast.expr_context.Load)
)
}
def decorator[_: P]: P[Ast.expr] = P( "@" ~/ dotted_name ~ ("(" ~ arglist ~ ")" ).? ~~ Lexical.nonewlinewscomment.? ~~ NEWLINE).map{
case (name, None) => collapse_dotted_name(name)
case (name, Some((args, (keywords, starargs, kwargs)))) =>
val x = collapse_dotted_name(name)
Ast.expr.Call(x, args, keywords, starargs, kwargs)
}
def decorators[_: P] = P( decorator.rep )
def decorated[_: P]: P[Ast.stmt] = P( decorators ~ (classdef | funcdef) ).map{case (a, b) => b(a)}
def classdef[_: P]: P[Seq[Ast.expr] => Ast.stmt.ClassDef] =
P( kw("class") ~/ NAME ~ ("(" ~ testlist.? ~ ")").?.map(_.toSeq.flatten.flatten) ~ ":" ~~ suite ).map{
case (a, b, c) => Ast.stmt.ClassDef(a, b, c, _)
}
def funcdef[_: P]: P[Seq[Ast.expr] => Ast.stmt.FunctionDef] = P( kw("def") ~/ NAME ~ parameters ~ ":" ~~ suite ).map{
case (name, args, suite) => Ast.stmt.FunctionDef(name, args, suite, _)
}
def parameters[_: P]: P[Ast.arguments] = P( "(" ~ varargslist ~ ")" )
def stmt[_: P]: P[Seq[Ast.stmt]] = P( compound_stmt.map(Seq(_)) | simple_stmt )
def simple_stmt[_: P]: P[Seq[Ast.stmt]] = P( small_stmt.rep(1, sep = ";") ~ ";".? )
def small_stmt[_: P]: P[Ast.stmt] = P(
print_stmt | del_stmt | pass_stmt | flow_stmt |
import_stmt | global_stmt | exec_stmt | assert_stmt | expr_stmt
)
def expr_stmt[_: P]: P[Ast.stmt] = {
def aug = P( testlist ~ augassign ~ (yield_expr | testlist.map(tuplize)) )
def assign = P( testlist ~ ("=" ~ (yield_expr | testlist.map(tuplize))).rep )
P(
aug.map{case (a, b, c) => Ast.stmt.AugAssign(tuplize(a), b, c) } |
assign.map{
case (a, Nil) => Ast.stmt.Expr(tuplize(a))
case (a, b) => Ast.stmt.Assign(Seq(tuplize(a)) ++ b.init, b.last)
}
)
}
def augassign[_: P]: P[Ast.operator] = P(
"+=".!.map(_ => Ast.operator.Add) |
"-=".!.map(_ => Ast.operator.Sub) |
"*=".!.map(_ => Ast.operator.Mult) |
"/=".!.map(_ => Ast.operator.Div) |
"%=".!.map(_ => Ast.operator.Mod) |
"&=".!.map(_ => Ast.operator.BitAnd) |
"|=".!.map(_ => Ast.operator.BitOr) |
"^=".!.map(_ => Ast.operator.BitXor) |
"<<=".!.map(_ => Ast.operator.LShift) |
">>=".!.map(_ => Ast.operator.RShift) |
"**=".!.map(_ => Ast.operator.Pow) |
"//=".!.map(_ => Ast.operator.FloorDiv)
)
def print_stmt[_: P]: P[Ast.stmt.Print] = {
def noDest = P( test.rep(sep = ",") ~ ",".?).map(Ast.stmt.Print(None, _, true))
def dest = P( ">>" ~ test ~ ("," ~ test).rep ~ ",".?).map{case (dest, exprs) => Ast.stmt.Print(Some(dest), exprs, true)}
P( "print" ~~ " ".rep ~~ (noDest | dest) )
}
def del_stmt[_: P] = P( kw("del") ~~ " ".rep ~~ exprlist ).map(Ast.stmt.Delete)
def pass_stmt[_: P] = P( kw("pass") ).map(_ => Ast.stmt.Pass)
def flow_stmt[_: P]: P[Ast.stmt] = P( break_stmt | continue_stmt | return_stmt | raise_stmt | yield_stmt )
def break_stmt[_: P] = P( kw("break") ).map(_ => Ast.stmt.Break)
def continue_stmt[_: P] = P( kw("continue") ).map(_ => Ast.stmt.Continue)
def return_stmt[_: P] = P( kw("return") ~~ " ".rep ~~ testlist.map(tuplize).? ).map(Ast.stmt.Return)
def yield_stmt[_: P] = P( yield_expr ).map(Ast.stmt.Expr)
def raise_stmt[_: P]: P[Ast.stmt.Raise] = P( kw("raise") ~~ " ".rep ~~test.? ~ ("," ~ test).? ~ ("," ~ test).? ).map(Ast.stmt.Raise.tupled)
def import_stmt[_: P]: P[Ast.stmt] = P( import_name | import_from )
def import_name[_: P]: P[Ast.stmt.Import] = P( kw("import") ~ dotted_as_names ).map(Ast.stmt.Import)
def import_from[_: P]: P[Ast.stmt.ImportFrom] = {
def named = P( ".".rep(1).!.? ~ dotted_name.!.map(Some(_)) )
def unNamed = P( ".".rep(1).!.map(x => (Some(x), None)) )
def star = P( "*".!.map(_ => Seq(Ast.alias(Ast.identifier("*"), None))) )
P( kw("from") ~ (named | unNamed) ~ kw("import") ~ (star | "(" ~ import_as_names ~ ")" | import_as_names) ).map{
case (dots, module, names) => Ast.stmt.ImportFrom(module.map(Ast.identifier), names, dots.map(_.length))
}
}
def import_as_name[_: P]: P[Ast.alias] = P( NAME ~ (kw("as") ~ NAME).? ).map(Ast.alias.tupled)
def dotted_as_name[_: P]: P[Ast.alias] = P( dotted_name.map(x => Ast.identifier(x.map(_.name).mkString("."))) ~ (kw("as") ~ NAME).? ).map(Ast.alias.tupled)
def import_as_names[_: P] = P( import_as_name.rep(1, ",") ~ (",").? )
def dotted_as_names[_: P] = P( dotted_as_name.rep(1, ",") )
def dotted_name[_: P] = P( NAME.rep(1, ".") )
def global_stmt[_: P]: P[Ast.stmt.Global] = P( kw("global") ~ NAME.rep(sep = ",") ).map(Ast.stmt.Global)
def exec_stmt[_: P]: P[Ast.stmt.Exec] = P( kw("exec") ~ expr ~ (kw("in") ~ test ~ ("," ~ test).?).? ).map {
case (expr, None) => Ast.stmt.Exec(expr, None, None)
case (expr, Some((globals, None))) => Ast.stmt.Exec(expr, Some(globals), None)
case (expr, Some((globals, Some(locals)))) => Ast.stmt.Exec(expr, Some(globals), Some(locals))
}
def assert_stmt[_: P]: P[Ast.stmt.Assert] = P( kw("assert") ~ test ~ ("," ~ test).? ).map(Ast.stmt.Assert.tupled)
def compound_stmt[_: P]: P[Ast.stmt] = P( if_stmt | while_stmt | for_stmt | try_stmt | with_stmt | decorated )
def if_stmt[_: P]: P[Ast.stmt.If] = {
def firstIf = P( kw("if") ~/ test ~ ":" ~~ suite )
def elifs = P( (space_indents ~~ kw("elif") ~/ test ~ ":" ~~ suite).repX )
def lastElse = P( (space_indents ~~ kw("else") ~/ ":" ~~ suite).? )
P( firstIf ~~ elifs ~~ lastElse ).map{
case (test, body, elifs, orelse) =>
val (init :+ last) = (test, body) +: elifs
val (last_test, last_body) = last
init.foldRight(Ast.stmt.If(last_test, last_body, orelse.toSeq.flatten)){
case ((test, body), rhs) => Ast.stmt.If(test, body, Seq(rhs))
}
}
}
def space_indents[_: P] = P( spaces.repX ~~ " ".repX(indent) )
def while_stmt[_: P] = P( kw("while") ~/ test ~ ":" ~~ suite ~~ (space_indents ~~ kw("else") ~/ ":" ~~ suite).?.map(_.toSeq.flatten) ).map(Ast.stmt.While.tupled)
def for_stmt[_: P]: P[Ast.stmt.For] = P( kw("for") ~/ exprlist ~ kw("in") ~ testlist ~ ":" ~~ suite ~~ (space_indents ~ kw("else") ~/ ":" ~~ suite).? ).map {
case (itervars, generator, body, orelse) =>
Ast.stmt.For(tuplize(itervars), tuplize(generator), body, orelse.toSeq.flatten)
}
def try_stmt[_: P]: P[Ast.stmt]= {
def `try` = P( kw("try") ~/ ":" ~~ suite )
def excepts: P[Seq[Ast.excepthandler]] = P( (except_clause ~ ":" ~~ suite).map{
case (None, body) => Ast.excepthandler.ExceptHandler(None, None, body)
case (Some((x, None)), body) => Ast.excepthandler.ExceptHandler(Some(x), None, body)
case (Some((x, Some(y))), body) => Ast.excepthandler.ExceptHandler(Some(x), Some(y), body)
}.repX )
def `else` = P( space_indents ~~ kw("else") ~/ ":" ~~ suite )
def `finally` = P( space_indents ~~ kw("finally") ~/ ":" ~~ suite )
P( `try` ~~ excepts ~~ `else`.? ~~ `finally`.? ).map{
case (tryBlock, excepts, elseBlock, None) =>
Ast.stmt.TryExcept(tryBlock, excepts, elseBlock.toSeq.flatten)
case (tryBlock, Nil, None, Some(finallyBlock)) =>
Ast.stmt.TryFinally(tryBlock, finallyBlock)
case (tryBlock, excepts, elseBlock, Some(finallyBlock)) =>
Ast.stmt.TryFinally(
Seq(Ast.stmt.TryExcept(tryBlock, excepts, elseBlock.toSeq.flatten)),
finallyBlock
)
}
}
def with_stmt[_: P]: P[Ast.stmt.With] = P( kw("with") ~/ with_item.rep(1, ",")~ ":" ~~ suite ).map{
case (items, body) =>
val (last_expr, last_vars) = items.last
val inner = Ast.stmt.With(last_expr, last_vars, body)
items.init.foldRight(inner){
case ((expr, vars), body) => Ast.stmt.With(expr, vars, Seq(body))
}
}
def with_item[_: P]: P[(Ast.expr, Option[Ast.expr])] = P( test ~ (kw("as") ~ expr).? )
// NB compile.c makes sure that the default except clause is last
def except_clause[_: P] = P( space_indents ~ kw("except") ~/ (test ~ ((kw("as") | ",") ~ test).?).? )
def suite[_: P]: P[Seq[Ast.stmt]] = {
def deeper: P[Int] = {
def commentLine = P("\n" ~~ Lexical.nonewlinewscomment.?.map(_ => 0)).map((_, Some("")))
def endLine = P("\n" ~~ (" "|"\t").repX(indent + 1).!.map(_.length) ~~ Lexical.comment.!.? )
P( Lexical.nonewlinewscomment.? ~~ ( endLine | commentLine ).repX(1) ).map{
_.collectFirst{ case (s, None) => s}
}.filter(_.isDefined).map(_.get)
}
def indented = P( deeper.flatMapX{ nextIndent =>
new Statements(nextIndent).stmt.repX(1, spaces.repX(1) ~~ (" " * nextIndent | "\t" * nextIndent)).map(_.flatten)
} )
P( indented | " ".rep ~ simple_stmt )
}
}