• Content count

  • Joined

  • Last visited

Community Reputation

169 Neutral

About cellulose

  • Rank

    I think that might avert a great deal of confusion -- I've had some of my collaborators trip over the present @ semantics. A new operator like "[b]:=[/b]" for value assignment might be an order if such a change is made, though.
  2. Function call operators in the future?

    Awesome; I'll perform a merge with trunk then. I may take the liberty of creating a new compiler method to unify the logic for callable objects.
  3. Function call operators in the future?

    Below is my current progress with CompileFunctionCall. It looks like the CompileVariableAccess that looks for a callable variable is already doing the work of pushing the called object onto the stack. It's hilarious to think this might work in some naive way. However, I doubt it does. I'm guessing I need to deal with the format of the variable (reference/dereference it?) and possibly store a temporary copy, as is done for funcdefs toward the bottom. The supposed necessity of a temporary variable here actually baffles me a little, given the only eligible identifiers are local, class and global variables of funcdef type. [CODE] int asCCompiler::CompileFunctionCall(asCScriptNode *node, asSExprContext *ctx, asCObjectType *objectType, bool objIsConst, const asCString &scope) { asCString name; asCTypeInfo tempObj; asCArray<int> funcs; int r = -1; asCScriptNode *nm = node->lastChild->prev; name.Assign(&script->code[nm->tokenPos], nm->tokenLength); // If we're compiling a class method, then the call may be to a class method // even though it looks like an ordinary call to a global function. If it is // to a class method it is necessary to implicitly add the this pointer. if( objectType == 0 && outFunc && outFunc->objectType && scope != "::" ) { // The special keyword 'super' may be used in constructors to invoke the base // class' constructor. This can only be used without any scoping operator if( m_isConstructor && name == SUPER_TOKEN && scope == "" ) { // We are calling the base class' constructor, so set the objectType objectType = outFunc->objectType; } else { // Are there any class methods that may match? // TODO: namespace: Should really make sure the scope also match. Because the scope // may match a base class, or it may match a global namespace. If it is // matching a global scope then we're not calling a class method even // if there is a method with the same name. asCArray<int> funcs; builder->GetObjectMethodDescriptions(name.AddressOf(), outFunc->objectType, funcs, false); if( funcs.GetLength() ) { // We're calling a class method, so set the objectType objectType = outFunc->objectType; } } // If a class method is being called then implicitly add the this pointer for the call if( objectType ) { asCDataType dt = asCDataType::CreateObject(objectType, false); // The object pointer is located at stack position 0 ctx->bc.InstrSHORT(asBC_PSF, 0); ctx->type.SetVariable(dt, 0, false); ctx->type.dataType.MakeReference(true); Dereference(ctx, true); } } // First check for a local variable of a callable type // Must not allow function names, nor global variables to be returned in this instance asSExprContext callableVar(engine); if( objectType == 0 ) { //In the case of a callable object, this adds the this pointer for the call. r = CompileVariableAccess(name, scope, &callableVar, node, true, true); } if( r < 0 ) { if( objectType ) { // If we're compiling a constructor and the name of the function is super then // the constructor of the base class is being called. // super cannot be prefixed with a scope operator if( scope == "" && m_isConstructor && name == SUPER_TOKEN ) { // If the class is not derived from anyone else, calling super should give an error if( objectType->derivedFrom ) funcs = objectType->derivedFrom->beh.constructors; // Must not allow calling base class' constructor multiple times if( continueLabels.GetLength() > 0 ) { // If a continue label is set we are in a loop Error(TXT_CANNOT_CALL_CONSTRUCTOR_IN_LOOPS, node); } else if( breakLabels.GetLength() > 0 ) { // TODO: inheritance: Should eventually allow constructors in switch statements // If a break label is set we are either in a loop or a switch statements Error(TXT_CANNOT_CALL_CONSTRUCTOR_IN_SWITCH, node); } else if( m_isConstructorCalled ) { Error(TXT_CANNOT_CALL_CONSTRUCTOR_TWICE, node); } m_isConstructorCalled = true; } else { // The scope is can be used to specify the base class builder->GetObjectMethodDescriptions(name.AddressOf(), objectType, funcs, objIsConst, scope); } // It is still possible that there is a class member of a callable type if( funcs.GetLength() == 0 ) CompileVariableAccess(name, scope, &callableVar, node, true, true, objectType); } else { // The scope is used to define the namespace asSNameSpace *ns = DetermineNameSpace(scope); if( ns ) builder->GetFunctionDescriptions(name.AddressOf(), funcs, ns); else { asCString msg; msg.Format(TXT_NAMESPACE_s_DOESNT_EXIST, scope.AddressOf()); Error(msg.AddressOf(), node); return -1; } // TODO: funcdef: It is still possible that there is a global variable of a callable type // ...is it? (Code above should account for this) -- EDB 09/2012 } } // Handle the callable variable if ( callableVar.type.dataType.IsValid() ) { //Determine callability of object r = -1; if( callableVar.type.dataType.GetFuncDef() ) { //Function pointers are always callable funcs.PushLast(callableVar.type.dataType.GetFuncDef()->id); r = 0; } else if( callableVar.type.dataType.IsObject() ) { //Check whether object implements opCall const char *opCall = "opCall"; asCObjectType *callType = callableVar.type.dataType.GetObjectType(); builder->GetObjectMethodDescriptions(opCall, callType, funcs, false); if ( funcs.GetLength() ) { r = 0; objectType = callType; } } if (r < 0) { // The variable is not a function asCString msg; msg.Format(TXT_NOT_A_FUNC_s_IS_VAR, name.AddressOf()); Error(msg.AddressOf(), node); return -1; } } // Compile the arguments asCArray<asSExprContext *> args; asCArray<asCTypeInfo> temporaryVariables; if( CompileArgumentList(node->lastChild, args) >= 0 ) { // Special case: Allow calling func(void) with a void expression. if( args.GetLength() == 1 && args[0]->type.dataType == asCDataType::CreatePrimitive(ttVoid, false) ) { // Evaluate the expression before the function call MergeExprBytecode(ctx, args[0]); asDELETE(args[0],asSExprContext); args.SetLength(0); } MatchFunctions(funcs, args, node, name.AddressOf(), objectType, objIsConst, false, true, scope); if( funcs.GetLength() != 1 ) { // The error was reported by MatchFunctions() // Dummy value ctx->type.SetDummy(); } else { int r = asSUCCESS; // Add the default values for arguments not explicitly supplied asCScriptFunction *func = (funcs[0] & 0xFFFF0000) == 0 ? engine->scriptFunctions[funcs[0]] : 0; if( func && args.GetLength() < (asUINT)func->GetParamCount() ) r = CompileDefaultArgs(node, args, func); // TODO: funcdef: Do we have to make sure the handle is stored in a temporary variable, or // is it enough to make sure it is in a local variable? // For function pointer we must guarantee that the function is safe, i.e. // by first storing the function pointer in a local variable (if it isn't already in one) if( r == asSUCCESS ) { if( func && func->funcType == asFUNC_FUNCDEF ) { if( objectType ) { Dereference(ctx, true); // Dereference the object pointer to access the member // The actual function should be called as if a global function objectType = 0; } Dereference(&callableVar, true); ConvertToVariable(&funcPtr); ctx->bc.AddCode(&funcPtr.bc); if( !funcPtr.type.isTemporary ) ctx->bc.Instr(asBC_PopPtr); } MakeFunctionCall(ctx, funcs[0], objectType, args, node, false, 0, callableVar.type.stackOffset); // If the function pointer was copied to a local variable for the call, then // release it again (temporary local variable) if( func && func->funcType == asFUNC_FUNCDEF ) { ReleaseTemporaryVariable(funcPtr.type, &ctx->bc); } } } } else { // Failed to compile the argument list, set the dummy type and continue compilation ctx->type.SetDummy(); } // Cleanup for( asUINT n = 0; n < args.GetLength(); n++ ) if( args[n] ) { asDELETE(args[n],asSExprContext); } return 0; } [/CODE] EDIT: Just realized why that global-variable lookup would be necessary. I'll probably be adding code there... EDIT: I can't seem to determine what disqualifies global variables from the first variable lookup attempt. Any insight into that? [color=#0000ff]CompileVariableAccess(name, scope, &callableVar, node, true, true);[/color]
  4. Function call operators in the future?

    I got sidetracked from this for about a week. Back into the fray of things. Question for Andreas: Where in CompileFunctionCall is the "this" value indicated to the bytecode? I'm getting my head around expression contexts et cetera. EDIT: Partially answered my own question. (at least sort of -- CompileFunctionCall is evidently only used for "get" accessors and function calls that are not part of a larger expression?) I can see now how callable variables (funcPtrs and opCall-able objects) throw a wrench into the compiler's current handling of function calling... EDIT: I'm going to invest the time to get a better top-down understanding of the compiler's architecture, I think... Still no idea how the 'this' reference is handled.
  5. Function call operators in the future?

    Already at work on it. Are there any particular caveats responsible for this remark in CompileFunctionCall? (Version 2.24.1) [CODE] // TODO: funcdef: It is still possible that there is a global variable of a function type [/CODE] Or would it be safe to add the variable-access resolution code? EDIT: Ah, it looks like it's superfluous...
  6. Function call operators in the future?

    In that case I eagerly await the next release.
  7. Function call operators in the future?

    Pardon the necro-post; the release of version 2.24 makes this relevant again. A statement from Andreas in my topic on hardcoding geometry and initializer lists: [quote]TheAtom reported a bug today about making calls with function defs. I still need to investigate what changes needs to be made to support those scenarios, but as they look similar in syntax to what the use of a function call operator might look like I recommend you wait a little until I've fixed that bug before you dig too deep into the code.[/quote] Since the improved semantics for function pointers are now implemented, how straightforward would the implementation of an opCall be? (Either as a new official addition to the language or, barring that, as a hack to my own copy?)
  8. Bug with parsing of callable expressions

    How feasible is it looking to add a function-call operator, given the new development? (Pardon if this is tangental to the topic at hand; for the uninformed, Andreas remarked that this change would be involve of the work prerequisite to function-call operators.)
  9. angelcode.com favicon

    This feels tangental in this forum, but I think a favicon would be a good addition to angelcode.com; I leave the angelscript docs open often and lose track of which tab is referring to them... Regarding the design, anything sufficiently distinctive would do. The Angelscript logo seems the obvious choice though it might be hard to identify at a small size.
  10. Hardcoding geometry / initializer lists as expressions?

    For my part, I think the initialization-list constructor should be something that's defined manually somehow, to prevent an ambiguity between constructors which take arrays as their arguments and those which take initialization lists -- for instance, an array<int> has a 2-int constructor where the first specifies a size and the second specifies an element to be copied to all indices, but when we pass it an initialization list we want a size of two and the given elements. There might be a better way around this but I wouldn't know what it was. Also thanks -- this is going to be an immensely awesome language feature.
  11. Get name of a primitive passed as parameter to C++

    You said the issue with the original technique was the need to iterate through all your member variables -- is making a hashmap acceptable? It still involves processing a string, but operates in ~O(1) time.
  12. Hardcoding geometry / initializer lists as expressions?

    Maybe an expression syntax like TYPE{element, element, ...} ? That would allow me to do something like this: [CODE] typedef float[][] Vertices; // ... myShape.build(Piece_Polygon, Vertices{{1.5f, 2.0f}, {-5.0f, 3.1f}, {6.1f, 3.2f}}); [/CODE] Admittedly type inference would be way nicer but I understand it's a non-trivial problem when things like function overloads are involved. :/ [b]EDIT:[/b] [i]Definitely [/i]okay with waiting a while if it means a possibility of function-call operators.
  13. Hardcoding geometry / initializer lists as expressions?

    Hah, well, my investigation of the compiler code has enlightened me in two ways: 1. It's hard to do a casual code dive in a compiler. 2. Function-call syntax has to sort out a ton of ambiguities, most relevantly the one between an operator() on a member property and a call to a member function. I'm still really fascinated with the notion of performing some kind of substitution where the error [font=courier new,courier,monospace]TXT_NOT_A_FUNC_s_IS_VAR [/font]occurs. Any insight you might be able to give on the possibility of this, Andreas? EDIT: But it doesn't hit that error in the chained-parenthesis case. Darn.
  14. I've been trying to find a good way to facilitate terse, hardcoded vertex data in my engine's scripting library -- the engine in question defines all its graphics based on polygon outlines, and is meant to facilitate generative art and (now that I'm adding script support) quick prototyping. If at all possible I'd like to find a decent way to avoid needing an identifier (like [font=courier new,courier,monospace]Point(...)[/font] or [font=courier new,courier,monospace].add(...)[/font]) with every pair of coordinates. My C++ method is reliant on a temporary class and a function-call ([font=courier new,courier,monospace]operator()[/font]) overload, which Angelscript doesn't support by design and is not a likely future addition based on remarks by Andreas: [CODE] myShape.build(Shape::LINE_LOOP)(-.5f, -.4f)(.0f, .4f)(.5f, -.4f); [/CODE] Another possible (though inelegant) means of doing it would be a variable-parameter-count function call (the generic calling convention?), but I haven't seen evidence of support for such in function registration. Yesterday it struck me that an extremely attractive solution (clearer still than my C++ one, if less terse) would be using array initializer lists: [CODE] Point p = {.5f, .2f}; myShape.build(Shape::POLYGON, {{-.5f, -.4f}, {.0f, .4f}, {.5f, -.4f}}); // or perhaps myShape.add(Shape::POLYGON) = {{-.5f, -.4f}, {.0f, .4f}, {.5f, -.4f}}; [/CODE] ...I didn't expect to see this sort of behavior outlawed, but unfortunately it is, for reasons which I assume relate to static typing. I could impose the extra step of creating an array of type float[][] or Point[], but the former discards its elegance while keeping the caveat of inner arrays allowing incorrect sizes, and the latter requires a slew of constructors which is what I'm trying to avoid in the first place. I'm curious to hear any solutions or ideas folks here might have. I'm getting tempted to write a preprocessor that turns the token ".(" into ".opFunc(" or something to that effect, but it's likely to be a messy solution and create inconsistencies with normal script behavior. I could go a step further and attempt to implement a proper function-call operator in angelscript's compiler, but this may be overkill given the work involved and that I'd need to merge the modification with each new version of the library as I updated. Still, it's tempting, and I've taken the initiative of digging around in the parser/compiler/tokenizer code.
  15. Function call operators in the future?

    Oh, no, I'm quite aware. I'm saying I'm happy to have property accessors. But anyway. Keeping my fingers crossed. And who knows? Maybe if I'm antsy enough it's the sort of thing I could write a preprocessor for.