How much effort would it take to implement inline variables? When I was modifying compiler source to disable/overwrite/mangle/shorten RTTI as much as possible because I so hate it what I saw was codebase so huge, complex, so hard to change, so hard to add a new feature, to extend syntax. Commits on gitlab are mostly little bugfixes for RTL, nothing going on with the compiler itself.
Delphi has them for 5 years.
Probably not too much, I've started the past few weeks playing around in the compiler, and it may look intimidating at first, but once you find out the interesting points, you will quickly find out that even though the code is really cumbersome to read (no casing at all, inconsistent casing between declaration and usage, mixing of german and english identifier names, etc.), it's surprisingly easy to add new language features, at least as long as they mostly concern AST parsing.
For your specific request, just look at the function read_var_decls in pdecvar.pas. It's a giant function, handling all of the different cases (local definition, global definition, procedure variables, etc.), but when you get down to it, the most important bits are actually not that much, first you must create a tlocalvarsym (great no-casing naming, get used to it, sometimes it's interspersed with snake casing, because who needs consistency anyways) and add it to the functions symtable.
Additionally you need to create the code for the assignment within the AST. For this which you can read the right part of the assignment using the "sub_expr" function to generate a tassignmentnode (with the left hand side being a variable access to your newly created symbol). This should also be pretty straight forward when you look at the assignment code in line 4925 of pexpr.pas.
So just adding inline variables should not be that hard. What is probably going to be harder is tracking scopes, for this you would need to keep a chain (linked list) of currently active tstatementnodes (see statement_block function in pstatmnt.pas) and then map visibility within the "searchsym_maybe_with_symoption" function (symtable.pas).
All in all it should be managable to get a proof of concept quite quickly. But then of course, the devil is in the details, e.g. how does this work with generics, how does it work with anonymous functions, etc.
It is quite easy to do add something as a proof of concept, but it of course is much harder to do it right. And of course, you need a lot of tests.
Oh the humanity..
I personally see so much opportunities to use inline variable. Sometimes just need one, Im writing fast, testing code, testing things, need one more variable, need other type variable, change type from A to W, and everytime.. scroll up and find them, or hover over, click, but still need to go back to code, so scroll over tens or hundreds lines of code (I use debug breakpoints to mark the line to locate it visually while grabbing scrollbar and looking for where I was, duh).
As I stated before, requiring scoping for your variables is at most just a hotfix for unreadable code. When you have to jump hundreds of lines of code, your code is to complex.
One of the typical examples for very good code is the Linux kernel, it's probably the best and most consistently structured large code base in existance, and pretty much everyone who has worked with it praises the code quality. And it archives this with ony a few rules, and one strict ruler (Torvalds) who enforces them with an iron fist.
Two of those are, functions should not be larger than 1-2 (console) screens (i.e. ~40 lines), and variables are always declared on the top of the function, which they do voluntarily, even though C allows inline variables, they do not use that feature to have better code.
For example, I've just gone into the kernel source and took a kernel/dma/remap.c file at random, I never saw this file before, I just clicked somewhere. And in this file there are only 4 functions relating to DMA remapping, the longest of which looks like this:
/*
* Remaps an allocated contiguous region into another vm_area.
* Cannot be used in non-sleeping contexts
*/
void *dma_common_contiguous_remap(struct page *page, size_t size,
pgprot_t prot, const void *caller)
{
int count = PAGE_ALIGN(size) >> PAGE_SHIFT;
struct page **pages;
void *vaddr;
int i;
pages = kvmalloc_array(count, sizeof(struct page *), GFP_KERNEL);
if (!pages)
return NULL;
for (i = 0; i < count; i++)
pages[i] = nth_page(page, i);
vaddr = vmap(pages, count, VM_DMA_COHERENT, prot);
kvfree(pages);
return vaddr;
}
Variable definitions on the top, and not more than 14 lines of code.
Of course there are a few exceptions, especially driver code tends to get a bit messy, most of the kernel code looks like this (just go and look for yourself:
https://github.com/torvalds/linux/tree/master/kernel).
There is no reason to write giant unreadable functions. If the Linux kernel can have such clean and well structured code, and still be one of the largest and most important software projects in the world, while also having enough performance to power the OS running in billions of devices, then there is no reason why whatever (comparatively) small app you are writing cant.