public ValType CheckValueType(Node node, Scope inner, Scope outer) { switch (node.GetNodeType()) { case NodeType.Variable: VariableNode variableNode = node as VariableNode; if (!inner.variables.TryGetValue(variableNode.name, out VariableNode val)) { if (!outer.variables.TryGetValue(variableNode.name, out val)) { throw new SemanticException(SemanticErrorCode.UndeclaredVariable, "Variable \"" + variableNode.name + "\" is not declared.", node.Line); } else { variableNode.ValType = val.ValType; variableNode.LocalIndex = val.LocalIndex; } } else { variableNode.ValType = val.ValType; variableNode.LocalIndex = val.LocalIndex; } return(variableNode.ValType); case NodeType.Assign: AssignNode assignNode = node as AssignNode; ValType left = CheckValueType(assignNode.left, inner, outer); ValType right = CheckValueType(assignNode.right, inner, outer); if (assignNode.left.ValType != right && !(assignNode.left.ValType == ValType.Double && right == ValType.Int)) { throw new SemanticException(SemanticErrorCode.IllegalCast, "Cannot cast " + Enum.GetName(typeof(ValType), right) + " to " + Enum.GetName(typeof(ValType), assignNode.left.ValType) + ".", assignNode.right.Line); } if (right == ValType.Int && assignNode.left.ValType == ValType.Double) { DoubleCastNode dcn = new DoubleCastNode(assignNode.right.Line); dcn.content = assignNode.right; assignNode.right = dcn; } assignNode.ValType = left; return(left); case NodeType.Int: return(ValType.Int); case NodeType.Double: return(ValType.Double); case NodeType.Bool: return(ValType.Bool); case NodeType.BinaryOp: BinaryOpNode binaryOpNode = node as BinaryOpNode; ValType l = CheckValueType(binaryOpNode.left, inner, outer); ValType r = CheckValueType(binaryOpNode.right, inner, outer); int v = (int)l * (int)r; if (binaryOpNode.type == BinaryOpType.BitAnd || binaryOpNode.type == BinaryOpType.BitOr) { if (v != 1) { throw new SemanticException(SemanticErrorCode.IllegalCast, "Expected Int value.", node.Line); } return(ValType.Int); } else { if (v > 1 && v < 4) { if (l == ValType.Double) { DoubleCastNode dcn = new DoubleCastNode(binaryOpNode.right.Line); dcn.content = binaryOpNode.right; binaryOpNode.right = dcn; } else { DoubleCastNode dcn = new DoubleCastNode(binaryOpNode.left.Line); dcn.content = binaryOpNode.left; binaryOpNode.left = dcn; } binaryOpNode.ValType = ValType.Double; return(ValType.Double); } else if (l == ValType.Bool || r == ValType.Bool) { throw new SemanticException(SemanticErrorCode.IllegalCast, "Expected Int or Double.", binaryOpNode.Line); } } binaryOpNode.ValType = l; return(l); case NodeType.LogicOp: LogicOpNode logicOpNode = node as LogicOpNode; left = CheckValueType(logicOpNode.left, inner, outer); right = CheckValueType(logicOpNode.right, inner, outer); if (left != ValType.Bool) { string typeString = Enum.GetName(typeof(ValType), left); throw new SemanticException(SemanticErrorCode.IllegalCast, "Expected Bool, but got " + typeString + ".", logicOpNode.left.Line); } if (right != ValType.Bool) { string typeString = Enum.GetName(typeof(ValType), right); throw new SemanticException(SemanticErrorCode.IllegalCast, "Expected Bool, but got " + typeString + ".", logicOpNode.right.Line); } logicOpNode.ValType = ValType.Bool; return(ValType.Bool); case NodeType.Comparison: ComparisonNode comparisonNode = node as ComparisonNode; left = CheckValueType(comparisonNode.left, inner, outer); right = CheckValueType(comparisonNode.right, inner, outer); if (left != right) { if (left == ValType.Bool || right == ValType.Bool) { throw new SemanticException(SemanticErrorCode.IllegalCast, "Comparison arguments are not the same type.", comparisonNode.Line); } else if (left == ValType.Int && right == ValType.Double) { DoubleCastNode dcn = new DoubleCastNode(comparisonNode.left.Line); dcn.content = comparisonNode.left; comparisonNode.left = dcn; } else if (right == ValType.Int && left == ValType.Double) { DoubleCastNode dcn = new DoubleCastNode(comparisonNode.right.Line); dcn.content = comparisonNode.right; comparisonNode.right = dcn; } } comparisonNode.ValType = ValType.Bool; return(ValType.Bool); case NodeType.Parenthesis: ParenthesisNode parenthesisNode = node as ParenthesisNode; ValType type = CheckValueType(parenthesisNode.content, inner, outer); parenthesisNode.ValType = type; return(type); case NodeType.IntCast: IntCastNode intCastNode = node as IntCastNode; CheckInScope(intCastNode.content, inner, outer); intCastNode.ValType = ValType.Int; return(ValType.Int); case NodeType.DoubleCast: DoubleCastNode doubleCastNode = node as DoubleCastNode; CheckInScope(doubleCastNode.content, inner, outer); doubleCastNode.ValType = ValType.Double; return(ValType.Double); case NodeType.Not: NotNode notNode = node as NotNode; type = CheckValueType(notNode.content, inner, outer); if (type != ValType.Bool) { string typeString = Enum.GetName(typeof(ValType), type); throw new SemanticException(SemanticErrorCode.IllegalCast, "Expected Bool, but got " + typeString + ".", notNode.content.Line); } notNode.ValType = ValType.Bool; return(ValType.Bool); case NodeType.Minus: MinusNode minusNode = node as MinusNode; type = CheckValueType(minusNode.content, inner, outer); if (type == ValType.Bool) { string typeString = Enum.GetName(typeof(ValType), type); throw new SemanticException(SemanticErrorCode.IllegalCast, "Expected Int or Double.", minusNode.content.Line); } minusNode.ValType = type; return(type); case NodeType.Neg: NegNode negNode = node as NegNode; type = CheckValueType(negNode.content, inner, outer); if (type != ValType.Int) { string typeString = Enum.GetName(typeof(ValType), type); throw new SemanticException(SemanticErrorCode.IllegalCast, "Expected Int, but got " + typeString + ".", negNode.content.Line); } negNode.ValType = type; return(ValType.Int); } return(ValType.None); }
public void CheckInScope(Node node, Scope inner, Scope outer) { switch (node.GetNodeType()) { case NodeType.Block: Scope newOuter = new Scope(outer); newOuter.AddScope(inner); GoDeeperInScope(node as BlockNode, newOuter); break; case NodeType.If: IfNode ifNode = node as IfNode; ValType type = CheckValueType(ifNode.check, inner, outer); if (type == ValType.Bool) { CheckInScope(ifNode.ifBlock, inner, outer); if (!(ifNode.elseBlock is null)) { CheckInScope(ifNode.elseBlock, inner, outer); } } else { string typeString = Enum.GetName(typeof(ValType), type); throw new SemanticException(SemanticErrorCode.IllegalCast, "Expected Bool, but got " + typeString + ".", node.Line); } break; case NodeType.While: WhileNode whileNode = node as WhileNode; type = CheckValueType(whileNode.check, inner, outer); if (type == ValType.Bool) { CheckInScope(whileNode.block, inner, outer); } else { string typeString = Enum.GetName(typeof(ValType), type); throw new SemanticException(SemanticErrorCode.IllegalCast, "Expected Bool, but got " + typeString + ".", node.Line); } break; case NodeType.Read: ReadNode readNode = node as ReadNode; CheckInScope(readNode.target, inner, outer); break; case NodeType.Write: WriteNode writeNode = node as WriteNode; if (!(writeNode.content is StringNode)) { CheckInScope(writeNode.content, inner, outer); } break; case NodeType.Variable: VariableNode variableNode = node as VariableNode; CheckValueType(variableNode, inner, outer); break; case NodeType.Init: InitNode initNode = node as InitNode; if (inner.variables.ContainsKey(initNode.variable.name)) { throw new SemanticException(SemanticErrorCode.VariableAlreadyDeclared, "Variable \"" + initNode.variable.name + "\" already declared in scope", initNode.variable.Line); } else { inner.variables.Add(initNode.variable.name, initNode.variable); } initNode.variable.LocalIndex = locals.Count; locals.Add(new LocalVariable { Name = initNode.variable.name, Type = initNode.variable.ValType }); break; case NodeType.Assign: AssignNode assignNode = node as AssignNode; CheckValueType(assignNode, inner, outer); break; case NodeType.BinaryOp: BinaryOpNode binaryOpNode = node as BinaryOpNode; CheckValueType(binaryOpNode, inner, outer); break; case NodeType.Comparison: ComparisonNode comparisonNode = node as ComparisonNode; CheckValueType(comparisonNode, inner, outer); break; case NodeType.Parenthesis: ParenthesisNode parenthesisNode = node as ParenthesisNode; CheckInScope(parenthesisNode.content, inner, outer); break; case NodeType.Int: case NodeType.Double: case NodeType.Bool: case NodeType.String: break; case NodeType.LogicOp: LogicOpNode logicNode = node as LogicOpNode; CheckValueType(logicNode, inner, outer); break; case NodeType.IntCast: IntCastNode intCastNode = node as IntCastNode; CheckValueType(intCastNode, inner, outer); break; case NodeType.DoubleCast: DoubleCastNode doubleCastNode = node as DoubleCastNode; CheckValueType(doubleCastNode, inner, outer); break; case NodeType.Not: NotNode notNode = node as NotNode; CheckValueType(notNode, inner, outer); break; case NodeType.Minus: MinusNode minusNode = node as MinusNode; CheckValueType(minusNode, inner, outer); break; case NodeType.Neg: NegNode negNode = node as NegNode; CheckValueType(negNode, inner, outer); break; } }