Sunday, 7 August 2011

Some more grammar

With the pending completion (finally!) of the parser, I've been playing with writing more and more DeadMG++, not just for fun (although it is!) but also to stress the parser and lexer, and make sure that the lexer produces all the tokens it should, and that sort of thing. Also, gaining some experience here is rapidly looking like it's going to be important. Have a look at the following code:


type StandardAllocatorType {
    HashMap(int, VariableReference) Pools;
    VariableReference Default := HeapAllocator;
    GetAllocator()(Size) {
        if (Size > 48)
            return Default;
        if (Pools.Find(Size) == Pools.End())
            Pools[Size] = Pool(Size);
        return Pools[Size];
    }
public:

    Allocate(Type, TypeReference... Types)(Types.RvalueReference()... arguments) {
        compiletime {
            Allocator := GetAllocator(Type.Size());
            runtime {
                return Allocator.Allocate(Type, forward(arguments)...);
            }
        }
    }
    Allocate(Size)() {
        compiletime {
            Allocator := GetPoolAllocator(Size);
            runtime {
                return Allocator.Allocate(Size);
            }
        }
    }
    Allocate()(Size) {
        runtime {
            return Default.Allocate(Size);
        }
    }
}



Firstly, I know that I haven't spoken about VariableReference. It performs a job similar to the imaginatively similarly named FunctionReference and TypeReference- it refers to a variable which exists in the next pass, i.e., a static variable, where I have created one of type "HeapAllocator", whatever that is (although in the logic at hand, that realistically means malloc, give or take).

The main problem here is that it's not explicit about when the functions, or variables, exist- namely, the variable Pools has no indication when it exists and what disaster you might create by attempting to refer to it at run-time. I will have to alter the compiletime/runtime blocks to include functions and variables in types, too. Something like

type StandardAllocatorType {
    compiletime {
        HashMap(int, VariableReference) Pools;
        VariableReference Default := HeapAllocator;
        GetAllocator()(Size) {
            if (Size > 48)
                return Default;
            if (Pools.Find(Size) == Pools.End())
                Pools[Size] = Pool(Size);
            return Pools[Size];
        }
    }
public:
    Allocate(Type, TypeReference... Types)(Types.RvalueReference()... arguments) {
        compiletime {
            Allocator := GetAllocator(Type.Size());
            runtime {
                return Allocator.Allocate(Type, forward(arguments)...);
            }
        }
    }
    Allocate(Size)() {
        compiletime {
            Allocator := GetPoolAllocator(Type.Size());
            runtime {
                return Allocator.Allocate(Size);
            }
        }
    }
    Allocate()(Size) {
        return Default.Allocate(Size);
    }
}


It's a bit of a mess. This one object exists at both compile-time and run-time. I imagined that most objects would not exist at compile-time and run-time. Normally, it wouldn't be necessary, but we have to have one object of one type. Unless, I added the ability to execute on namespaces too. Then the object could be split into it's two logical parts- run-time allocation, and compile-time type/size tracking, and I might be able to ditch this whole compiletime {} runtime {} thing. Might. That would be swell.

It's also worth noting that I had to sacrifice using the dot notation for namespaces and types as well, and had to revent to the old C++ double colon for that, but I did manage to scrap the var keyword, which I like. Was not a fan of that at all. If I do add the ability to manipulate namespaces, which I guess is just logical after functions, types, expressions, and variables, then perhaps I can get back the dot.

No comments:

Post a Comment