syntax: merge ScopeContext and EscapeContext
authorJean Privat <jean@pryen.org>
Fri, 18 Jun 2010 18:53:07 +0000 (14:53 -0400)
committerJean Privat <jean@pryen.org>
Thu, 24 Jun 2010 06:11:27 +0000 (02:11 -0400)
and reorganize the related modules modules

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

src/syntax/flow.nit [moved from src/syntax/control_flow.nit with 78% similarity]
src/syntax/scope.nit [moved from src/syntax/escape.nit with 62% similarity]
src/syntax/typing.nit

similarity index 78%
rename from src/syntax/control_flow.nit
rename to src/syntax/flow.nit
index 7661039..102ed35 100644 (file)
 # limitations under the License.
 
 # Analysis control flow and variable visibility in property bodies, statements and expressions
-package control_flow
+package flow
 
 import syntax_base
 
-# Associate symbols to variable
-# Can be nested
-abstract class ScopeContext
-       # Look for the variable from its name
-       # Return null if nothing found
-       fun [](s: Symbol): nullable Variable
-       do
-               if _dico.has_key(s) then
-                       return _dico[s]
-               else
-                       return null
-               end
-       end
-
-       # Register a new variable with its name
-       fun add(v: Variable)
-       do
-               var old_var = self[v.name]
-               if old_var != null then
-                       _visitor.error(v.decl, "Error: '{v}' already defined at {old_var.decl.location.relative_to(v.decl.location)}.")
-               end
-               _dico[v.name] = v
-               _all_variables.add(v)
-       end
-
-       # Build a new ScopeContext
-       fun sub(node: ANode): ScopeContext
-       do
-               return new SubScopeContext.with_prev(self, node)
-       end
-
-       # Variables by name (in the current context only)
-       var _dico: Map[Symbol, Variable] = new HashMap[Symbol, Variable]
-
-       # All variables in all contextes
-       var _all_variables: Set[Variable]
-
-       # The visitor of the context (used to display error)
-       var _visitor: AbsSyntaxVisitor
-
-       # The syntax node that introduced the context
-       readable var _node: ANode
-
-       init(visitor: AbsSyntaxVisitor, node: ANode)
-       do
-               _visitor = visitor
-               _node = node
-       end
-end
-
-# Root of a variable scope context hierarchy
-class RootScopeContext
-special ScopeContext
-       init(visitor: AbsSyntaxVisitor, node: ANode)
-       do
-               super(visitor, node)
-               _all_variables = new HashSet[Variable]
-       end
-end
-
-# Contexts that can see local variables of a prevous context
-# Local variables added to this context are not shared with the previous context
-class SubScopeContext
-special ScopeContext
-       readable var _prev: ScopeContext
-
-       redef fun [](s)
-       do
-               if _dico.has_key(s) then
-                       return _dico[s]
-               else
-                       return prev[s]
-               end
-       end
-
-       init with_prev(p: ScopeContext, node: ANode)
-       do
-               init(p._visitor, node)
-               _prev = p
-               _all_variables = p._all_variables
-       end
-end
-
 #################################################################
 
 # All-in-one context for flow control.
similarity index 62%
rename from src/syntax/escape.nit
rename to src/syntax/scope.nit
index bd02187..2fa457a 100644 (file)
 # limitations under the License.
 
 # Manage nested escapable blocks (while, for and closure) and escape statements (break and continue)
-package escape
+package scope
 
 import syntax_base
-import control_flow
+import flow
 
-# Stack escapable blocks
-class EscapableContext
-       # Stack of blocks
-       var _stack: Array[EscapableBlock] = new Array[EscapableBlock]
+# All-in-one context for scoped things.
+# It features:
+# * variable and labels visibility
+# * control for 'break' and 'continue'
+# This class manage itself the entree and the exit of scopes:
+# * use push or push_escapable to enter in a new scope block
+# * use pop to remove the last scope block (do not foget do pop!)
+# Warning: ScopeContext are created empty: you must perform a first push to register variables
+class ScopeContext
+       # Stack of scope blocks
+       var _stack: Array[ScopeBlock] = new Array[ScopeBlock]
+
+       # Known variables
+       # (all variables, even out of scope ones)
+       # Used for debuging
+       var _variables: Array[Variable] = new Array[Variable]
 
        # 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
-       # Display error message if tere is a problem with the label
-       fun push(block: EscapableBlock, n_label: nullable ALabel)
+       # Return the variable associated with a name
+       fun [](n: Symbol): nullable Variable
+       do
+               var i = _stack.length - 1
+               while i >= 0 do
+                       var b = _stack[i]
+                       var va = b.get_variable(n)
+                       if va != null then return va
+                       i -= 1
+               end
+               return null
+       end
+
+       # Register a variable with its name in the current scope
+       # Display an error if name conflict
+       fun add_variable(v: Variable)
+       do
+               var old_var = self[v.name]
+               if old_var != null then
+                       _visitor.error(v.decl, "Error: '{v}' already defined at {old_var.decl.location.relative_to(v.decl.location)}.")
+               end
+               _stack.last.add_variable(v)
+               _variables.add(v)
+       end
+
+       # Push a simple unlabeled variable scope block
+       fun push(node: ANode)
+       do
+               var block = new ScopeBlock(node)
+               _stack.push(block)
+       end
+
+       # Push a specific escapable block
+       # Display error message if there is a problem with the label
+       fun push_escapable(block: EscapableBlock, n_label: nullable ALabel)
        do
                _stack.push(block)
                if n_label != null then
@@ -54,7 +98,7 @@ class EscapableContext
                var i = _stack.length - 1
                while i >= 0 do
                        var h = _stack[i]
-                       if not (h isa BreakOnlyEscapableBlock) then return h
+                       if h isa EscapableBlock and not (h isa BreakOnlyEscapableBlock) then return h
                        i -= 1
                end
                return null
@@ -65,11 +109,10 @@ class EscapableContext
        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
+                       if b isa EscapableBlock and b.lab == lab then return b
                        i -= 1
                end
                visitor.error(nl, "Syntax error: invalid label {lab}.")
@@ -91,14 +134,47 @@ end
 
 ###############################################################################
 
-# A escapable block correspond to a block statement where break and/or continue can by used
-# 'for' and 'while' use this class
-# 'do' uses the BreakOnlyEscapableBlock subclass
-# closures uses the EscapableClosure subclass
-class EscapableBlock
+# A single scope block. Thez are stacked in a ScopeContext
+# This block is used only to store local variables
+class ScopeBlock
        # The syntax node of the block
        readable var _node: ANode
 
+       # List of local variables of the block
+       # Lazily constructed since many blocks does not have local variables
+       var _dico: nullable HashMap[Symbol, Variable] = null
+
+       private fun add_variable(v: Variable)
+       do
+               var dico = _dico
+               if dico == null then
+                       dico = new HashMap[Symbol, Variable]
+                       _dico = dico
+               end
+               dico[v.name] = v
+       end
+
+       private fun get_variable(n: Symbol): nullable Variable
+       do
+               var dico = _dico
+               if dico == null then return null
+               if not dico.has_key(n) then return null
+               return dico[n]
+       end
+
+       init(node: ANode)
+       do
+               _node = node
+       end
+end
+
+# A escapable block correspond to a scope block where break and/or continue can by used.
+# EscapableBlocks can also be labeled.
+# 'for' and 'while' use this class
+# labeled 'do' uses the BreakOnlyEscapableBlock subclass
+# closures uses the EscapableClosure subclass
+class EscapableBlock
+special ScopeBlock
        # The label of the block (if any)
        # Set by the push in EscapableContext
        readable var _lab: nullable Symbol
@@ -119,7 +195,7 @@ class EscapableBlock
 
        init(node: ANode)
        do
-               _node = node
+               super(node)
        end
 end
 
@@ -162,7 +238,7 @@ special ALabelable
        fun kwname: String is abstract
 
        # Compute, set and return the associated escapable block
-       fun compute_escapable_block(lctx: EscapableContext): nullable EscapableBlock
+       fun compute_escapable_block(lctx: ScopeContext): nullable EscapableBlock
        do
                var block: nullable EscapableBlock
                var nl = n_label
index 5338a91..b7e324a 100644 (file)
@@ -18,8 +18,8 @@
 package typing
 
 import syntax_base
-import escape
-import control_flow
+import flow
+import scope
 
 redef class MMSrcModule
        # Walk trough the module and type statments and expressions
@@ -43,9 +43,8 @@ special AbsSyntaxVisitor
                if n != null then n.accept_typing(self)
        end
 
-       # Current knowledge about variables names and types
-       fun scope_ctx: ScopeContext do return _scope_ctx.as(not null)
-       writable var _scope_ctx: nullable ScopeContext
+       # Current knowledge about scoped things (variable, labels, etc.)
+       readable var _scope_ctx: ScopeContext = new ScopeContext(self)
 
        # Current knowledge about control flow
        fun flow_ctx: FlowContext do return _flow_ctx.as(not null)
@@ -68,19 +67,15 @@ special AbsSyntaxVisitor
        fun enter_visit_block(node: nullable AExpr)
        do
                if node == null then return
-               var old_scope_ctx = scope_ctx
-               scope_ctx = scope_ctx.sub(node)
+               scope_ctx.push(node)
                enter_visit(node)
-               scope_ctx = old_scope_ctx
+               scope_ctx.pop
        end
 
        # Non-bypassable knowledge about variables names and types
        fun base_flow_ctx: FlowContext do return _base_flow_ctx.as(not null)
        writable var _base_flow_ctx: nullable FlowContext
 
-       # Current knowledge about escapable blocks
-       readable writable var _escapable_ctx: EscapableContext = new EscapableContext(self)
-
        # The current reciever
        fun self_var: ParamVariable do return _self_var.as(not null)
        writable var _self_var: nullable ParamVariable
@@ -101,7 +96,7 @@ special AbsSyntaxVisitor
                if ctx != null then flow_ctx = ctx
        end
 
-       # Make the if_false_scope_ctx of the expression effective
+       # Make the if_false_flow_ctx of the expression effective
        private fun use_if_false_flow_ctx(e: AExpr)
        do
                var ctx = e.if_false_flow_ctx
@@ -181,7 +176,6 @@ end
 redef class AClassdef
        redef fun accept_typing(v)
        do
-               v.scope_ctx = new RootScopeContext(v, self)
                v.self_var = new ParamVariable("self".to_symbol, self)
                v.self_var.stype = local_class.get_type
                super
@@ -199,15 +193,13 @@ redef class AAttrPropdef
                v.flow_ctx = new RootFlowContext(v, self)
                v.base_flow_ctx = v.flow_ctx
 
-               var old_scope_ctx = v.scope_ctx
-               v.scope_ctx = old_scope_ctx.sub(self)
-
+               v.scope_ctx.push(self)
                _self_var = v.self_var
                super
                if n_expr != null then
                        v.check_conform_expr(n_expr.as(not null), prop.signature.return_type.as(not null))
                end
-               v.scope_ctx = old_scope_ctx
+               v.scope_ctx.pop
        end
 end
 
@@ -217,11 +209,10 @@ redef class AMethPropdef
                v.flow_ctx = new RootFlowContext(v, self)
                v.base_flow_ctx = v.flow_ctx
 
-               var old_scope_ctx = v.scope_ctx
-               v.scope_ctx = old_scope_ctx.sub(self)
+               v.scope_ctx.push(self)
                _self_var = v.self_var
                super
-               v.scope_ctx = old_scope_ctx
+               v.scope_ctx.pop
        end
 end
 
@@ -289,7 +280,7 @@ end
 redef class AParam
        redef fun after_typing(v)
        do
-               v.scope_ctx.add(variable)
+               v.scope_ctx.add_variable(variable)
        end
 end
 
@@ -300,20 +291,18 @@ redef class AClosureDecl
        redef fun accept_typing(v)
        do
                # Register the closure for ClosureCallExpr
-               v.scope_ctx.add(variable)
+               v.scope_ctx.add_variable(variable)
 
-               var old_scope_ctx = v.scope_ctx
                var old_flow_ctx = v.flow_ctx
                var old_base_flow_ctx = v.base_flow_ctx
                v.base_flow_ctx = v.flow_ctx
-               v.scope_ctx = v.scope_ctx.sub(self)
 
                var blist: nullable Array[AExpr] = null
                var t = v.local_property.signature.return_type
                if t != null then blist = new Array[AExpr]
                var escapable = new EscapableClosure(self, variable.closure, blist)
                _escapable = escapable
-               v.escapable_ctx.push(escapable, null)
+               v.scope_ctx.push_escapable(escapable, null)
 
                v.is_default_closure_definition = true
 
@@ -334,10 +323,9 @@ redef class AClosureDecl
                        v.check_conform_expr(x, t)
                end
 
-               v.scope_ctx = old_scope_ctx
                v.flow_ctx = old_flow_ctx
                v.base_flow_ctx = old_base_flow_ctx
-               v.escapable_ctx.pop
+               v.scope_ctx.pop
        end
 end
 
@@ -399,7 +387,7 @@ redef class AVardeclExpr
        do
                var va = new VarVariable(n_id.to_symbol, n_id)
                _variable = va
-               v.scope_ctx.add(va)
+               v.scope_ctx.add_variable(va)
                var ne = n_expr
                if ne != null then v.mark_is_set(va)
 
@@ -462,7 +450,7 @@ redef class AContinueExpr
        redef fun after_typing(v)
        do
                v.mark_unreash(self)
-               var esc = compute_escapable_block(v.escapable_ctx)
+               var esc = compute_escapable_block(v.scope_ctx)
                if esc == null then return
 
                if esc.is_break_block then
@@ -487,7 +475,7 @@ redef class ABreakExpr
        do
                var old_flow_ctx = v.flow_ctx
                v.mark_unreash(self)
-               var esc = compute_escapable_block(v.escapable_ctx)
+               var esc = compute_escapable_block(v.scope_ctx)
                if esc == null then return
 
                esc.break_flow_contexts.add(old_flow_ctx)
@@ -524,7 +512,7 @@ special AExpr
        do
                # Register the escapable block
                _escapable = escapable
-               v.escapable_ctx.push(escapable, n_label)
+               v.scope_ctx.push_escapable(escapable, n_label)
 
                # Save an prepare the contextes
                var old_flow_ctx = v.flow_ctx
@@ -548,7 +536,7 @@ special AExpr
                end
 
                if is_loop then v.base_flow_ctx = old_base_flow_ctx
-               v.escapable_ctx.pop
+               v.scope_ctx.pop
                _is_typed = true
        end
 
@@ -659,14 +647,13 @@ special AAbsControl
 
        redef fun process_control_inside(v)
        do
-               var old_scope_ctx = v.scope_ctx
-               v.scope_ctx = v.scope_ctx.sub(self)
+               v.scope_ctx.push(self)
                var old_flow_ctx = v.flow_ctx
 
                # Create the automatic variable
                var va = new AutoVariable(n_id.to_symbol, n_id)
                _variable = va
-               v.scope_ctx.add(va)
+               v.scope_ctx.add_variable(va)
 
                # Process collection
                v.enter_visit(n_expr)
@@ -687,7 +674,7 @@ special AAbsControl
 
                # end == begin of the loop
                v.flow_ctx = old_flow_ctx
-               v.scope_ctx = old_scope_ctx
+               v.scope_ctx.pop
        end
 end
 
@@ -1297,9 +1284,9 @@ redef class AAbsAbsSendExpr
                                        var csi = psig.closure_named(cni)
                                        if csi != null then
                                                var esc = new EscapableClosure(cdi, csi, break_list)
-                                               v.escapable_ctx.push(esc, n_label)
+                                               v.scope_ctx.push_escapable(esc, n_label)
                                                cdi.accept_typing2(v, esc)
-                                               v.escapable_ctx.pop
+                                               v.scope_ctx.pop
                                        else if cs.length == 1 then
                                                v.error(cdi.n_id, "Error: no closure named '!{cni}' in {name}; only closure is !{cs.first.name}.")
                                        else
@@ -1800,17 +1787,16 @@ redef class AClosureDef
 
                _closure = esc.closure
 
-               var old_scope_ctx = v.scope_ctx
+               v.scope_ctx.push(self)
                var old_flow_ctx = v.flow_ctx
                var old_base_flow_ctx = v.base_flow_ctx
                v.base_flow_ctx = v.flow_ctx
-               v.scope_ctx = v.scope_ctx.sub(self)
                variables = new Array[AutoVariable]
                for i in [0..n_ids.length[ do
                        var va = new AutoVariable(n_ids[i].to_symbol, n_ids[i])
                        variables.add(va)
                        va.stype = sig[i]
-                       v.scope_ctx.add(va)
+                       v.scope_ctx.add_variable(va)
                end
 
                _accept_typing2 = true
@@ -1823,9 +1809,9 @@ redef class AClosureDef
                                v.error(self, "Control error: Reached end of break block (a 'break' with a value was expected).")
                        end
                end
-               v.scope_ctx = old_scope_ctx
                v.flow_ctx = old_flow_ctx
                v.base_flow_ctx = old_base_flow_ctx
+               v.scope_ctx.pop
        end
 end