Exit Full View

Feather / todo.txt

Feather To Do List
==================

Current
-------

Why can't we implement Lambdas?.
    e.g.fun List<T>filter( Block1<Item, boolean> ) (similar to Function)

        my.filter{ item -> item.isDead() }

    Ah, the problem is that the block need to run inside the *filter* method.
    So let's rewrite using a FunctionType :

    fun List<T>filter( (T)->boolean )
    The first issue is that we now need to support parameterised types in Feather functions :-(
    But if we assume that filter is written in Kotlin for now.

    Now we just need to create a private static function. Give it a compiler generated name such as "_$methodName_{lambdaCount++}"
    Initially, force all parameters to be explicitly typed i.e.

    myStrings.filter { item : String -> item.size() > 3 }

    We also need the same rules, such as no "return", "continue" and "break" cannot extend out of the lambda block.
    Also, we cannot change any local variables outside the lambda block.
    We also need to pass in any local variables that are used within the lambda.
    If the lambda is called from a non-static method, also pass in "this" as the first parameter.
        OR have two mechanisms, one for lambdas within static methods, another for lambdas within non-static methods.
    I suppose we *could* allow locals to be changed, by passing their values back again.
        i.e. the lambda function return a Pair<return value : R, localVariables : Array<Object> >
        After the call to filter, set the locals to the values in the array.

Bugs
----

If a var has the same name as the function it is calling, then the type must be declared explicitly.
    fun foo() = 1
    val foo = foo()
    val foo : int = foo()

`continue` keyword silently screws up :

    static fun main( args : String... ) {
        var verbose = false

        if ( args.size() < 1 ) {
            println( usage )
            exit()
        }
        for (arg in args) {

            if (arg == "-h" || arg == "--help" ) {
                println( usage )
                exit()
            }

            if ( arg == "-v" || arg == "--verbose" ) {
                verbose = true
                // TODO I wanted to use the `continue` keyword here (instead of the else),
                // But feather silently failed!
                // Added the bug to feather's todo.txt
            } else {
                val src = arg
                val dest = "${File(src).pathWithoutExtension()}.png"

                if (verbose) {
                    println( "From $src to $dest" )
                }

                $(convert -background none "$src" "$dest").run()
            }
        }
    }

Static and NonStatic Method calls differ on how they cast the return type.

if (super.foo()) { ... } // Doesn't compile! See CQPlayer

Helper.resolveGenericType what if "index" isn't a

as? Foo within class Foo gives an error : Not a Class nor a ParameterizedType

Narrowing the type in a subclass doesn't work as intended :
    myAutoFlashPreferences.node("foo") is of type Preferences, not AutoFlushPreferences

if ( foo() == true ) ...
    Fails if foo() returns a Boolean Object value null.
    Probably similar for other primitives.

Test : Create a var of type interface, and use it in a smart string.
    I think it may barf, as we are doing "toString" on it, which will fail, because the interface doesn't implement toString,
    so we need to call toString on Object, not the interface???

Code if (myInterface == myOtherInterface) fails badly at runtime! Trying to call .equals on an interface.

if ( foo == null ) is using .equals
    Could use the same as ===

Sandbox checking
    Given that all Types are resolved LAZY are they really bound by the sandbox?
    Maybe it would be safer if we ignore the sandbox during the first phase, and
    check it in the compilation phase, by checking the class name for every method call/field access,
    including <init> - so we cannot create a subclass of an unsafe class.
    We would need to make sure that all asmDescriptions are built via the sandbox checking method,
        and not "hand-built".
    Also, make DAMN sure that Function creation is checking the sandbox.
    Have a set of tests specifically to test the sandbox.

Foo.instance.apply {...} does not allow Foo to be a Feather class in a later source file.

Two methods with the same signature are not tested for during parsing.

exitExpression - Does this work for e.g. myArray[2] += 3

All the AssignableStackEntries do a DUP BEFORE the adjustment.
    This is fine for postIncrementDecrement, but is it good for anything else?
    Also, can we optimise it out if the value isn't used?
        (in a similar way to the final expression of a block)
        We often have code such as : local ++    where the dup will have to be popped straight away.

Uses of Type.klass()
    If, Throw, Elvis

Override a method, narrowing the return type to a MetaDataType, and compileMethod throws,
as it is casting to a Class<*>.

Override a method with Object return type, and then give NO return type fails.
    Should return null??? or Unit???


If we have listOf<Baz>(a,b)
    What if "this" is also a Baz? it will actually call listOf(this,a,b)
        which is really not what we want!
    We really need a syntax for extension methods i.e. fun Baz.foo() { ... }
        vs fun foo( a : Baz ) { ... }

Cannot call a method from interface B, which is inherited from interface A
    e.g. myShape3d.toScad() when toScad() is defined in Shape, rather than Shape3d
    

All places that uses CommonSuperClass won't work for MetaDataClass
    e.g. feather types cannot be used as "if/else" results.

An overridden method doesn't fail at compile time when the return type is incompatible???


With multi-scripts and listOf<ClassInAnotherScript> *sometimes* fails
(probably based on compilation order)


Next
----

Allow annotations to use enum values.


Default floating point type : float or double?
    Also allow doubles to be explicitly specified using 1d 1.0d etc. (just like floats use 1f and 1.0f)

Is it worth creating a "static" top-level structure, so that all fields and functions are static?
    static Foo {
        var baz = 1
        fun blah() {}
    }
In kotlin, we do a similar thing WITHOUT any block around it. But I don't like it
OR
Allow functions at the top level, and put them in a class named after the file.
If a class is also declared with the same name, then merge the two??? With a warning?

Allow static imports from Feather classes


Add @Extension annotation to tag methods as extension functions. Allows: a.myFunction()
    Error if the function takes zero arguments
    Are they allowed on primitives?
    Error if attempted to use a non-static method as an extension
        Currently when we look for a method, we either find it, or not. This makes error messages tricky.
        If we send back a LIST of methods (some of which may be illegal), this will error messages meaningful.
    Tests for if methods are extensions should also be compatible with Kotlin's (without needing kotlin runtime)

Add @Infix attribute to tag methods as "infix". Allows: a myFunction b
    Error if the function doesn't have 2 arguments
    Can be an extension function OR a regular method
    Tests for if methods are infix should also be compatible with Kotlin's (without needing kotlin runtime)

Ensure that extension and inline methods defined within feather can be used.
    I think currently only "java" methods can be used as extensions???

Most Important
--------------

"*" (spread) operator when calling a method (valueArgument)

Constructors cannot be varargs at the moment.???

Secondary constructors (must chain to primary constructor eventually)
    Must chain to another constructor ABOVE this one.

Medium
------

Allow for maven dependencies from command line feather scripts (not in-app scripts).
    The script can contain something like this near the top :
        depends( nickthecoder.co.uk/paratask/1.2 )
    Look for existing cached maven files, and if not found, then create a dummy gradle file to download them.
        Then try again.
    Should we also have support for local (non-maven) jar files too?
        library( /home/nick/foo.jar )

Make more use of TypeUtils class
    e.g. use getRawType instead of Type.klass()
    Maybe make an extension function (inline?) the call to TypeUtils e.g.
        inline fun Type.getRawType() = TypeUtils.getRawType(this)

Think about how to write a field with a getter and setter.
    Syntax the same as Kotlin?
    How to determine if the property is a field or a getter/setter with private field?
        Maybe have "field" keyword :
            field val foo = 1
            field var bar = 1
        And if the "field" is omitted, then it is a getter/setter with a private field.
    Allow interfaces to have val s and vars (by declaring getter/setter methods).

listOf<String>( "Hello" ) can also be written as listOf( "Hello" )
    It's easy to hard-code a special case for listOf, but doing it in a generic way is HARD?
    If on type parameters given, but the function HAS type parameters :
        Collect all arguments in a map, keyed by the Type variable
        Then nearest-common-ancestor of each
    Only do this if everything uses a "simple" type argument, and not an "in/out" version.

Consider making Configuration a val to Compiler's constructor.
    Pass the configuration to the compiler's constructor, and make a copy it - do not use the original.

Consider reworking BinaryOperator and ArrayAccess to use a "GetSetter", so that the auto-casting stuff
is consistent across all assignable objects.

Less Important
--------------

Allow fully qualified class names wherever a class name can occur.

Feather command line :
    Accept a classpath (otherwise we can't use external jars)!
    Option to dump the human-readable bytecode (using ASM's viewer).

Add an "include jar", so that a script can be run from the command line without knowing its dependencies.
    Different ANTLR string points to a script which allows "includes".

Rewrite
=======

Look for all places where `Type.klass()` is used, and decide if it is appropriate
    Such places may mean that feather classes won't work, as they are not type Class during compilation.

Remove methods which use Class<*> where they should use Type.

Have a concept of an "Error", which is reportable to the user.
    A FeatherException has a list of Errors.
    We can choose how to handle Errors. The default is to throw a FeatherException
    when we first encounter one. But the command line version may choose to report
    each Error as it occurs and allow the compilation to continue.

How we look for method calls must be improved
    But this is now done only during the "resolving" phase.


How we resolve types must be improved


Intellisense
------------
We need to be able to parse syntactically "wrong" code (because there may be
        a trailing "." for example)
    This may mean we change the way the parser works... We walk the tree semi-manually???
    Try to recover from errors and continue parsing.
    Use the same strategy for syntax highlighting???

Look for val/var/fun in the given classloader as well as the current script,
    but do not try to compile other scripts.
    Cheap and dirty creation of MetaDataClasses for the current script.
    We look up the given classloader as well as the cheap MetaDataClasses for possible symbols.
However, version 1 could exclude symbols from the current script, and use the classloader only.


Optimisations
-------------

Keep track of the dependencies between classes/source files, so that a change to a (set of) file(s) can
do a partial recompilation. (Do a straight COPY of some class's byte code, and recompile the others, using
reflection on those classes which are not being recompiled).

Keep a map of type names -> Type, so that resolveType is quick to resolve the same type on subsequent calls
    Kept on Source?

++ is wasteful when the return value isn't needed. It duplicates the old value, and then pops it.
    Could replace StackEntry.compile(mv) with compile(mv, ignoreResult = false)

Look for compile(), then autoCast(), and replace with compileAutoCast()
    This will optimise. e.g. passing 0 to a method which expects a Double will cast at compile time rather than runtime.

Finishing Touches
-----------------

feather command to create "fat" jar files, including the contents of the included jars.
    There's existing code out there, which creates fat jars!