# Used to avoid repeating the same error message
readable writable attr _already_unreash: Bool = false
- # Current controlable block (for or while)
- readable writable attr _base_block: PNode = null
-
# Set of variable that are set (assigned)
readable attr _set_variables: HashSet[Variable] = new HashSet[Variable]
_prev = p
_unreash = p.unreash
_already_unreash = p.already_unreash
- _base_block = p.base_block
end
end
end
end
-class ABlockControler
-special PExpr
- readable attr _block: PNode
-end
-
redef class ABreakExpr
-special ABlockControler
redef meth accept_control_flow(v)
do
super
- var block = v.control_flow_ctx.base_block
- if block == null then
- v.error(self, "Syntax Error: 'break' statment outside block.")
- return
- end
- _block = block
v.control_flow_ctx.unreash = true
end
end
redef class AContinueExpr
-special ABlockControler
redef meth accept_control_flow(v)
do
super
- var block = v.control_flow_ctx.base_block
- if block == null then
- v.error(self, "Syntax Error: 'continue' outside block.")
- return
- end
- _block = block
v.control_flow_ctx.unreash = true
end
end
var old_control_flow_ctx = v.control_flow_ctx
v.control_flow_ctx = v.control_flow_ctx.sub
- # Register the block
- v.control_flow_ctx.base_block = self
-
super
# Check control flow if any
if n_expr != null then
var old_control_flow_ctx = v.control_flow_ctx
v.control_flow_ctx = v.control_flow_ctx.sub
- # Register the block
- v.control_flow_ctx.base_block = n_expr
super
--- /dev/null
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Copyright 2008 Jean Privat <jean@pryen.org>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Manage nested escapable blocks (while, for and closure) and escape statements (break and continue)
+package escape
+
+import syntax_base
+
+# Stack escapable blocks
+class EscapableContext
+ # Stack of blocks
+ attr _stack: Array[EscapableBlock] = new Array[EscapableBlock]
+
+ # Push a new escapable block
+ meth push(block: EscapableBlock)
+ do
+ _stack.push(block)
+ end
+
+ # Is there no block in the stack?
+ meth is_empty: Bool do return _stack.is_empty
+
+ # Return the current block (the last stacked)
+ meth head: EscapableBlock
+ do
+ return _stack.last
+ end
+
+ # Remove the last block (the last stacked)
+ meth pop
+ do
+ var n = _stack.pop
+ end
+
+ readable attr _visitor: AbsSyntaxVisitor
+ init (v: AbsSyntaxVisitor)
+ do
+ _visitor = v
+ end
+end
+
+###############################################################################
+
+# A escapable block correspond to a block statement where break and/or continue can by used
+# For and while use this class. closures uses the EscapableClosure subclass.
+class EscapableBlock
+ # The syntax node of the block
+ readable attr _node: PNode = null
+
+ # Is self a break closure ?
+ meth is_break_block: Bool do return false
+
+ # Collected expressions used in breaks.
+ # null if break does not accept values.
+ # break_list is used to store expressions used in break statments and perform type checks latter
+ meth break_list: Array[PExpr] do return null
+
+ # The static type required by the continue statement (if any)
+ meth continue_stype: MMType do return null
+
+ init(node: PNode)
+ do
+ _node = node
+ end
+end
+
+# specific EscapableBlock for closures
+class EscapableClosure
+special EscapableBlock
+ # The associated closure
+ readable attr _closure: MMClosure
+
+ redef meth is_break_block do return _closure.is_break
+
+ redef readable attr _break_list: Array[PExpr]
+
+ redef meth continue_stype do return _closure.signature.return_type
+
+ init(node: PNode, closure: MMClosure, break_list: Array[PExpr])
+ do
+ super(node)
+ _closure = closure
+ _break_list = break_list
+ end
+end
+
+###############################################################################
+
+class AEscapeExpr
+special PNode
+ # The associated escapable block
+ readable attr _escapable_block: EscapableBlock
+
+ # The name of the keyword
+ meth kwname: String is abstract
+
+ # Compute, set and return the _abelable_node value
+ meth compute_escapable_block(lctx: EscapableContext): EscapableBlock
+ do
+ var block: EscapableBlock
+ if lctx.is_empty then
+ lctx.visitor.error(self, "Syntax Error: '{kwname}' statment outside block.")
+ return null
+ end
+ block = lctx.head
+ _escapable_block = block
+ return block
+ end
+end
+
+redef class AContinueExpr
+special AEscapeExpr
+ redef meth kwname do return "continue"
+end
+
+redef class ABreakExpr
+special AEscapeExpr
+ redef meth kwname do return "break"
+end
+
package typing
import syntax_base
+import escape
redef class MMSrcModule
# Walk trough the module and type statments and expressions
# Current knowledge about variables names and types
readable writable attr _variable_ctx: VariableContext
+ # Current knowledge about escapable blocs
+ readable writable attr _escapable_ctx: EscapableContext = new EscapableContext(self)
+
# The current reciever
readable writable attr _self_var: ParamVariable
# Block of the current method
readable writable attr _top_block: PExpr
- # Current closure (if any)
- readable writable attr _closure: MMClosure
-
- # Current closure method return type (for break) (if any)
- readable writable attr _closure_break_stype: MMType = null
-
- # Current closure break expressions (if any)
- readable writable attr _break_list: Array[PExpr]
-
# List of explicit invocation of constructors of super-classes
readable writable attr _explicit_super_init_calls: Array[MMMethod]
end
end
-
###############################################################################
redef class PNode
end
redef class AClosureDecl
+ # The corresponding escapable object
+ readable attr _escapable: EscapableBlock
+
redef meth accept_typing(v)
do
# Register the closure for ClosureCallExpr
var old_var_ctx = v.variable_ctx
v.variable_ctx = v.variable_ctx.sub
- v.closure = variable.closure
+
+ _escapable = new EscapableClosure(self, variable.closure, null)
+ v.escapable_ctx.push(_escapable)
super
v.variable_ctx = old_var_ctx
- v.closure = null
+ v.escapable_ctx.pop
end
end
redef class AContinueExpr
redef meth after_typing(v)
do
- var c = v.closure
- var t: MMType = null
- if c != null then
- if c.is_break then
- v.error(self, "Error: 'continue' forbiden in break blocks.")
- return
- end
- t = c.signature.return_type
+ var esc = compute_escapable_block(v.escapable_ctx)
+ if esc == null then return
+
+ if esc.is_break_block then
+ v.error(self, "Error: 'continue' forbiden in break blocks.")
+ return
end
+ var t = esc.continue_stype
if n_expr == null and t != null then
v.error(self, "Error: continue with a value required in this bloc.")
else if n_expr != null and t == null then
redef class ABreakExpr
redef meth after_typing(v)
do
- var t = v.closure_break_stype
- if n_expr == null and t != null then
+ var esc = compute_escapable_block(v.escapable_ctx)
+ if esc == null then return
+
+ var bl = esc.break_list
+ if n_expr == null and bl != null then
v.error(self, "Error: break with a value required in this bloc.")
- else if n_expr != null and t == null then
+ else if n_expr != null and bl == null then
v.error(self, "Error: break without value required in this bloc.")
- else if n_expr != null and t != null then
+ else if n_expr != null and bl != null then
# Typing check can only be done later
- v.break_list.add(n_expr)
+ bl.add(n_expr)
end
end
end
end
redef class AWhileExpr
- redef meth after_typing(v)
+ # The corresponding escapable block
+ readable attr _escapable: EscapableBlock
+
+ redef meth accept_typing(v)
do
+ _escapable = new EscapableBlock(self)
+ v.escapable_ctx.push(_escapable)
+
+ super
+
v.check_conform_expr(n_expr, v.type_bool)
+ v.escapable_ctx.pop
end
end
redef class AForExpr
+ # The corresponding escapable block
+ readable attr _escapable: EscapableBlock
+
readable attr _meth_iterator: MMMethod
readable attr _meth_is_ok: MMMethod
readable attr _meth_item: MMMethod
readable attr _meth_next: MMMethod
redef meth accept_typing(v)
do
+ _escapable = new EscapableBlock(self)
+ v.escapable_ctx.push(_escapable)
+
v.variable_ctx = v.variable_ctx.sub
var va = new AutoVariable(n_id.to_symbol, self)
variable = va
var varctx = v.variable_ctx
assert varctx isa SubVariableContext
v.variable_ctx = varctx.prev
+
+ v.escapable_ctx.pop
end
end
else if cd.length > cs.length or cd.length < min_arity then
v.error(self, "Error: {name} requires {cs.length} blocs, {cd.length} found.")
else
- var old_bbst = v.closure_break_stype
- var old_bl = v.break_list
- v.closure_break_stype = t
- v.break_list = new Array[ABreakExpr]
+ # Initialize the break list if a value is required for breaks (ie. if the method is a function)
+ var break_list: Array[ABreakExpr] = null
+ if t != null then break_list = new Array[ABreakExpr]
+
+ # Process each closure definition
for i in [0..cd.length[ do
- cd[i].accept_typing2(v, cs[i])
+ var csi = cs[i]
+ var cdi = cd[i]
+ var esc = new EscapableClosure(cdi, csi, break_list)
+ v.escapable_ctx.push(esc)
+ cdi.accept_typing2(v, esc)
+ v.escapable_ctx.pop
end
- for n in v.break_list do
- var ntype = n.stype
- if t == null or (t != null and t < ntype) then
- t = ntype
+
+ # Check break type conformity
+ if break_list != null then
+ for n in break_list do
+ var ntype = n.stype
+ if t == null or (t != null and t < ntype) then
+ t = ntype
+ end
+ end
+ for n in break_list do
+ v.check_conform_expr(n, t)
end
end
- for n in v.break_list do
- v.check_conform_expr(n, t)
- end
-
- v.closure_break_stype = old_bbst
- v.break_list = old_bl
end
else if min_arity != 0 then
v.error(self, "Error: {name} requires {cs.length} blocs.")
end
end
end
+
super
end
-
+
redef meth closure_defs
do
if n_closure_defs == null or n_closure_defs.is_empty then
end
redef class PClosureDef
+ # The corresponding escapable object
+ readable attr _escapable: EscapableBlock
+
attr _accept_typing2: Bool
redef meth accept_typing(v)
do
if _accept_typing2 then super
end
- private meth accept_typing2(v: TypingVisitor, clos: MMClosure) is abstract
+ private meth accept_typing2(v: TypingVisitor, esc: EscapableClosure) is abstract
end
redef class AClosureDef
- redef meth accept_typing2(v, clos)
- do
- var sig = clos.signature
+ redef meth accept_typing2(v, esc)
+ do
+ _escapable = esc
+
+ var sig = esc.closure.signature
if sig.arity != n_id.length then
v.error(self, "Error: {sig.arity} automatic variable names expected, {n_id.length} found.")
return
end
- closure = clos
-
- var old_clos = v.closure
- v.closure = clos
+ closure = esc.closure
v.variable_ctx = v.variable_ctx.sub
variables = new Array[AutoVariable]
_accept_typing2 = true
accept_typing(v)
-
- v.closure = old_clos
end
end