Chromium Code Reviews| Index: frog/leg/resolver.dart |
| diff --git a/frog/leg/resolver.dart b/frog/leg/resolver.dart |
| index db1a755a6da2ba991350c1ddcbccdb723e8d0f04..b58c510cd7b80fe8aced274f7ea3ffbee6239e5c 100644 |
| --- a/frog/leg/resolver.dart |
| +++ b/frog/leg/resolver.dart |
| @@ -488,6 +488,19 @@ class LabeledStatementLabelScope implements LabelScope { |
| } |
| } |
|
ahe
2012/03/09 13:58:32
Remove extra line
Lasse Reichstein Nielsen
2012/03/12 13:05:23
Done.
|
| + |
| +class SwitchLabelScope implements LabelScope { |
| + final LabelScope outer; |
| + final Map<String, LabelElement> caseLabels; |
| + SwitchLabelScope(this.outer, this.caseLabels); |
| + LabelElement lookup(String labelName) { |
| + LabelElement result = caseLabels[labelName]; |
| + if (result !== null) return result; |
| + return outer.lookup(labelName); |
| + } |
| +} |
| + |
|
ahe
2012/03/09 13:58:32
Extra line.
Lasse Reichstein Nielsen
2012/03/12 13:05:23
Done.
|
| + |
| class EmptyLabelScope implements LabelScope { |
| const EmptyLabelScope(); |
| LabelElement lookup(String label) => null; |
| @@ -506,26 +519,43 @@ class StatementScope { |
| breakTargetStack = const EmptyLink<StatementElement>(), |
| continueTargetStack = const EmptyLink<StatementElement>(); |
| - LabelElement lookupLabel(String label) => labels.lookup(label); |
| + LabelElement lookupLabel(String label) => |
| + labels.lookup(label); |
| + |
| StatementElement currentBreakTarget() => |
| breakTargetStack.isEmpty() ? null : breakTargetStack.head; |
| + |
| StatementElement currentContinueTarget() => |
| continueTargetStack.isEmpty() ? null : continueTargetStack.head; |
| void enterLabelScope(LabelElement element) { |
| labels = new LabeledStatementLabelScope(labels, element); |
| } |
| + |
| void exitLabelScope() { |
| labels = labels.outer; |
| } |
| + |
| void enterLoop(StatementElement element) { |
| breakTargetStack = breakTargetStack.prepend(element); |
| continueTargetStack = continueTargetStack.prepend(element); |
| } |
| + |
| void exitLoop() { |
| breakTargetStack = breakTargetStack.tail; |
| continueTargetStack = continueTargetStack.tail; |
| } |
| + |
| + void enterSwitch(StatementElement breakElement, |
| + Map<String, LabelElement> continueElements) { |
| + breakTargetStack = breakTargetStack.prepend(breakElement); |
| + labels = new SwitchLabelScope(labels, continueElements); |
| + } |
| + |
| + void exitSwitch() { |
| + breakTargetStack = breakTargetStack.tail; |
| + labels = labels.outer; |
| + } |
| } |
| @@ -538,6 +568,8 @@ class ResolverVisitor extends CommonResolverVisitor<Element> { |
| bool typeRequired = false; |
| StatementScope statementScope; |
| int allowedCategory = ElementCategory.VARIABLE | ElementCategory.FUNCTION; |
| + // For generating locally unique ids. |
| + int sequence = 0; |
| ResolverVisitor(Compiler compiler, Element element) |
| : this.mapping = new TreeElementMapping(), |
| @@ -563,7 +595,13 @@ class ResolverVisitor extends CommonResolverVisitor<Element> { |
| StatementElement getOrCreateStatementElement(Node statement) { |
| StatementElement element = mapping[statement]; |
| if (element !== null) return element; |
| - element = new StatementElement(statement, enclosingElement); |
| + if (statement is SwitchStatement) { |
|
ahe
2012/03/09 13:58:32
statement.asSwitchStatement() !== null
Lasse Reichstein Nielsen
2012/03/12 13:05:23
Why use something that's harder to read and is pos
Lasse Reichstein Nielsen
2012/03/12 13:21:28
But I rewrote it anyway :)
|
| + element = new SwitchStatementElement(statement, |
| + enclosingElement, |
| + sequence++); |
| + } else { |
| + element = new StatementElement(statement, enclosingElement); |
| + } |
| mapping[statement] = element; |
| return element; |
| } |
| @@ -1037,6 +1075,10 @@ class ResolverVisitor extends CommonResolverVisitor<Element> { |
| return; |
| } |
| target = label.target; |
| + if (!target.statement.isValidBreakTarget()) { |
| + error(node.target, MessageKind.INVALID_BREAK, [labelName]); |
| + return; |
| + } |
| label.setBreakTarget(); |
| mapping[node.target] = label; |
| } |
| @@ -1125,20 +1167,58 @@ class ResolverVisitor extends CommonResolverVisitor<Element> { |
| visitSwitchStatement(SwitchStatement node) { |
| node.expression.accept(this); |
| - StatementElement element = getOrCreateStatementElement(node); |
| - statementScope.enterLoop(element); |
| + |
| + StatementElement breakElement = getOrCreateStatementElement(node); |
| + Map<String, LabelElement> continueLabels = <LabelElement>{}; |
| + for (SwitchCase switchCase in node.cases) { |
| + if (switchCase.label !== null) { |
| + Identifier labelIdentifier = switchCase.label; |
| + String labelName = labelIdentifier.source.slowToString(); |
| + |
| + LabelElement existingElement = continueLabels[labelName]; |
| + if (existingElement !== null) { |
| + // It's an error if the same label occurs twice in the same switch. |
| + warning(labelIdentifier, MessageKind.DUPLICATE_LABEL, [labelName]); |
| + error(existingElement.label, MessageKind.EXISTING_LABEL, [labelName]); |
| + } else { |
| + // It's only a warning if it shadows another label. |
| + existingElement = statementScope.lookupLabel(labelName); |
| + if (existingElement !== null) { |
| + warning(labelIdentifier, MessageKind.DUPLICATE_LABEL, [labelName]); |
| + warning(existingElement.label, |
| + MessageKind.EXISTING_LABEL, [labelName]); |
| + } |
| + } |
| + |
| + StatementElement statementElement = |
| + new StatementElement(switchCase, enclosingElement); |
| + mapping[switchCase] = statementElement; |
| + |
| + LabelElement label = |
| + new LabelElement(labelIdentifier, labelName, |
| + statementElement, enclosingElement); |
| + mapping[labelIdentifier] = label; |
| + continueLabels[labelName] = label; |
| + } |
| + } |
| + statementScope.enterSwitch(breakElement, continueLabels); |
| node.cases.accept(this); |
| - statementScope.exitLoop(); |
| + statementScope.exitSwitch(); |
| + |
| + // Clean-up unused labels |
| + continueLabels.forEach((String key, LabelElement label) { |
| + StatementElement statementElement = label.target; |
| + SwitchCase switchCase = statementElement.statement; |
| + if (!label.isContinueTarget) { |
| + mapping.remove(switchCase); |
| + mapping.remove(label.label); |
| + } |
| + }); |
| } |
| visitSwitchCase(SwitchCase node) { |
| - // TODO(ahe): What about the label? |
| - node.expression.accept(this); |
| - visitIn(node.statements, new BlockScope(context)); |
| - } |
| - |
| - visitDefaultCase(DefaultCase node) { |
| - // TODO(ahe): What about the label? |
| + // The label was handled in [visitSwitchStatement(SwitchStatement)]. |
| + node.expressions.accept(this); |
| visitIn(node.statements, new BlockScope(context)); |
| } |