Из всего, что вы надеетесь выполнить, наиболее сложным требованием может быть «очень мало (макс. 1-2 KLOC)». Я думаю, что ваше первое требование (генерация вывода в формате ELF) само по себе может занять более тысячи строк кода.
Один из способов упростить проблему, по крайней мере, для начала, - это сгенерировать код в тексте на языке ассемблера, который затем будет передан в существующий ассемблер ( nasm будет хорошим выбором). Ассемблер позаботится о генерации фактического машинного кода, а также всего специального кода ELF, необходимого для создания фактического исполняемого исполняемого файла. Тогда ваша работа сводится к анализу языка и генерации ассемблерного кода. Когда ваш проект достигнет точки, в которой вы хотите удалить зависимость от ассемблера, вы можете переписать эту часть самостоятельно и подключить ее в любое время.
Если бы я был тобой, я мог бы начать с ассемблера и строить на нем части. Простейший «компилятор» может использовать язык с несколькими очень простыми возможными утверждениями:
print "hello"
a = 5
print a
и переведите это на язык ассемблера. Как только вы это заработаете, вы можете создать лексер, парсер и абстрактное синтаксическое дерево и генератор кода, которые являются большинством частей, которые вам понадобятся для современного блочно-структурированного языка.
Удачи!