syntax: handle labels for for, while and closures
[nit.git] / src / syntax / escape.nit
index ced1bf5..9cc40a7 100644 (file)
@@ -22,30 +22,63 @@ import syntax_base
 # Stack escapable blocks
 class EscapableContext
        # Stack of blocks
-       attr _stack: Array[EscapableBlock] = new Array[EscapableBlock]
+       var _stack: Array[EscapableBlock] = new Array[EscapableBlock]
+
+       # Known labels
+       # (all labels, even out of scopes ones)
+       # Used to find duplicates
+       var _labels: Array[ALabel] = new Array[ALabel]
 
        # Push a new escapable block
-       meth push(block: EscapableBlock)
+       # Display error message if tere is a problem with the label
+       fun push(block: EscapableBlock, n_label: nullable ALabel)
        do
                _stack.push(block)
+               if n_label != null then
+                       var lab = n_label.n_id.to_symbol
+                       for nl in _labels do
+                               if n_label != nl and lab == nl.n_id.to_symbol then
+                                       visitor.error(n_label, "Syntax error: label {lab} already defined at {nl.location.relative_to(n_label.location)}.")
+                                       return
+                               end
+                       end
+                       _labels.add(n_label)
+                       block._lab = lab
+               end
        end
 
        # Is there no block in the stack?
-       meth is_empty: Bool do return _stack.is_empty
+       fun is_empty: Bool do return _stack.is_empty
 
        # Return the current block (the last stacked)
-       meth head: EscapableBlock
+       fun head: EscapableBlock
        do
                return _stack.last
        end
 
+       # Return the block associed to a label
+       # Output an error end return null if the label is not known
+       fun get_by_label(nl: ALabel): nullable EscapableBlock
+       do
+               var i = _stack.length - 1
+               var block: nullable EscapableBlock = null
+               var lab = nl.n_id.to_symbol
+               while i >= 0 do
+                       var b = _stack[i]
+                       if b.lab == lab then return b
+                       i -= 1
+               end
+               visitor.error(nl, "Syntax error: invalid label {lab}.")
+               return null
+       end
+
        # Remove the last block (the last stacked)
-       meth pop
+       fun pop
        do
                var n = _stack.pop
        end
 
-       readable attr _visitor: AbsSyntaxVisitor
+       readable var _visitor: AbsSyntaxVisitor
        init (v: AbsSyntaxVisitor)
        do
                _visitor = v
@@ -58,20 +91,24 @@ end
 # For and while use this class. closures uses the EscapableClosure subclass.
 class EscapableBlock
        # The syntax node of the block
-       readable attr _node: PNode
+       readable var _node: ANode
+
+       # The label of the block (if any)
+       # Set by the push in EscapableContext
+       readable var _lab: nullable Symbol
 
        # Is self a break closure ?
-       meth is_break_block: Bool do return false
+       fun 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: nullable Array[PExpr] do return null
+       fun break_list: nullable Array[AExpr] do return null
 
        # The static type required by the continue statement (if any)
-       meth continue_stype: nullable MMType do return null
+       fun continue_stype: nullable MMType do return null
 
-       init(node: PNode)
+       init(node: ANode)
        do
                _node = node
        end
@@ -81,15 +118,15 @@ end
 class EscapableClosure
 special EscapableBlock
        # The associated closure
-       readable attr _closure: MMClosure
+       readable var _closure: MMClosure
 
-       redef meth is_break_block do return _closure.is_break
+       redef fun is_break_block do return _closure.is_break
 
-       redef readable attr _break_list: nullable Array[PExpr]
+       redef readable var _break_list: nullable Array[AExpr]
 
-       redef meth continue_stype do return _closure.signature.return_type
+       redef fun continue_stype do return _closure.signature.return_type
 
-       init(node: PNode, closure: MMClosure, break_list: nullable Array[PExpr])
+       init(node: ANode, closure: MMClosure, break_list: nullable Array[AExpr])
        do
                super(node)
                _closure = closure
@@ -100,34 +137,38 @@ end
 ###############################################################################
 
 class AEscapeExpr
-special PNode
+special ALabelable
        # The associated escapable block
-       readable attr _escapable_block: nullable EscapableBlock
+       readable var _escapable: nullable EscapableBlock
 
        # The name of the keyword
-       meth kwname: String is abstract
+       fun kwname: String is abstract
 
-       # Compute, set and return the _abelable_node value
-       meth compute_escapable_block(lctx: EscapableContext): nullable EscapableBlock
+       # Compute, set and return the associated escapable block
+       fun compute_escapable_block(lctx: EscapableContext): nullable EscapableBlock
        do
-               var block: EscapableBlock
-               if lctx.is_empty then
+               var block: nullable EscapableBlock
+               var nl = n_label
+               if nl != null then
+                       block = lctx.get_by_label(nl)
+               else if lctx.is_empty then
                        lctx.visitor.error(self, "Syntax Error: '{kwname}' statment outside block.")
                        return null
+               else
+                       block = lctx.head
                end
-               block = lctx.head
-               _escapable_block = block
+               _escapable = block
                return block
        end
 end
 
 redef class AContinueExpr
 special AEscapeExpr
-       redef meth kwname do return "continue"
+       redef fun kwname do return "continue"
 end
 
 redef class ABreakExpr
 special AEscapeExpr
-       redef meth kwname do return "break"
+       redef fun kwname do return "break"
 end