Можно (если утомительно) написать прямой машинный код. Возможно, вы записываете программу на ассемблере на листе бумаги, а затем переводите ее вручную в инструкции с числовым машинным кодом, которые вводите в память машины. Вы даже можете пропустить шаг ассемблера на бумаге, если запомнили числовые значения всех инструкций машинного кода - не редкость в те дни, хотите верьте, хотите нет!
Самые первые компьютеры были напрямую запрограммированы в двоичном виде путем переключения физических переключателей. Это было большим улучшением производительности, когда аппаратное обеспечение развивалось, позволяя программисту (или помощнику по вводу данных) вводить код в шестнадцатеричных числах с клавиатуры!
Программный ассемблер стал актуальным только тогда, когда стало доступно больше памяти (поскольку ассемблерный код занимает больше места, чем необработанный машинный код), а аппаратное обеспечение развилось, чтобы позволить буквенно-цифровой ввод. Итак, первые ассемблеры были написаны непосредственно людьми, свободно владеющими машинным кодом.
Когда у вас есть ассемблер, вы можете написать компилятор для языка более высокого уровня в ассемблере.
История для C имеет несколько этапов. Первый компилятор C был написан на B (предшественник C), который, в свою очередь, был написан на BCPL. BCPL - довольно простой язык (например, у него вообще нет типов), но он все же шаг вперед по сравнению с необработанным ассемблером. Итак, вы видите, как постепенно более сложные языки создаются на более простых языках вплоть до ассемблера. И сам C - довольно маленький и простой язык по сегодняшним стандартам.
Сегодня первый компилятор для нового языка часто пишется на C, но когда язык достигает определенной зрелости, он часто переписывается «сам по себе». Первый компилятор Java был написан на C, но позже переписан на Java. Первый компилятор C # был написан на C ++, но недавно он был переписан на C #. Компилятор / интерпретатор Python написан на C, но проект PyPy - это попытка переписать его на Python.
Хотя не всегда возможно написать компилятор / интерпретатор для языка на самом языке. Интерпретатор JavaScript, написанный на JavaScript, существует, но компиляторы / интерпретаторы в современных браузерах по-прежнему написаны на C или C ++ по соображениям производительности. JavaScript, написанный на JavaScript, просто слишком медленный.
Но вам не нужно использовать C как «начальный язык» для компилятора. Первый компилятор F # был написан на OCaml, который является другим языком, наиболее тесно связанным с F #. Когда компилятор был готов, он был переписан на F #. Первый компилятор для Perl 6 был написан на Haskell (чистый функциональный язык, сильно отличающийся от Perl), но теперь имеет компилятор, написанный на C.
Интересным примером является Rust, где первый компилятор был написан на OCaml (теперь он переписан на Rust). Это примечательно, потому что OCaml, как правило, считается более высоким уровнем, чем Rust, который является языком систем ближе к железу. Так что это не всегда языки более высокого уровня, реализованные в языках более низкого уровня, это также может быть наоборот.