public void Validate_Get_expr_yields_expected_error_for_undefined_variable() { // Arrange var name = new Token(TokenType.IDENTIFIER, "foo", null, -1); var identifier = new Expr.Identifier(name); var getExpr = new Expr.Get(identifier, name); var bindings = new Dictionary <Expr, Binding> { // A null TypeReference here is the condition that is expected to lead to an "undefined variable" error. { identifier, new VariableBinding(typeReference: null, 0, identifier) } }; var typeValidationErrors = new List <TypeValidationError>(); var warnings = new List <CompilerWarning>(); // Act TypeValidator.Validate( new List <Stmt> { new Stmt.ExpressionStmt(getExpr) }, error => typeValidationErrors.Add(error), expr => bindings[expr], warning => warnings.Add(warning) ); // Assert Assert.Single(typeValidationErrors); Assert.Matches(typeValidationErrors.Single().Message, "Undefined identifier 'foo'"); Assert.Empty(warnings); }
public void Validate_Get_expr_yields_no_error_for_defined_variable() { // Arrange // // This is the method being referred to. The expression below roughly matches "Foo.to_string", where // Foo is a defined Perlang class. var name = new Token(TokenType.IDENTIFIER, "to_string", null, -1); var identifier = new Expr.Identifier(name); var getExpr = new Expr.Get(identifier, name); var bindings = new Dictionary <Expr, Binding> { { identifier, new ClassBinding(identifier, new PerlangClass("Foo", new List <Stmt.Function>())) } }; var typeValidationErrors = new List <TypeValidationError>(); var warnings = new List <CompilerWarning>(); // Act TypeValidator.Validate( new List <Stmt> { new Stmt.ExpressionStmt(getExpr) }, error => typeValidationErrors.Add(error), expr => bindings[expr], warning => warnings.Add(warning) ); // Assert Assert.Empty(typeValidationErrors); Assert.Empty(warnings); }
/// <summary> /// Parse an assignment /// </summary> /// <returns></returns> private Expr Assignment() { Expr expr = Or(); if (Match(TokenType.EQUAL)) { Token equals = Previous(); Expr value = Assignment(); // We have found a assignment target // Make sure its a variable if (expr is Expr.Variable) { Token name = ((Expr.Variable)expr).Name; return(new Expr.Assign(name, value)); } else if (expr is Expr.Get) { Expr.Get get = (Expr.Get)expr; return(new Expr.Set(get.Object, get.Name, value)); } Error(equals, "Invalid assignment target."); } return(expr); }
private Expr Assignment() { Expr expr = Or(); if (Match(Equal)) { Token equals = Previous(); Expr value = Assignment(); if (expr is Expr.Variable) { Token name = ((Expr.Variable)expr).Name; return(new Expr.Assign(name, value)); } else if (expr is Expr.Get) { Expr.Get get = (Expr.Get)expr; return(new Expr.Set(get.Value, get.Name, value)); } Error(equals, "Invalid assignment target."); } return(expr); }
private Expr Assignment() { Expr expr = Or(); if (Match(EQUAL)) // We are assigning something { Token equals = Previous(); Expr value = Assignment(); /// Assigning to a 'normal' basic instance if (expr is Expr.Variable) { Token name = ((Expr.Variable)expr).Name; return(new Expr.Assign(name, value)); } /// We are assigning something to a classinstance get... /// Which is impossoble, so it must be a set. else if (expr is Expr.Get) { Expr.Get get = (Expr.Get)expr; return(new Expr.Set(get.Object, get.Name, value)); } Error(equals, "Invalid assignment target"); } return(expr); }
/// <summary> /// Parse a function call /// </summary> /// <returns>The expression</returns> private Expr Call() { Expr expr = Primary(); while (true) { if (Match(TokenType.LEFT_PAREN)) { // Function call expr = FinishCall(expr); } else if (Match(TokenType.DOT)) { // Property Token name = Consume(TokenType.IDENTIFIER, "Expect property name after '.'."); expr = new Expr.Get(expr, name); } else { break; } } return(expr); }
public void Validate_Call_expr_does_something_useful() { // Arrange var name = new Token(TokenType.IDENTIFIER, "to_string", null, 1); var identifier = new Expr.Identifier(name); var get = new Expr.Get(identifier, name); var paren = new Token(TokenType.RIGHT_PAREN, ")", null, 1); var callExpr = new Expr.Call(get, paren, new List <Expr>()); var bindings = new Dictionary <Expr, Binding> { { identifier, new NativeClassBinding(identifier, typeof(string)) } }; var typeValidationErrors = new List <TypeValidationError>(); var warnings = new List <CompilerWarning>(); // Act TypeValidator.Validate( new List <Stmt> { new Stmt.ExpressionStmt(callExpr) }, error => typeValidationErrors.Add(error), expr => bindings[expr], warning => warnings.Add(warning) ); // Assert Assert.Empty(typeValidationErrors); Assert.Empty(warnings); }
public object VisitGetExpr(Expr.Get expr) { var obj = Evaluate(expr.obj); if (obj is LoxInstance li) { return(li.Get(expr.name)); } throw new RuntimeException(expr.name, "Only instances have properties"); }
public object Visit(Expr.Get expr) { object obj = Evaluate(expr.Object); if (obj is LoxInstance) { return(((LoxInstance)obj).Get(expr.Name)); } throw new RuntimeErrorException(expr.Name, "Only instances has properties."); }
public object visitGetExpr(Expr.Get expr) { object object_ = evaluate(expr.object_); if (object_ is LoxInstance) { return(((LoxInstance)object_).get(expr.name)); } throw new RuntimeError(expr.name, "Only instances have properties."); }
public object visitGetExpr(Expr.Get getExpr) { compile(getExpr.object_); captureToken(getExpr.name); byte name = identifierConstant(getExpr.name); emitBytes((byte)OpCode.OP_GET_PROPERTY, name); return(null); }
public object VisitGetExpr(Expr.Get expr) { object instanceObj = Evalutate(expr.Instance); if (instanceObj is LoxInstance instance) { return(instance.Get(expr.Name)); } throw new RuntimeError(expr.Name, "Only instances have properties."); }
public object VisitGetExpr(Expr.Get expr) { object obj = Evaluate(expr.Obj); if (obj is LoxInstance) { return(((LoxInstance)obj).Get(expr.Name)); } throw new InterpretingException(expr.Name, "Only instances have properties."); }
public object VisitGetExpr(Expr.Get expr) { object value = Evaluate(expr.Value); if (value is LoxInstance) { return(((LoxInstance)value).Get(expr.Name)); } throw new RuntimeException(expr.Name, "Only instances have properties."); }
void getCall(Expr.Call gCallExpr) { Expr.Get getExpr = (Expr.Get)gCallExpr.callee; compile(getExpr.object_); captureToken(getExpr.name); byte name = identifierConstant(getExpr.name); byte argCount = compile_argumentList(gCallExpr.arguments); emitBytes((byte)OpCode.OP_INVOKE, name); emitByte(argCount); }
private Expr Call() { Expr expr = Primary(); while (true) { if (Match(LEFT_PAREN)) { expr = FinishCall(expr); } else if (Match(DOT)) { Token name = Consume(IDENTIFIER, "Expect property name after '.'."); expr = new Expr.Get(expr, name); } else { break; } } return(expr); }
private Expr Call() { Expr expr = Primary(); while (true) { if (Match(LeftParen)) { expr = FinishCall(expr); } else if (Match(Dot)) { Token name = Consume(Identifier, "Expect property after '.'."); expr = new Expr.Get(expr, name); } else { break; } } return(expr); }
public string VisitGetExpr(Expr.Get expr) { return($"{PrintExpr(expr.Instance)}.{expr.Name.Lexeme}"); }
private void VisitCallExprForGetCallee(Expr.Call call, Expr.Get get) { string methodName = get.Name.Lexeme; if (get.Methods.Length == 0) { if (!get.Object.TypeReference.IsResolved) { // This is a bit of an oddball, but... We get here when an attempt is made to call a method on some // undefined type. (`Foo.do_stuff`) // // Now, this is a compile-time error, but the problem is that it's handled by this class itself; // encountering this error will not abort the tree traversal so we must avoid breaking it. } else { // This is even more odd, but we must ensure that we have well-defined semantics in the weird case // where this would happen. TypeValidationErrorCallback(new TypeValidationError( call.Paren, $"Internal compiler error: no methods with name '{methodName}' could be found. This is a critical " + $"error that should have aborted the compilation before the {nameof(TypesResolvedValidator)} " + "validation is started. " )); } } else if (get.Methods.Length == 1) { MethodInfo method = get.Methods.Single(); var parameters = method.GetParameters(); // There is exactly one potential method to call in this case. We use this fact to provide better // error messages to the caller than when calling an overloaded method. if (parameters.Length != call.Arguments.Count) { TypeValidationErrorCallback(new TypeValidationError( call.Paren, $"Method '{methodName}' has {parameters.Length} parameter(s) but was called with {call.Arguments.Count} argument(s)" )); return; } for (int i = 0; i < call.Arguments.Count; i++) { ParameterInfo parameter = parameters[i]; Expr argument = call.Arguments[i]; if (!argument.TypeReference.IsResolved) { throw new PerlangInterpreterException( $"Internal compiler error: Argument '{argument}' to function {methodName} not resolved"); } // FIXME: call.Token is a bit off here; it would be useful when constructing compiler warnings based // on this if we could provide the token for the argument expression instead. However, the Expr type // as used by 'argument' is a non-token-based expression so this is currently impossible. // FIXME: `null` here has disadvantages as described elsewhere. if (!TypeCoercer.CanBeCoercedInto(parameter.ParameterType, argument.TypeReference.ClrType, null)) { // Very likely refers to a native method, where parameter names are not available at this point. TypeValidationErrorCallback(new TypeValidationError( argument.TypeReference.TypeSpecifier !, $"Cannot pass {argument.TypeReference.ClrType.ToTypeKeyword()} argument as {parameter.ParameterType.ToTypeKeyword()} parameter to {methodName}()")); } } } else { // Method is overloaded. Try to resolve the best match we can find. foreach (MethodInfo method in get.Methods) { var parameters = method.GetParameters(); if (parameters.Length != call.Arguments.Count) { // The number of parameters do not match, so this method will never be a suitable candidate // for our expression. continue; } bool coercionsFailed = false; for (int i = 0; i < call.Arguments.Count; i++) { ParameterInfo parameter = parameters[i]; Expr argument = call.Arguments[i]; if (!argument.TypeReference.IsResolved) { throw new PerlangInterpreterException( $"Internal compiler error: Argument '{argument}' to method {methodName} not resolved"); } // FIXME: The same caveat as above with call.Token applies here as well. if (!TypeCoercer.CanBeCoercedInto(parameter.ParameterType, argument.TypeReference.ClrType, null)) { coercionsFailed = true; break; } } if (!coercionsFailed) { // We have found a suitable overload to use. Update the expression get.Methods = ImmutableArray.Create(method); return; } } TypeValidationErrorCallback(new NameResolutionTypeValidationError( call.Paren, $"Method '{call.CalleeToString}' found, but no overload matches the provided parameters." )); } }
public string visitGetExpr(Expr.Get expr) { throw new NotImplementedException(); }
public object VisitGetExpr(Expr.Get expr, object options = null) { return(null); }
public string VisitGetExpr(Expr.Get expr) { return(Parenthesize("get", expr.@object)); }
public object visitGetExpr(Expr.Get expr) { resolve(expr.object_); return(null); }
public object VisitGetExpr(Expr.Get expr) { Resolve(expr.obj); return(null); }
public VoidObject VisitGetExpr(Expr.Get expr) { Resolve(expr.Object); return(VoidObject.Void); }
public Token visitGetExpr(Expr.Get get) { return(get.name); }
public string VisitGetExpr(Expr.Get expr) { return(Parenthesize2(".", expr.Object, expr.Name.Lexeme)); }
public object VisitGetExpr(Expr.Get expr, object options) { return(Parenthesize2(".", expr.obj, expr.token.lexeme)); }
public Unit VisitGetExpr(Expr.Get expr) { Resolve(expr.Object); return(new Unit()); }
/// <summary> /// Resolve a property get /// </summary> /// <param name="expr">The expression</param> /// <returns></returns> public object Visit(Expr.Get expr) { Resolve(expr.Object); return(null); }