# See the License for the specific language governing permissions and
# limitations under the License.
-# Identification and scping of local variables and labels.
+# Identification and scoping of local variables and labels.
module scope
import phase
redef class ToolContext
+ # Run `APropdef::do_scope` on each propdef.
var scope_phase: Phase = new ScopePhase(self, null)
end
# A local variable (including parameters, automatic variables and self)
class Variable
# The name of the variable (as used in the program)
- var name: String
+ var name: String is writable
# Alias of `name`
redef fun to_s do return self.name
# The declaration of the variable, if any
- var location: nullable Location = null
+ var location: nullable Location = null is writable
# Is the local variable not read and need a warning?
var warn_unread = false is writable
# The name of the label (unless the mark is an anonymous loop mark)
var name: nullable String
- # Is the mark atached to a loop (loop, while, for)
- # Such a mark is a candidate to a labelless 'continue' or 'break'
- var for_loop: Bool
+ # The associated `continue` mark, if any.
+ # If the mark attached to a loop (loop, while, for), a distinct mark is used.
+ private var continue_mark: nullable EscapeMark = null
- # Each 'continue' attached to the mark
- var continues: Array[AContinueExpr] = new Array[AContinueExpr]
-
- # Each 'break' attached to the mark
- var breaks: Array[ABreakExpr] = new Array[ABreakExpr]
+ # Each break/continue attached to the mark
+ var escapes = new Array[AEscapeExpr]
end
# Visit a npropdef and:
# The tool context used to display errors
var toolcontext: ToolContext
- var selfvariable: Variable = new Variable("self")
+ var selfvariable = new Variable("self")
- init(toolcontext: ToolContext)
+ init
do
- self.toolcontext = toolcontext
scopes.add(new Scope)
end
# All stacked scope. `scopes.first` is the current scope
- private var scopes: List[Scope] = new List[Scope]
+ var scopes = new List[Scope]
# Shift and check the last scope
fun shift_scope
end
end
- # Regiter a local variable.
+ # Register a local variable.
# Display an error on toolcontext if a variable with the same name is masked.
fun register_variable(node: ANode, variable: Variable): Bool
do
var name = variable.name
var found = search_variable(name)
if found != null then
- self.error(node, "Error: A variable named `{name}' already exists")
+ self.error(node, "Error: a variable named `{name}` already exists.")
return false
end
scopes.first.variables[name] = variable
# Enter in a statement block `node` as inside a new scope.
# The block can be optionally attached to an `escapemark`.
- private fun enter_visit_block(node: nullable AExpr, escapemark: nullable EscapeMark)
+ fun enter_visit_block(node: nullable AExpr, escapemark: nullable EscapeMark)
do
if node == null then return
var scope = new Scope
end
# Look for a label `name`.
- # Return nulll if no such a label is found.
- private fun search_label(name: String): nullable EscapeMark
+ # Return null if no such a label is found.
+ fun search_label(name: String): nullable EscapeMark
do
for scope in scopes do
var res = scope.escapemark
# Create a new escape mark (possibly with a label)
# Display an error on toolcontext if a label with the same name is masked.
- private fun make_escape_mark(nlabel: nullable ALabel, for_loop: Bool): EscapeMark
+ fun make_escape_mark(nlabel: nullable ALabel, for_loop: Bool): EscapeMark
do
var name: nullable String
if nlabel != null then
if nid == null then
var res = search_label("")
if res != null then
- self.error(nlabel, "Syntax error: anonymous label already defined.")
+ self.error(nlabel, "Syntax Error: anonymous label already defined.")
end
name = ""
else
name = nid.text
var found = self.search_label(name)
if found != null then
- self.error(nlabel, "Syntax error: label {name} already defined.")
+ self.error(nlabel, "Syntax Error: label `{name}` already defined.")
end
end
else
name = null
end
- var res = new EscapeMark(name, for_loop)
+ var res = new EscapeMark(name)
+ if for_loop then res.continue_mark = new EscapeMark(name)
return res
end
# Look for an escape mark optionally associated with a label.
- # If a label is given, the the escapemark of this label is returned.
+ # If a label is given, the escapemark of this label is returned.
# If there is no label, the nearest escapemark that is `for loop` is returned.
# If there is no valid escapemark, then an error is displayed ans null is returned.
- # Return nulll if no such a label is found.
- private fun get_escapemark(node: ANode, nlabel: nullable ALabel): nullable EscapeMark
+ # Return null if no such a label is found.
+ fun get_escapemark(node: ANode, nlabel: nullable ALabel): nullable EscapeMark
do
if nlabel != null then
var nid = nlabel.n_id
if nid == null then
var res = search_label("")
if res == null then
- self.error(nlabel, "Syntax error: invalid anonymous label.")
+ self.error(nlabel, "Syntax Error: invalid anonymous label.")
return null
end
return res
var name = nid.text
var res = search_label(name)
if res == null then
- self.error(nlabel, "Syntax error: invalid label {name}.")
+ self.error(nlabel, "Syntax Error: invalid label `{name}`.")
return null
end
return res
return res
end
end
- self.error(node, "Syntax Error: 'break' statment outside block.")
+ self.error(node, "Syntax Error: `break` statement outside block.")
return null
end
end
# Display an error
- private fun error(node: ANode, message: String)
+ fun error(node: ANode, message: String)
do
self.toolcontext.error(node.hot_location, message)
end
end
private class Scope
- var variables: HashMap[String, Variable] = new HashMap[String, Variable]
+ var variables = new HashMap[String, Variable]
var escapemark: nullable EscapeMark = null
end
redef class ASelfExpr
- # The variable associated with the self reciever
+ # The variable associated with the self receiver
var variable: nullable Variable
redef fun accept_scope_visitor(v)
do
end
end
-redef class AContinueExpr
- # The escape mark associated with the continue
+redef class AEscapeExpr
+ # The escape mark associated with the break/continue
var escapemark: nullable EscapeMark
+end
+
+redef class AContinueExpr
redef fun accept_scope_visitor(v)
do
super
var escapemark = v.get_escapemark(self, self.n_label)
if escapemark == null then return # Skip error
- if not escapemark.for_loop then
+ escapemark = escapemark.continue_mark
+ if escapemark == null then
v.error(self, "Error: cannot 'continue', only 'break'.")
+ return
end
- escapemark.continues.add(self)
+ escapemark.escapes.add(self)
self.escapemark = escapemark
end
end
redef class ABreakExpr
- # The escape mark associated with the break
- var escapemark: nullable EscapeMark
redef fun accept_scope_visitor(v)
do
super
var escapemark = v.get_escapemark(self, self.n_label)
if escapemark == null then return # Skip error
- escapemark.breaks.add(self)
+ escapemark.escapes.add(self)
self.escapemark = escapemark
end
end
redef class ADoExpr
- # The escape mark associated with the 'do' block
- var escapemark: nullable EscapeMark
+ # The break escape mark associated with the 'do' block
+ var break_mark: nullable EscapeMark
+
redef fun accept_scope_visitor(v)
do
- self.escapemark = v.make_escape_mark(n_label, false)
- v.enter_visit_block(n_block, self.escapemark)
+ self.break_mark = v.make_escape_mark(n_label, false)
+ v.enter_visit_block(n_block, self.break_mark)
end
end
end
redef class AWhileExpr
- # The escape mark associated with the 'while'
- var escapemark: nullable EscapeMark
+ # The break escape mark associated with the 'while'
+ var break_mark: nullable EscapeMark
+
+ # The continue escape mark associated with the 'while'
+ var continue_mark: nullable EscapeMark
+
redef fun accept_scope_visitor(v)
do
var escapemark = v.make_escape_mark(n_label, true)
- self.escapemark = escapemark
+ self.break_mark = escapemark
+ self.continue_mark = escapemark.continue_mark
v.enter_visit(n_expr)
v.enter_visit_block(n_block, escapemark)
end
end
redef class ALoopExpr
- # The escape mark associated with the 'loop'
- var escapemark: nullable EscapeMark
+ # The break escape mark associated with the 'loop'
+ var break_mark: nullable EscapeMark
+
+ # The continue escape mark associated with the 'loop'
+ var continue_mark: nullable EscapeMark
+
redef fun accept_scope_visitor(v)
do
var escapemark = v.make_escape_mark(n_label, true)
- self.escapemark = escapemark
+ self.break_mark = escapemark
+ self.continue_mark = escapemark.continue_mark
v.enter_visit_block(n_block, escapemark)
end
end
# The automatic variables in order
var variables: nullable Array[Variable]
- # The escape mark associated with the 'for'
- var escapemark: nullable EscapeMark
+ # The break escape mark associated with the 'for'
+ var break_mark: nullable EscapeMark
+
+ # The continue escape mark associated with the 'for'
+ var continue_mark: nullable EscapeMark
redef fun accept_scope_visitor(v)
do
end
var escapemark = v.make_escape_mark(n_label, true)
- self.escapemark = escapemark
+ self.break_mark = escapemark
+ self.continue_mark = escapemark.continue_mark
v.enter_visit_block(n_block, escapemark)
v.shift_scope
end
end
+redef class AWithExpr
+ # The break escape mark associated with the 'with'
+ var break_mark: nullable EscapeMark
+
+ redef fun accept_scope_visitor(v)
+ do
+ v.scopes.unshift(new Scope)
+
+ var escapemark = v.make_escape_mark(n_label, true)
+ self.break_mark = escapemark
+
+ v.enter_visit(n_expr)
+ v.enter_visit_block(n_block, escapemark)
+
+ v.shift_scope
+ end
+end
+
+redef class AAssertExpr
+ redef fun accept_scope_visitor(v)
+ do
+ v.enter_visit(n_expr)
+ v.enter_visit_block(n_else, null)
+ end
+end
+
redef class AVarFormExpr
# The associated variable
- var variable: nullable Variable
+ var variable: nullable Variable is writable
end
redef class ACallFormExpr
if variable != null then
var n: AExpr
if not n_args.n_exprs.is_empty or n_args isa AParExprs then
- v.error(self, "Error: {name} is variable, not a function.")
+ v.error(self, "Error: `{name}` is a variable, not a method.")
return
end
n = variable_create(variable)
super
end
- # Create a variable acces corresponding to the call form
+ # Create a variable access corresponding to the call form
private fun variable_create(variable: Variable): AVarFormExpr is abstract
end