fbd8a77cc508e2508de06e6b2b1ac6585b0a94dc
[nit.git] / src / syntax / escape.nit
1 # This file is part of NIT ( http://www.nitlanguage.org ).
2 #
3 # Copyright 2008 Jean Privat <jean@pryen.org>
4 #
5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at
8 #
9 # http://www.apache.org/licenses/LICENSE-2.0
10 #
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
16
17 # Manage nested escapable blocks (while, for and closure) and escape statements (break and continue)
18 package escape
19
20 import syntax_base
21
22 # Stack escapable blocks
23 class EscapableContext
24 # Stack of blocks
25 var _stack: Array[EscapableBlock] = new Array[EscapableBlock]
26
27 # Known labels
28 # (all labels, even out of scopes ones)
29 # Used to find duplicates
30 var _labels: Array[ALabel] = new Array[ALabel]
31
32 # Push a new escapable block
33 # Display error message if tere is a problem with the label
34 fun push(block: EscapableBlock, n_label: nullable ALabel)
35 do
36 _stack.push(block)
37 if n_label != null then
38 var lab = n_label.n_id.to_symbol
39 for nl in _labels do
40 if n_label != nl and lab == nl.n_id.to_symbol then
41 visitor.error(n_label, "Syntax error: label {lab} already defined at {nl.location.relative_to(n_label.location)}.")
42 return
43 end
44 end
45 _labels.add(n_label)
46 block._lab = lab
47 end
48 end
49
50 # Is there no block in the stack?
51 fun is_empty: Bool do return _stack.is_empty
52
53 # Return the current block (the last stacked)
54 fun head: EscapableBlock
55 do
56 return _stack.last
57 end
58
59 # Return the block associed to a label
60 # Output an error end return null if the label is not known
61 fun get_by_label(nl: ALabel): nullable EscapableBlock
62 do
63 var i = _stack.length - 1
64 var block: nullable EscapableBlock = null
65 var lab = nl.n_id.to_symbol
66 while i >= 0 do
67 var b = _stack[i]
68 if b.lab == lab then return b
69 i -= 1
70 end
71 visitor.error(nl, "Syntax error: invalid label {lab}.")
72 return null
73 end
74
75 # Remove the last block (the last stacked)
76 fun pop
77 do
78 var n = _stack.pop
79 end
80
81 readable var _visitor: AbsSyntaxVisitor
82 init (v: AbsSyntaxVisitor)
83 do
84 _visitor = v
85 end
86 end
87
88 ###############################################################################
89
90 # A escapable block correspond to a block statement where break and/or continue can by used
91 # 'for' and 'while' use this class
92 # 'do' uses the BreakOnlyEscapableBlock subclass
93 # closures uses the EscapableClosure subclass
94 class EscapableBlock
95 # The syntax node of the block
96 readable var _node: ANode
97
98 # The label of the block (if any)
99 # Set by the push in EscapableContext
100 readable var _lab: nullable Symbol
101
102 # Is self a break closure ?
103 fun is_break_block: Bool do return false
104
105 # Collected expressions used in breaks.
106 # null if break does not accept values.
107 # break_list is used to store expressions used in break statments and perform type checks latter
108 fun break_list: nullable Array[AExpr] do return null
109
110 # The static type required by the continue statement (if any)
111 fun continue_stype: nullable MMType do return null
112
113 init(node: ANode)
114 do
115 _node = node
116 end
117 end
118
119 # specific EscapableBlock where only break can be used
120 class BreakOnlyEscapableBlock
121 special EscapableBlock
122 redef fun is_break_block: Bool do return true
123
124 init(node: ANode) do super
125 end
126
127 # specific EscapableBlock for closures
128 class EscapableClosure
129 special EscapableBlock
130 # The associated closure
131 readable var _closure: MMClosure
132
133 redef fun is_break_block do return _closure.is_break
134
135 redef readable var _break_list: nullable Array[AExpr]
136
137 redef fun continue_stype do return _closure.signature.return_type
138
139 init(node: ANode, closure: MMClosure, break_list: nullable Array[AExpr])
140 do
141 super(node)
142 _closure = closure
143 _break_list = break_list
144 end
145 end
146
147 ###############################################################################
148
149 class AEscapeExpr
150 special ALabelable
151 # The associated escapable block
152 readable var _escapable: nullable EscapableBlock
153
154 # The name of the keyword
155 fun kwname: String is abstract
156
157 # Compute, set and return the associated escapable block
158 fun compute_escapable_block(lctx: EscapableContext): nullable EscapableBlock
159 do
160 var block: nullable EscapableBlock
161 var nl = n_label
162 if nl != null then
163 block = lctx.get_by_label(nl)
164 else if lctx.is_empty then
165 lctx.visitor.error(self, "Syntax Error: '{kwname}' statment outside block.")
166 return null
167 else
168 block = lctx.head
169 end
170 _escapable = block
171 return block
172 end
173 end
174
175 redef class AContinueExpr
176 special AEscapeExpr
177 redef fun kwname do return "continue"
178 end
179
180 redef class ABreakExpr
181 special AEscapeExpr
182 redef fun kwname do return "break"
183 end
184