syntax: New escape block management
authorJean Privat <jean@pryen.org>
Tue, 9 Jun 2009 19:41:30 +0000 (15:41 -0400)
committerJean Privat <jean@pryen.org>
Wed, 10 Jun 2009 14:04:41 +0000 (10:04 -0400)
New module syntax/escape.nit to handle escape context and structures.
Use it in syntax/typing.nit (thus removing many visitor attributes).
Remove duplicate break and continue errors from syntax/control_flow.nit now in escape.nit.
Remove useless ABlockControler & cie. from syntax/control_flow.nit.

Signed-off-by: Jean Privat <jean@pryen.org>

src/syntax/control_flow.nit
src/syntax/escape.nit [new file with mode: 0644]
src/syntax/typing.nit

index 0903077..a4d2dae 100644 (file)
@@ -88,9 +88,6 @@ private class ControlFlowContext
        # 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]
 
@@ -114,7 +111,6 @@ private class ControlFlowContext
                _prev = p
                _unreash = p.unreash
                _already_unreash = p.already_unreash
-               _base_block = p.base_block
        end
 end
 
@@ -174,36 +170,17 @@ redef class AReturnExpr
        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
@@ -268,9 +245,6 @@ special PExpr
                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
@@ -322,8 +296,6 @@ redef class AClosureDecl
                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
 
diff --git a/src/syntax/escape.nit b/src/syntax/escape.nit
new file mode 100644 (file)
index 0000000..2d04967
--- /dev/null
@@ -0,0 +1,133 @@
+# 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
+
index 6553885..0c099de 100644 (file)
@@ -18,6 +18,7 @@
 package typing
 
 import syntax_base
+import escape
 
 redef class MMSrcModule
        # Walk trough the module and type statments and expressions
@@ -44,21 +45,15 @@ special AbsSyntaxVisitor
        # 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]
 
@@ -198,7 +193,6 @@ special VariableContext
        end
 end
 
-
 ###############################################################################
 
 redef class PNode
@@ -298,6 +292,9 @@ redef class PParam
 end
 
 redef class AClosureDecl
+       # The corresponding escapable object
+       readable attr _escapable: EscapableBlock
+
        redef meth accept_typing(v)
        do
                # Register the closure for ClosureCallExpr
@@ -305,12 +302,14 @@ redef class AClosureDecl
 
                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
 
@@ -386,16 +385,15 @@ 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
@@ -409,14 +407,17 @@ end
 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
@@ -444,19 +445,34 @@ redef class AIfExpr
 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
@@ -499,6 +515,8 @@ redef class AForExpr
                var varctx = v.variable_ctx 
                assert varctx isa SubVariableContext
                v.variable_ctx = varctx.prev
+
+               v.escapable_ctx.pop
        end
 end
 
@@ -1014,25 +1032,32 @@ special PExpr
                        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.")
@@ -1263,9 +1288,10 @@ redef class ACallFormExpr
                                end
                        end
                end
+
                super
        end
-       
+
        redef meth closure_defs
        do
                if n_closure_defs == null or n_closure_defs.is_empty then
@@ -1357,6 +1383,9 @@ redef class AClosureCallExpr
 end
 
 redef class PClosureDef
+       # The corresponding escapable object
+       readable attr _escapable: EscapableBlock
+
        attr _accept_typing2: Bool
        redef meth accept_typing(v)
        do
@@ -1364,22 +1393,21 @@ redef class PClosureDef
                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]
@@ -1392,8 +1420,6 @@ redef class AClosureDef
 
                _accept_typing2 = true
                accept_typing(v)
-
-               v.closure = old_clos
        end
 end