Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

removed char, added type checking and overload resolution

  • Loading branch information...
commit d47a374aed287459088daa753233ce330fdc8039 1 parent 7b8a3f9
@evanw authored
View
7 InteractiveServer.cs
@@ -126,8 +126,8 @@ private static string Compile(string input)
if (log.errors.Count == 0) {
module = Parser.Parse(log, tokens, "<stdin>");
if (module != null) {
- appendText(doc, xmlTree, module.Accept(new NodeToStringVisitor()));
Compiler.Compile(log, module);
+ appendText(doc, xmlTree, module.Accept(new NodeToStringVisitor()));
}
}
} catch (Exception e) {
@@ -303,11 +303,6 @@ public override string Visit(FloatExpr node)
return "FloatExpr { value = " + node.value + " }";
}
- public override string Visit(CharExpr node)
- {
- return "CharExpr { value = " + node.value.ToQuotedChar() + " }";
- }
-
public override string Visit(StringExpr node)
{
return "StringExpr { value = " + node.value.ToQuotedString() + " }";
View
18 Nodes.cs
@@ -175,16 +175,6 @@ public override T Accept<T>(Visitor<T> visitor)
}
}
-public class CharExpr : Expr
-{
- public int value;
-
- public override T Accept<T>(Visitor<T> visitor)
- {
- return visitor.Visit(this);
- }
-}
-
public class StringExpr : Expr
{
public string value;
@@ -290,6 +280,7 @@ public class MemberExpr : Expr
{
public Expr obj;
public string name;
+ public Symbol symbol;
public override T Accept<T>(Visitor<T> visitor)
{
@@ -334,8 +325,6 @@ public abstract class Visitor<T>
public abstract T Visit(FloatExpr node);
- public abstract T Visit(CharExpr node);
-
public abstract T Visit(StringExpr node);
public abstract T Visit(IdentExpr node);
@@ -457,11 +446,6 @@ public override Null Visit(FloatExpr node)
return null;
}
- public override Null Visit(CharExpr node)
- {
- return null;
- }
-
public override Null Visit(StringExpr node)
{
return null;
View
3  Parser.cs
@@ -158,7 +158,7 @@ static Parser()
pratt.Literal(TokenKind.False, (Token token) => new BoolExpr { location = token.location, value = false });
pratt.Literal(TokenKind.StringLit, (Token token) => new StringExpr { location = token.location, value = token.text });
pratt.Literal(TokenKind.Identifier, (Token token) => new IdentExpr { location = token.location, name = token.text });
- pratt.Literal(TokenKind.CharLit, (Token token) => new CharExpr { location = token.location, value = token.text[0] });
+ pratt.Literal(TokenKind.CharLit, (Token token) => new IntExpr { location = token.location, value = token.text[0] });
pratt.Get(TokenKind.IntLit).prefixParser = ParseIntExpr;
pratt.Get(TokenKind.FloatLit).prefixParser = ParseFloatExpr;
@@ -167,7 +167,6 @@ static Parser()
pratt.Literal(TokenKind.Bool, ParsePrimType);
pratt.Literal(TokenKind.Int, ParsePrimType);
pratt.Literal(TokenKind.Float, ParsePrimType);
- pratt.Literal(TokenKind.Char, ParsePrimType);
pratt.Literal(TokenKind.String, ParsePrimType);
// Infix operators
View
266 Passes.cs
@@ -31,11 +31,6 @@ public static void ErrorUndefinedSymbol(this Log log, Location location, string
log.Error(location, "reference to undefined symbol \"" + name + "\"");
}
- public static void ErrorNoTypeContext(this Log log, Location location, string name)
- {
- log.Error(location, "cannot deduce the type of " + name + " from the surrounding context");
- }
-
public static void ErrorNotType(this Log log, Location location, Type type)
{
log.Error(location, "value of type \"" + type + "\" is not a type");
@@ -45,6 +40,33 @@ public static void ErrorTypeMismatch(this Log log, Location location, Type expec
{
log.Error(location, "expected value of type \"" + expected + "\" but found value of type \"" + found + "\"");
}
+
+ public static void ErrorBinaryOpNotFound(this Log log, BinaryExpr node)
+ {
+ log.Error(node.location, "no match for operator " + node.op.AsString() + " that takes arguments \"(" +
+ node.left.computedType + ", " + node.right.computedType + ")\"");
+ }
+
+ public static void ErrorInvalidCast(this Log log, Location location, Type from, Type to)
+ {
+ log.Error(location, "cannot cast value of type \"" + from + "\" to \"" + to + "\"");
+ }
+
+ public static void ErrorBadMemberAccess(this Log log, MemberExpr node)
+ {
+ log.Error(node.location, "cannot access member \"" + node.name + "\" on value of type \"" +
+ node.obj.computedType + "\"");
+ }
+
+ public static void ErrorCallNotFound(this Log log, Location location, Type funcType, List<Type> argTypes)
+ {
+ log.Error(location, "cannot call value of type \"" + funcType + "\" with arguments \"" + argTypes.AsString() + "\"");
+ }
+
+ public static void ErrorMultipleOverloadsFound(this Log log, Location location, List<Type> argTypes)
+ {
+ log.Error(location, "multiple ambiguous overloads that match arguments \"" + argTypes.AsString() + "\"");
+ }
}
////////////////////////////////////////////////////////////////////////////////
@@ -276,6 +298,61 @@ public override Null Visit(FuncDef node)
return null;
}
+
+ public override Null Visit(ExternalStmt node)
+ {
+ // External statements don't have their own scope
+ node.block.scope = scope;
+ base.Visit(node);
+ return null;
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// class ComputeSymbolTypesPass
+////////////////////////////////////////////////////////////////////////////////
+
+// Compute the type of all expressions that are expected to contain types, then
+// use these to set symbol types. This way the types of all symbols will be
+// known when we compute the types of the other expressions in a later pass.
+public class ComputeSymbolTypesPass : DefaultVisitor
+{
+ private Log log;
+ private ComputeTypesPass helper;
+
+ public ComputeSymbolTypesPass(Log log)
+ {
+ this.log = log;
+ helper = new ComputeTypesPass(log);
+ }
+
+ public Type GetInstanceType(Expr node)
+ {
+ helper.scope = scope;
+ node.Accept(helper);
+ if (node.computedType is MetaType) {
+ return ((MetaType)node.computedType).instanceType;
+ }
+ log.ErrorNotType(node.location, node.computedType);
+ return new ErrorType();
+ }
+
+ public override Null Visit(VarDef node)
+ {
+ base.Visit(node);
+ node.symbol.type = GetInstanceType(node.type);
+ return null;
+ }
+
+ public override Null Visit(FuncDef node)
+ {
+ base.Visit(node);
+ node.symbol.type = new FuncType {
+ returnType = GetInstanceType(node.returnType),
+ argTypes = node.argDefs.ConvertAll(arg => GetInstanceType(arg.type))
+ };
+ return null;
+ }
}
////////////////////////////////////////////////////////////////////////////////
@@ -315,12 +392,6 @@ public override Null Visit(FloatExpr node)
return null;
}
- public override Null Visit(CharExpr node)
- {
- node.computedType = new PrimType { kind = PrimKind.Char };
- return null;
- }
-
public override Null Visit(StringExpr node)
{
node.computedType = new PrimType { kind = PrimKind.String };
@@ -329,8 +400,7 @@ public override Null Visit(StringExpr node)
public override Null Visit(NullExpr node)
{
- node.computedType = new ErrorType();
- log.ErrorNoTypeContext(node.location, "null");
+ node.computedType = new NullType();
return null;
}
@@ -350,6 +420,9 @@ public override Null Visit(BinaryExpr node)
{
node.computedType = new ErrorType();
base.Visit(node);
+ if (!SetUpBinaryOp(node)) {
+ log.ErrorBinaryOpNotFound(node);
+ }
return null;
}
@@ -357,6 +430,47 @@ public override Null Visit(CallExpr node)
{
node.computedType = new ErrorType();
base.Visit(node);
+
+ // Try to resolve overloaded functions
+ Type type = node.func.computedType;
+ List<Type> argTypes = node.args.ConvertAll(arg => arg.computedType);
+ if (type is OverloadedFuncType) {
+ OverloadedFuncType overloadedType = (OverloadedFuncType)type;
+ List<FuncType> exactMatches = new List<FuncType>();
+ List<FuncType> implicitMatches = new List<FuncType>();
+
+ // Try to mach each overload
+ foreach (Symbol symbol in overloadedType.overloads) {
+ FuncType funcType = (FuncType)symbol.type;
+ if (argTypes.MatchesExactly(funcType.argTypes)) {
+ exactMatches.Add(funcType);
+ } else if (argTypes.MatchesWithImplicitConversions(funcType.argTypes)) {
+ implicitMatches.Add(funcType);
+ }
+ }
+
+ // Pick the best-matching overload
+ List<FuncType> matches = (exactMatches.Count > 0) ? exactMatches : implicitMatches;
+ if (matches.Count > 1) {
+ log.ErrorMultipleOverloadsFound(node.location, argTypes);
+ } else if (matches.Count == 1) {
+ type = matches[0];
+ }
+ }
+
+ // Call the function if there is one, inserting implicit casts as appropriate
+ if (type is FuncType && argTypes.MatchesWithImplicitConversions(((FuncType)type).argTypes)) {
+ FuncType funcType = (FuncType)type;
+ for (int i = 0; i < funcType.argTypes.Count; i++) {
+ if (!node.args[i].computedType.EqualsType(funcType.argTypes[i])) {
+ node.args[i] = InsertCast(node.args[i], funcType.argTypes[i]);
+ }
+ }
+ node.computedType = funcType.returnType;
+ } else {
+ log.ErrorCallNotFound(node.location, type, argTypes);
+ }
+
return null;
}
@@ -364,6 +478,19 @@ public override Null Visit(CastExpr node)
{
node.computedType = new ErrorType();
base.Visit(node);
+
+ // Check that the cast is valid
+ if (!(node.target.computedType is MetaType)) {
+ log.ErrorNotType(node.location, node.target.computedType);
+ } else {
+ Type targetType = ((MetaType)node.target.computedType).instanceType;
+ if (!IsValidCast(node.value.computedType, targetType)) {
+ log.ErrorInvalidCast(node.value.location, node.value.computedType, targetType);
+ } else {
+ node.computedType = targetType;
+ }
+ }
+
return null;
}
@@ -371,6 +498,18 @@ public override Null Visit(MemberExpr node)
{
node.computedType = new ErrorType();
base.Visit(node);
+
+ if (node.obj.computedType is ClassType) {
+ node.symbol = ((ClassType)node.obj.computedType).def.block.scope.Lookup(node.name);
+ if (node.symbol == null) {
+ log.ErrorBadMemberAccess(node);
+ } else {
+ node.computedType = node.symbol.type;
+ }
+ } else {
+ log.ErrorBadMemberAccess(node);
+ }
+
return null;
}
@@ -381,12 +520,108 @@ public override Null Visit(VarDef node)
log.ErrorNotType(node.type.location, node.type.computedType);
} else {
node.symbol.type = ((MetaType)node.type.computedType).instanceType;
- if (node.value != null && !node.symbol.type.EqualsType(node.value.computedType)) {
- log.ErrorTypeMismatch(node.location, node.symbol.type, node.value.computedType);
+ if (node.value != null && !node.value.computedType.EqualsType(node.symbol.type)) {
+ if (node.value.computedType.CanImplicitlyConvertTo(node.symbol.type)) {
+ node.value = InsertCast(node.value, node.symbol.type);
+ } else {
+ log.ErrorTypeMismatch(node.location, node.symbol.type, node.value.computedType);
+ }
}
}
return null;
}
+
+ private static Expr InsertCast(Expr value, Type target)
+ {
+ Type type = new MetaType { instanceType = target };
+ return new CastExpr {
+ location = value.location,
+ value = value,
+ target = new TypeExpr { type = type, computedType = type },
+ computedType = target
+ };
+ }
+
+ private bool IsValidCast(Type from, Type to)
+ {
+ return from.EqualsType(to) || from.CanImplicitlyConvertTo(to) || (from.IsNumeric() && to.IsNumeric());
+ }
+
+ private bool SetUpBinaryOpHelper(BinaryExpr node, bool resultIsBool)
+ {
+ Type left = node.left.computedType;
+ Type right = node.right.computedType;
+
+ if (left.EqualsType(right)) {
+ node.computedType = resultIsBool ? new PrimType { kind = PrimKind.Bool } : left;
+ return true;
+ }
+
+ if (left.CanImplicitlyConvertTo(right)) {
+ node.left = InsertCast(node.left, right);
+ node.computedType = resultIsBool ? new PrimType { kind = PrimKind.Bool } : right;
+ return true;
+ }
+
+ if (right.CanImplicitlyConvertTo(left)) {
+ node.right = InsertCast(node.right, left);
+ node.computedType = resultIsBool ? new PrimType { kind = PrimKind.Bool } : left;
+ return true;
+ }
+
+ return false;
+ }
+
+ private bool SetUpBinaryOp(BinaryExpr node)
+ {
+ Type left = node.left.computedType;
+ Type right = node.right.computedType;
+
+ switch (node.op) {
+ case BinaryOp.Assign:
+ break;
+
+ case BinaryOp.And:
+ case BinaryOp.Or:
+ if (left.IsBool() && right.IsBool()) {
+ node.computedType = new PrimType { kind = PrimKind.Bool };
+ return true;
+ }
+ break;
+
+ case BinaryOp.Add:
+ if (((left.IsNumeric() && right.IsNumeric()) || (left.IsString() && right.IsString())) && SetUpBinaryOpHelper(node, false)) {
+ return true;
+ }
+ break;
+
+ case BinaryOp.Subtract:
+ case BinaryOp.Multiply:
+ case BinaryOp.Divide:
+ if (left.IsNumeric() && right.IsNumeric() && SetUpBinaryOpHelper(node, false)) {
+ return true;
+ }
+ break;
+
+ case BinaryOp.Equal:
+ case BinaryOp.NotEqual:
+ if (SetUpBinaryOpHelper(node, true)) {
+ return true;
+ }
+ break;
+
+ case BinaryOp.LessThan:
+ case BinaryOp.GreaterThan:
+ case BinaryOp.LessThanEqual:
+ case BinaryOp.GreaterThanEqual:
+ if (((left.IsNumeric() && right.IsNumeric()) || (left.IsString() && right.IsString())) && SetUpBinaryOpHelper(node, true)) {
+ return true;
+ }
+ break;
+ }
+
+ return false;
+ }
}
////////////////////////////////////////////////////////////////////////////////
@@ -421,6 +656,7 @@ public static bool Compile(Log log, Module module)
Pass[] passes = new Pass[] {
new VisitorPass<Null>(new StructuralCheckPass(log)),
new VisitorPass<Null>(new DefineSymbolsPass(log)),
+ new VisitorPass<Null>(new ComputeSymbolTypesPass(log)),
new VisitorPass<Null>(new ComputeTypesPass(log)),
};
foreach (Pass pass in passes) {
View
1  Tokenizer.cs
@@ -61,7 +61,6 @@ public enum TokenKind
Bool,
Int,
Float,
- Char,
String,
// Literals
View
19 Types.cs
@@ -28,7 +28,6 @@ public enum PrimKind
Bool,
Int,
Float,
- Char,
String,
}
@@ -54,12 +53,13 @@ public class FuncType : Type
public override bool EqualsType(Type other)
{
- return other is FuncType && returnType.EqualsType((FuncType)other) && argTypes.Matches(((FuncType)other).argTypes);
+ return other is FuncType && returnType.EqualsType((FuncType)other) &&
+ argTypes.MatchesExactly(((FuncType)other).argTypes);
}
public override string ToString()
{
- return returnType + " function(" + string.Join(", ", argTypes.ConvertAll(x => x.ToString()).ToArray()) + ")";
+ return returnType + " function" + argTypes.AsString();
}
}
@@ -108,6 +108,19 @@ public override string ToString()
}
}
+public class NullType : Type
+{
+ public override bool EqualsType(Type other)
+ {
+ return other is NullType;
+ }
+
+ public override string ToString()
+ {
+ return "<null>";
+ }
+}
+
public class ErrorType : Type
{
public override bool EqualsType(Type other)
View
36 Utility.cs
@@ -122,14 +122,14 @@ public static class Constants
{ "bool", TokenKind.Bool },
{ "int", TokenKind.Int },
{ "float", TokenKind.Float },
- { "char", TokenKind.Char },
{ "string", TokenKind.String },
};
+
+ // Map tokens for primitive types to the equivalent PrimKind
public static readonly Dictionary<TokenKind, PrimKind> tokenToPrim = new Dictionary<TokenKind, PrimKind> {
{ TokenKind.Bool, PrimKind.Bool },
{ TokenKind.Int, PrimKind.Int },
{ TokenKind.Float, PrimKind.Float },
- { TokenKind.Char, PrimKind.Char },
{ TokenKind.String, PrimKind.String },
};
@@ -203,7 +203,12 @@ public static string AsString(this BinaryOp op)
return Constants.tokenToString[Constants.binaryOperators.enumToToken[op]];
}
- public static bool Matches(this List<Type> a, List<Type> b)
+ public static bool CanImplicitlyConvertTo(this Type from, Type to)
+ {
+ return (from.IsInt() && to.IsFloat()) || (from is NullType && to is ClassType);
+ }
+
+ public static bool MatchesExactly(this List<Type> a, List<Type> b)
{
if (a.Count != b.Count) {
return false;
@@ -216,6 +221,24 @@ public static bool Matches(this List<Type> a, List<Type> b)
return true;
}
+ public static bool MatchesWithImplicitConversions(this List<Type> from, List<Type> to)
+ {
+ if (from.Count != to.Count) {
+ return false;
+ }
+ for (int i = 0; i < from.Count; i++) {
+ if (!from[i].EqualsType(to[i]) && !from[i].CanImplicitlyConvertTo(to[i])) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public static string AsString(this List<Type> argTypes)
+ {
+ return "(" + string.Join(", ", argTypes.ConvertAll(arg => arg.ToString()).ToArray()) + ")";
+ }
+
public static bool IsBool(this Type type)
{
return type is PrimType && ((PrimType)type).kind == PrimKind.Bool;
@@ -231,11 +254,6 @@ public static bool IsFloat(this Type type)
return type is PrimType && ((PrimType)type).kind == PrimKind.Float;
}
- public static bool IsChar(this Type type)
- {
- return type is PrimType && ((PrimType)type).kind == PrimKind.Char;
- }
-
public static bool IsString(this Type type)
{
return type is PrimType && ((PrimType)type).kind == PrimKind.String;
@@ -243,6 +261,6 @@ public static bool IsString(this Type type)
public static bool IsNumeric(this Type type)
{
- return type.IsInt() || type.IsFloat() || type.IsChar();
+ return type.IsInt() || type.IsFloat();
}
}
Please sign in to comment.
Something went wrong with that request. Please try again.