X-Git-Url: http://nitlanguage.org diff --git a/src/semantize/scope.nit b/src/semantize/scope.nit index 30aade4..de85c22 100644 --- a/src/semantize/scope.nit +++ b/src/semantize/scope.nit @@ -18,8 +18,10 @@ module scope import phase +import modelbuilder redef class ToolContext + # Run `APropdef::do_scope` on each propdef. var scope_phase: Phase = new ScopePhase(self, null) end @@ -32,13 +34,13 @@ 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 @@ -50,15 +52,12 @@ class EscapeMark # The name of the label (unless the mark is an anonymous loop mark) var name: nullable String - # Is the mark attached 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 = new Array[AContinueExpr] - - # Each 'break' attached to the mark - var breaks = new Array[ABreakExpr] + # Each break/continue attached to the mark + var escapes = new Array[AEscapeExpr] end # Visit a npropdef and: @@ -72,16 +71,18 @@ private class ScopeVisitor # The tool context used to display errors var toolcontext: ToolContext + # The analysed property + var propdef: APropdef + 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 = new List[Scope] + var scopes = new List[Scope] # Shift and check the last scope fun shift_scope @@ -102,7 +103,7 @@ private class ScopeVisitor 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 @@ -163,20 +164,21 @@ private class ScopeVisitor 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 @@ -192,7 +194,8 @@ private class ScopeVisitor 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.") + node.is_broken = true return null end return res @@ -200,7 +203,8 @@ private class ScopeVisitor 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}`.") + node.is_broken = true return null end return res @@ -211,7 +215,7 @@ private class ScopeVisitor return res end end - self.error(node, "Syntax Error: 'break' statement outside block.") + self.error(node, "Syntax Error: `break` statement outside block.") return null end end @@ -220,6 +224,7 @@ private class ScopeVisitor fun error(node: ANode, message: String) do self.toolcontext.error(node.hot_location, message) + node.is_broken = true end end @@ -246,10 +251,13 @@ redef class ANode end redef class APropdef + # The break escape mark associated with the return + var return_mark: nullable EscapeMark + # Entry point of the scope analysis fun do_scope(toolcontext: ToolContext) do - var v = new ScopeVisitor(toolcontext) + var v = new ScopeVisitor(toolcontext, self) v.enter_visit(self) v.shift_scope end @@ -260,6 +268,11 @@ redef class AParam var variable: nullable Variable redef fun accept_scope_visitor(v) do + if variable != null then + v.register_variable(self.n_id, variable.as(not null)) + return + end + super var nid = self.n_id var variable = new Variable(nid.text) @@ -303,10 +316,12 @@ redef class AContinueExpr 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 @@ -317,19 +332,36 @@ redef class ABreakExpr 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 AReturnExpr + redef fun accept_scope_visitor(v) + do + super + + var escapemark = v.propdef.return_mark + if escapemark == null then + escapemark = new EscapeMark + v.propdef.return_mark = escapemark + end + + 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) + v.enter_visit_block(n_catch) end end @@ -343,74 +375,120 @@ redef class AIfExpr 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 redef class AForExpr - # The automatic variables in order - var variables: nullable Array[Variable] + # The break escape mark associated with the 'for' + var break_mark: nullable EscapeMark - # The escape mark associated with the 'for' - var escapemark: nullable EscapeMark + # The continue escape mark associated with the 'for' + var continue_mark: nullable EscapeMark redef fun accept_scope_visitor(v) do - v.enter_visit(n_expr) + for g in n_groups do + v.enter_visit(g.n_expr) + end # Protect automatic variables v.scopes.unshift(new Scope) - # Create the automatic variables - var variables = new Array[Variable] - self.variables = variables - for nid in n_ids do - var va = new Variable(nid.text) - v.register_variable(nid, va) - variables.add(va) + for g in n_groups do + # Create the automatic variables + var variables = new Array[Variable] + g.variables = variables + for nid in g.n_ids do + var va = new Variable(nid.text) + v.register_variable(nid, va) + variables.add(va) + end 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 AForGroup + # The automatic variables in order + var variables: nullable Array[Variable] +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 redef fun accept_scope_visitor(v) do if n_expr isa AImplicitSelfExpr then - var name = n_id.text + var name = n_qid.n_id.text var variable = v.search_variable(name) 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.") + if not n_args.n_exprs.is_empty or n_args isa AParExprs or self isa ACallrefExpr then + v.error(self, "Error: `{name}` is a variable, not a method.") return end n = variable_create(variable) @@ -432,14 +510,14 @@ redef class ACallExpr redef fun variable_create(variable) do variable.warn_unread = false - return new AVarExpr.init_avarexpr(n_id) + return new AVarExpr.init_avarexpr(n_qid.n_id) end end redef class ACallAssignExpr redef fun variable_create(variable) do - return new AVarAssignExpr.init_avarassignexpr(n_id, n_assign, n_value) + return new AVarAssignExpr.init_avarassignexpr(n_qid.n_id, n_assign, n_value) end end @@ -447,6 +525,14 @@ redef class ACallReassignExpr redef fun variable_create(variable) do variable.warn_unread = false - return new AVarReassignExpr.init_avarreassignexpr(n_id, n_assign_op, n_value) + return new AVarReassignExpr.init_avarreassignexpr(n_qid.n_id, n_assign_op, n_value) + end +end + +redef class ALambdaExpr + redef fun accept_scope_visitor(v) + do + # TODO + return end end