Кроссплатформенный, быстрый, Kotlin-подобный ЯП!

Всё, что касается программирования на старых языках или для старых систем

Re: Кроссплатформенный, быстрый, Kotlin-подобный ЯП!

Сообщение StoYazykov » 14 июл 2026, 11:02

Итааааак....
Теперь такая программка
Код: Выделить всё
@print("Input string: ")
var String: a
a = @readln(0))
@print("Your string: ")
@println(a)

Код: Выделить всё
-- VM --

Bytecode size: 20
Input string: Hello World!
Your string: Hello World!

root@artem:~/c#

Остались только циклы и функции... (и массивы).

P. S. Кроме того заметим, что эта программка может быть записана и так:
Код: Выделить всё
@print("Input string: ")var String: a a =@readln(0)) @print("Your string: ")@println(a)

И отдельные инструкции в одной строке не надо разделять точкой с запятой.
Последний раз редактировалось StoYazykov 14 июл 2026, 11:04, всего редактировалось 1 раз.
Самое тёмное дело - это строки в C

Объектно-ориентированное программирование -- метод изготовления граблей по принципу матрешки.

http://revival.narod.ws

Изображение
Аватара пользователя
StoYazykov
Мастер Даунгрейда
 
Сообщения: 278
Зарегистрирован: 25 дек 2023, 11:25
Откуда: Казань
Железо: Intel Pentium MMX 166 MHz, 8 и 2 ГБ HDD, 80 MB RAM; AMD A8-6410 APU with Radeon R5 Graphics, 16 ГБ

Re: Кроссплатформенный, быстрый, Kotlin-подобный ЯП!

Сообщение IBMForever » 16 июл 2026, 21:25

Опять пишет: нет такого юзера!.
P.S. Linux никогда не пользовался.
Аватара пользователя
IBMForever
Мастер Даунгрейда
 
Сообщения: 303
Зарегистрирован: 24 авг 2022, 00:23
Железо: IBM PC 286 AT

Re: Кроссплатформенный, быстрый, Kotlin-подобный ЯП!

Сообщение StoYazykov » 17 июл 2026, 16:58

IBMForever, в личку.
Самое тёмное дело - это строки в C

Объектно-ориентированное программирование -- метод изготовления граблей по принципу матрешки.

http://revival.narod.ws

Изображение
Аватара пользователя
StoYazykov
Мастер Даунгрейда
 
Сообщения: 278
Зарегистрирован: 25 дек 2023, 11:25
Откуда: Казань
Железо: Intel Pentium MMX 166 MHz, 8 и 2 ГБ HDD, 80 MB RAM; AMD A8-6410 APU with Radeon R5 Graphics, 16 ГБ

Re: Кроссплатформенный, быстрый, Kotlin-подобный ЯП!

Сообщение StoYazykov » 20 июл 2026, 23:42

В общем.
Решено переходить на AST (Abstract Syntax Tree, Абстрактное Синтаксическое Дерево).
Это сделает код более сформированным и уберёт некоторые дублированные места.
Самое тёмное дело - это строки в C

Объектно-ориентированное программирование -- метод изготовления граблей по принципу матрешки.

http://revival.narod.ws

Изображение
Аватара пользователя
StoYazykov
Мастер Даунгрейда
 
Сообщения: 278
Зарегистрирован: 25 дек 2023, 11:25
Откуда: Казань
Железо: Intel Pentium MMX 166 MHz, 8 и 2 ГБ HDD, 80 MB RAM; AMD A8-6410 APU with Radeon R5 Graphics, 16 ГБ

Re: Кроссплатформенный, быстрый, Kotlin-подобный ЯП!

Сообщение IBMForever » 21 июл 2026, 06:34

StoYazykov писал(а):В общем.
Решено переходить на AST (Abstract Syntax Tree, Абстрактное Синтаксическое Дерево).
Это сделает код более сформированным и уберёт некоторые дублированные места.

Что такое AST? Какие преимущества дает? Можно ли будет подпрограммы на Си использовать?
Аватара пользователя
IBMForever
Мастер Даунгрейда
 
Сообщения: 303
Зарегистрирован: 24 авг 2022, 00:23
Железо: IBM PC 286 AT

Re: Кроссплатформенный, быстрый, Kotlin-подобный ЯП!

Сообщение StoYazykov » 21 июл 2026, 10:52

IBMForever,


Что такое AST.

Как работают однопроходные компиляторы (на примере моей реализации).

Однопроходный компилятор - самый простой по своему устройству. Он проходит по исходнику, превращает его в массив токенов (это делает лексер), а затем проходится по этому массиву и генерирует машинный код (или байт-код), записывая его в файл (парсер).
Токен - это стуктура, состоящая из двух полей: типа и значения.

Разберём работу компилятора на простенькой программе.

Код: Выделить всё
typedef enum TokenType {
    LCALL, // вызов нативной функции, @
    NUMBER, // число
    STRING, // строка
    ID, // например, имя переменной и т.д.
    ASSIGN, // =
    LPAREN, // {
    RPAREN, // }
    LBRACE, // (
    RBRACE, // )
    ... // и т.д. и т.п.
};
typedef struct {
    TokenType type;
    char *value;
} Token;


Например, есть у нас такая программка:
Код: Выделить всё
@println(123)


После лексера массив токенов:

type=LCALL, value="@"
type=ID, value="println"
type=LBRACE, value="("
type=NUMBER, value="123"
type=RBRACE, value=")"

То есть лексер, как бы обезличивает исходый код и превращает его в плотный массив токенов, над которым собственно парсер уже спокойно работает, и парсеру уже не надо возиться с пробелами, отсупами и прочим.

Затем по этому массиву токенов проходит парсер.
Он видит токен с типом LCALL, получает следующий токен (ID), записывает его в образ кучи, получает следующий токен, убеждается, что он является LBRACE, (если нет - опять-таки ошибка), увеличивает указатель на текущий токен, он указывает уже на 5, затем он вызывает функцию par_parExpr(), которая парсит арифметические выражения, результат уже лежит в байт-коде, и осталось только добавить в массив байт-кода вызов нативной функции.
Делается это так:
Код: Выделить всё
par_render(a, (Vm){CALL, selszu(АДРЕС_ИМЕНИ_НАТИВНОЙ_БИБЛИОТЕКИ_И_ФУНКЦИИ_В_НЕЙ_В_КУЧЕ), АДРЕС_ИМЕНИ_НАТИВНОЙ_БИБЛИОТЕКИ_И_ФУНКЦИИ_В_НЕЙ_В_КУЧЕ);

Ну а потом парсер завершает свою работу и начинает работать функция par_free(), которая уже записывает в .cvm-файлик весь массив байт-кода одним махом. ПОтом файлик закрывается, вызывается free() для всего, что надо очистить, и компилятор завершается. Свою работу он сделал.



А теперь разберём, как работает AST.

Первый проход - построение синтаксического дерева. Для этой программы оно будет выглядеть так:

Код: Выделить всё
Program
|--- Statement (CALL)
       |--- FunctionName: "println"
       |--- Arguments:
             |--- Expression (Literal)   
                   |--- Value: "123"
                   |--- Type: Integer


Второй проход - это генерация байт-кода.

Главная функция - codegen().
Она видит узел Program и... вызывает сама себя на его единственного "ребёнка" - Statement.
Она записывает в кучу FunctionName, сохраняет его смещение в куче... но пока не пишет CALL в байт-код, а вызывает сама себя на Arguments.
В Arguments она снова вызывает сама себя на Expression, а потом рендерит в байт-код:

Код: Выделить всё
PUSH INT|1 123

(1 - это минимальное количество байт, необходимых для умещения этого числа. В данном случае, 123 входит в диапазон -128 - 127 и поэтому ей хватает одного байта).
После чего эта функция завершается и...
Вспомните: а кто её вызывал?
Правильно, она сама, при обработке Arguments. Потом она так же входит из Arguments, попадает в Statement, и тут-то и рендерится вызов нативки.
Потом она выходит в Program, и... всё! Работа парсера окончена!

P.S. Насчёт подпрограмм на Си - AST с этим никак не связан. Нет.
Но можно написать динамическую библиотеку с функциями (т.е. как раз подпрограммами) на Си, добавить её в imports-table.txt и вызывать через
Код: Выделить всё
@ИМЯ_ФУНКЦИИ(АРГУМЕНТЫ)
Последний раз редактировалось StoYazykov 21 июл 2026, 10:54, всего редактировалось 1 раз.
Самое тёмное дело - это строки в C

Объектно-ориентированное программирование -- метод изготовления граблей по принципу матрешки.

http://revival.narod.ws

Изображение
Аватара пользователя
StoYazykov
Мастер Даунгрейда
 
Сообщения: 278
Зарегистрирован: 25 дек 2023, 11:25
Откуда: Казань
Железо: Intel Pentium MMX 166 MHz, 8 и 2 ГБ HDD, 80 MB RAM; AMD A8-6410 APU with Radeon R5 Graphics, 16 ГБ

Re: Кроссплатформенный, быстрый, Kotlin-подобный ЯП!

Сообщение StoYazykov » 23 июл 2026, 14:34

Итак, теперь можно написать так:
Код: Выделить всё
@println(5+2*3/8*07843)


То есть - теперь есть арифметические выражения.
Сгенерированное дерево AST в этом случае будет таким:
Код: Выделить всё
Program
  CALL: println
    BINARY
      LITERAL: 5
      BINARY
        BINARY
          BINARY
            LITERAL: 2
            LITERAL: 3
          LITERAL: 8
        LITERAL: 07843
Самое тёмное дело - это строки в C

Объектно-ориентированное программирование -- метод изготовления граблей по принципу матрешки.

http://revival.narod.ws

Изображение
Аватара пользователя
StoYazykov
Мастер Даунгрейда
 
Сообщения: 278
Зарегистрирован: 25 дек 2023, 11:25
Откуда: Казань
Железо: Intel Pentium MMX 166 MHz, 8 и 2 ГБ HDD, 80 MB RAM; AMD A8-6410 APU with Radeon R5 Graphics, 16 ГБ

Re: Кроссплатформенный, быстрый, Kotlin-подобный ЯП!

Сообщение StoYazykov » 29 июл 2026, 13:35

Добавлена свёртка констант (Constant Folding).
Это такой способ оптимизации, когда компилятор вычисляет арифметические выражения с __константами__ на этапе компиляции, следовательно, байт-код сокращается.
Например - вот такое дерево (исходный код - @println(5+2))
Код: Выделить всё
Program
  CALL: println
    BINARY
      LITERAL: 5
      LITERAL: 2

- превратится, в вот такое:

Код: Выделить всё
Program
  CALL: println
    LITERAL: 7

что аналогично @println(7).

Но если бы мы использовали в выражении переменные - оно бы осталось таким же, каким и было (ибо значение переменной не предугадать - только в некоторых случаях, и эти случаи ещё не обрабатываются).
Последний раз редактировалось StoYazykov 23 июл 2026, 17:21, всего редактировалось 1 раз.
Самое тёмное дело - это строки в C

Объектно-ориентированное программирование -- метод изготовления граблей по принципу матрешки.

http://revival.narod.ws

Изображение
Аватара пользователя
StoYazykov
Мастер Даунгрейда
 
Сообщения: 278
Зарегистрирован: 25 дек 2023, 11:25
Откуда: Казань
Железо: Intel Pentium MMX 166 MHz, 8 и 2 ГБ HDD, 80 MB RAM; AMD A8-6410 APU with Radeon R5 Graphics, 16 ГБ

Re: Кроссплатформенный, быстрый, Kotlin-подобный ЯП!

Сообщение StoYazykov » 29 июл 2026, 22:25

Теперь есть и условные переходы (if).
PS: Теперь и while. Ну, здравствуй, Тьюринг-полнота!

 Развернуть: Пример программки (while не юзается - только if)
Код: Выделить всё
var Int a
a = @readint("Введите любое число: ")
if(a > 2) {
   @println("Вы ввели число больше двух!")
}

Результат:

Код: Выделить всё
root@admin:~/c# ./chkvm demo.cvm
Введите любое число: 5
Вы ввели число больше двух!
root@admin:~/c# ./chkvm demo.cvm
Введите любое число: 2
root@admin:~/c#
Последний раз редактировалось StoYazykov 29 июл 2026, 22:24, всего редактировалось 3 раз(а).
Самое тёмное дело - это строки в C

Объектно-ориентированное программирование -- метод изготовления граблей по принципу матрешки.

http://revival.narod.ws

Изображение
Аватара пользователя
StoYazykov
Мастер Даунгрейда
 
Сообщения: 278
Зарегистрирован: 25 дек 2023, 11:25
Откуда: Казань
Железо: Intel Pentium MMX 166 MHz, 8 и 2 ГБ HDD, 80 MB RAM; AMD A8-6410 APU with Radeon R5 Graphics, 16 ГБ

Re: Кроссплатформенный, быстрый, Kotlin-подобный ЯП!

Сообщение StoYazykov » 02 авг 2026, 22:39

Наконец-то. Пофиксил незамеееетный баг в par_parBlock().
Вложенные циклы, работали неверно. Оказалось - в par_parBlock() надо было вставить строчку a->p++. И всё сразу заработало.
В попытках понять природу сего бага, я переписал часть компилятора, дотошно проверил векторы, переписал формат прыжков, часть ВМ, дизассемблер, сделал безтипизированный стек... а ларчик просто открывался!

Но - бестипизированый стек, останется.
И ещё - нативные функции больше не будут заботиться о типах. Почему.
Имена функций, будут манглированные (то бишь, искажённые). Например, в коде - будет объявлено

Код: Выделить всё
native Void println(Int num)


Компилятор превратит это в вызов нативной функции v_println_i(size_t argc, int64_t *argp, ChkEnv *env).
А сама функция - будет просто делать printf("%ll", argp[0]);.
И всё!
(унификация такая - для того, чтобы, не делать трамплины, не пользоваться ffi_lib'ой и прочая, прочая).
Самое тёмное дело - это строки в C

Объектно-ориентированное программирование -- метод изготовления граблей по принципу матрешки.

http://revival.narod.ws

Изображение
Аватара пользователя
StoYazykov
Мастер Даунгрейда
 
Сообщения: 278
Зарегистрирован: 25 дек 2023, 11:25
Откуда: Казань
Железо: Intel Pentium MMX 166 MHz, 8 и 2 ГБ HDD, 80 MB RAM; AMD A8-6410 APU with Radeon R5 Graphics, 16 ГБ

Пред.

Вернуться в Программирование

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 8