The Poise programming language.
This is a rewrite of grace because grace was a mess (the name is a synonym).
- Parse argv properly, main function/file in general
- Testing
- Test individual important functions
- Fuzzing
- Think about object -> bool conversion
- Generally improve compiler errors and stop it from spitting out nonsense after one error, eg lambda capture errors
- Use hashes instead of strings in runtime
- Function names, namespace lookups, etc
- And maybe also in the compiler when we need to optimise that
- Package manager type thingy
- Type checker for those optional type hints
- Project manager/runner
- Dependency stuff (cloning a Git repo, structure convention, precompiled bytecode)
- Compiler warnings
- Optimisation
getCodeAtLine()
for imported files- Store number of lines in a file when it's loaded so
getNumLines
isn't so expensive - Try and remove static data structures
- Full UFCS +
Any
type annotation?- In general, I think I prefer the idea of having an
Any
type annotation as opposed to full UFCS. - However, adding an extension function to
Any
gets complicated. - Currently, each type has a list of its extension functions. Keeping this system, an extension function on
Any
would have to be added to all current and future types when compiled - We'd have to reverse this association, so each function knows which types it extends, but this is tedious to look up at runtime
- So maybe the current system is ok? A
PoiseFunction
instance in aValue
is basically a shared pointer, so it's not too crazy
- In general, I think I prefer the idea of having an
- Namespace qualified calls are a little messy
DualIndexSet
is great but let's optimise it a bitDone, just make a wrapper around a boost set!
- Use of more efficient sets/maps, and use these instead of vectors for things that are often looked up with
find_if
Pop unused expression/return resultsCreate values by calling type as a constructor, egInt()
,Float(1.0)
Implement types as first class objects - see below- Done except for user defined classes when we get to that...
Assertion failure in formatting compiler error output for missing semicolonsExpressions with primitivesShort-circuiting
VariablesFunctionsDeclarationCallingReturningLambda
Assignment statementsError handlingWhen we do constants, handle a constant value for construction Exceptions- Print only the first few call stack entries on error
throw
statement
If statementsWhile loopsNative functions- These are done, just need to actually implement everything else
Disallow calling a native function outside std files
Imports + NamespacesWildcardsMultiple imports in{}
Back a folder
Namespace aliasesExport functions- Dot functions
For imported functions...Put the namespace stuff into its own classFunctions need to know what namespace they're inTypes know what extension functions they haveCheck if that function's namespace has been imported to the namespace of the current function in the Vm
Two functions with the same name in a different namespace will override each-other- Add an ability for "static" extension methods - this is how we will do struct constructors (
Foo.new()
)
- Builtin objects
Use types::Type instead of Value::TypeInternal for type()Iterable collectionsTidy up access modifiersListsRangesCan't change the internal state of the range so that you can iterate over it many times, or concurrently
TupleDictsSetsIndex operator
Special constructors for the aboveBinary ops for the above- Could we do some kind of rust/c# linq/ranges style lazy evaluation...
Option and Result types?No need,try
assignments work like Results, everything is an option because anything can benone
- implementis_none()
andis_some()
onAny
For loopsMake sure everything's working fine for more collections we add
Zig style try assignmentCould this be like a unary operator as opposed to only valid on assignment, sofoo(try bar())
is valid?
- Parameter packing and unpacking
Expand unpacking into collectionsRemember this when we add Dicts and Sets...
Unpack a collection- Multiple assignments on one line (
a, b = b, a
) or assigning an unpack (a, b = ...collection
)- Partial unpacking -
var (a, b, ...rest) = ...collection
unpacks the first two items intoa
andb
and the remaining intorest
. How to parsevar a, b, c = try ...expr
?Doesn't really work, because exceptions get a bit strange. This implementation is sorted now.
- How badly do we want multiple assignments? Right now it works for variable declarations, but not for...
- Reassigning variables
- Assigning indexing etc
- It would also imply the ability to return multiple values from a function
- I'm just hesitant because of how annoying it is to compile LOL but maybe there's a better way to handle assignments in general in the compiler
- Partial unpacking -
Friendship ended withPoisePack
,PoiseList
is my best friend nowA "pack" will not be a unique type, and you will simply be able to unpack any collection
- Construct
Type
instance,Type
ident Union type annotation so that we can implement functions on multiple collections- Have type aliases, eg
type OrderedCollection = List|Range
- You should be able to export and import these
- Maybe leave this until we do user defined classes, and we can come up with a nice generic way to handle it, since we'll have to work off of identifiers rather than builtin type keywords.
- Have type aliases, eg
Break/continue statements- Structs
Member variable access- Extension function access
- Need to generate
PoiseType
instances for these, and hook them into everything else - we may need to do some type of verification step on extension methods when the vm starts running Need a class for instancestypeof
for structs and struct instances such that forstruct Foo {}
,typeof(Foo) == Type
andtypeof(Foo{}) == Foo
- Objects in constant expressions to allow for object default struct values
- This shouldn't be complicated, there's really no reason not to allow it, we just can't call functions
- Need a deep clone mechanism for objects so that structs instantiated with default values get deep copies of objects
GC for cyclesInvestigate horrible performance with Clang with cycles.poise (n=10000)- Ah, so using
unordered_set
instead ofvector
solved this - But, I bet performance is better for small amounts of objects - test this, and decide which should we optimise for? Is swapping between the two depending on the load viable?
- Optimise the sweep frequency - perhaps update the graph constantly with allocations/deallocations?
Binary/Hex literalsDigit separatorsConstantsConstant expressionsType checked and folded into bytecode
- Crashes in compiler on EOF token
- Pattern matching
- Ifs as expressions
- Do we want this, or some kind of ternary mechanism?
Identifier loading in runtime can be simplified now that strings are internedSingle expression lambdasBut, perhaps we could do|| => <expr>
and then also have single line functions like that too (func foo() => <expr>
)Figure out how to compile this as a statement rather than an expression - need to return null in case of a print or loop statement etc.
Type hints- Expand to include user defined classes when we do these
- Could we compile time check for the existence of the class? Is there a situation where you'd need to define a function that takes a class, before defining that class?
- Yes, for cyclic dependant files.
Allow >= 2 generic types for dictionaries, tuples etc when we do those
- Expand to include user defined classes when we do these
- CL arg parsing
- Standard Library
- Precompile as bytecode files
- Finish iterators for DualIndexSet
Object
|
| _______________________ Iterable
|__|_______|__________ |
| | | | |______|____ Hashable
| | | | | | | |
List Range Dict Set