syntax: 'do' blocks only accept labelled breaks
[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 # Return the last stacked block that accepts unlabelled break/continue
51 fun head: nullable EscapableBlock
52 do
53 var i = _stack.length - 1
54 while i >= 0 do
55 var h = _stack[i]
56 if not (h isa BreakOnlyEscapableBlock) then return h
57 i -= 1
58 end
59 return null
60 end
61
62 # Return the block associed to a label
63 # Output an error end return null if the label is not known
64 fun get_by_label(nl: ALabel): nullable EscapableBlock
65 do
66 var i = _stack.length - 1
67 var block: nullable EscapableBlock = null
68 var lab = nl.n_id.to_symbol
69 while i >= 0 do
70 var b = _stack[i]
71 if b.lab == lab then return b
72 i -= 1
73 end
74 visitor.error(nl, "Syntax error: invalid label {lab}.")
75 return null
76 end
77
78 # Remove the last block (the last stacked)
79 fun pop
80 do
81 var n = _stack.pop
82 end
83
84 readable var _visitor: AbsSyntaxVisitor
85 init (v: AbsSyntaxVisitor)
86 do
87 _visitor = v
88 end
89 end
90
91 ###############################################################################
92
93 # A escapable block correspond to a block statement where break and/or continue can by used
94 # 'for' and 'while' use this class
95 # 'do' uses the BreakOnlyEscapableBlock subclass
96 # closures uses the EscapableClosure subclass
97 class EscapableBlock
98 # The syntax node of the block
99 readable var _node: ANode
100
101 # The label of the block (if any)
102 # Set by the push in EscapableContext
103 readable var _lab: nullable Symbol
104
105 # Is self a break closure ?
106 fun is_break_block: Bool do return false
107
108 # Collected expressions used in breaks.
109 # null if break does not accept values.
110 # break_list is used to store expressions used in break statments and perform type checks latter
111 fun break_list: nullable Array[AExpr] do return null
112
113 # The static type required by the continue statement (if any)
114 fun continue_stype: nullable MMType do return null
115
116 init(node: ANode)
117 do
118 _node = node
119 end
120 end
121
122 # specific EscapableBlock where only labelled break can be used
123 class BreakOnlyEscapableBlock
124 special EscapableBlock
125 redef fun is_break_block: Bool do return true
126
127 init(node: ANode) do super
128 end
129
130 # specific EscapableBlock for closures
131 class EscapableClosure
132 special EscapableBlock
133 # The associated closure
134 readable var _closure: MMClosure
135
136 redef fun is_break_block do return _closure.is_break
137
138 redef readable var _break_list: nullable Array[AExpr]
139
140 redef fun continue_stype do return _closure.signature.return_type
141
142 init(node: ANode, closure: MMClosure, break_list: nullable Array[AExpr])
143 do
144 super(node)
145 _closure = closure
146 _break_list = break_list
147 end
148 end
149
150 ###############################################################################
151
152 class AEscapeExpr
153 special ALabelable
154 # The associated escapable block
155 readable var _escapable: nullable EscapableBlock
156
157 # The name of the keyword
158 fun kwname: String is abstract
159
160 # Compute, set and return the associated escapable block
161 fun compute_escapable_block(lctx: EscapableContext): nullable EscapableBlock
162 do
163 var block: nullable EscapableBlock
164 var nl = n_label
165 if nl != null then
166 block = lctx.get_by_label(nl)
167 else
168 block = lctx.head
169 if block == null then
170 lctx.visitor.error(self, "Syntax Error: '{kwname}' statment outside block.")
171 end
172 end
173 _escapable = block
174 return block
175 end
176 end
177
178 redef class AContinueExpr
179 special AEscapeExpr
180 redef fun kwname do return "continue"
181 end
182
183 redef class ABreakExpr
184 special AEscapeExpr
185 redef fun kwname do return "break"
186 end
187