From: Jean Privat Date: Tue, 9 Jun 2009 19:41:30 +0000 (-0400) Subject: syntax: New escape block management X-Git-Tag: v0.2.1~6 X-Git-Url: http://nitlanguage.org syntax: New escape block management 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 --- diff --git a/src/syntax/control_flow.nit b/src/syntax/control_flow.nit index 0903077..a4d2dae 100644 --- a/src/syntax/control_flow.nit +++ b/src/syntax/control_flow.nit @@ -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 index 0000000..2d04967 --- /dev/null +++ b/src/syntax/escape.nit @@ -0,0 +1,133 @@ +# This file is part of NIT ( http://www.nitlanguage.org ). +# +# Copyright 2008 Jean Privat +# +# 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 + diff --git a/src/syntax/typing.nit b/src/syntax/typing.nit index 6553885..0c099de 100644 --- a/src/syntax/typing.nit +++ b/src/syntax/typing.nit @@ -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