Адрес: ул. Б. Очаковская 32 Москва Россия
Наши официальные канал и чат в telegram, группа в ВКонтакте

YABCT (v0.1.0): как собрать 5.5k строк на Yabasic. Архитектура, код и грабли

Технический разбор разработки YABCT на Yabasic: модульная архитектура, парсинг CSV, генерация HTML, ISAK, TEM, грабли языка

Разговоры на любые темы касающихся языка Бейсик и его диалектов. Общение с администрацией и прочее
Аватара пользователя
Anton
Site Admin
Сообщения: 254
Зарегистрирован: Чт фев 08, 2024 7:03 pm
Откуда: Москва

ИИ и Yabasic. Что выдумывает нейросеть

Сообщение Anton »

YABCT (v0.1.0): как собрать 5.5k строк на Yabasic. Архитектура, код и грабли
Использовал DeepSeek как ассистента. Скажу сразу: на сложных архитектурных вещах он экономит кучу времени. Но когда дело доходит до написания кода на Yabasic, нейросеть начинает откровенно галлюцинировать. Модель обучена на тоннах Python и C++, поэтому она постоянно пытается приписать Yabasic функции и синтаксис из современных языков.

Я собрал список «мифов», которые ИИ предлагал внедрить в проект. Все примеры реальные: я копировал код от нейросети, пытался запустить, получал ошибку и переписывал руками.

Миф 1: Встроенная функция file_exists()

Что предлагал ИИ:

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

if file_exists("clients.csv") then
    // ...
end if
Реальность: В Yabasic такой функции нет.
Как решал: Пришлось писать свою. Причем просто открыть файл open fname$ for reading as #1 нельзя — если файла нет, интерпретатор вылетит с фатальной ошибкой. Работает только проверка через f = open(fname$, "r") и сравнение с нулем.

Миф 2: Оператор инкремента i++ или i += 1
Что предлагал ИИ:

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

i = 1
while i <= 10
    // ...
    i++
wend
Реальность: В Yabasic нет ни постфиксного инкремента, ни сокращенных операторов присваивания. Вообще.
Как решал: Писать полную форму i = i + 1.

Миф 3: Методы строк (как в Python)
Что предлагал ИИ:

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

s$ = s$.replace("a", "b")
s$ = s$.strip()
Реальность: В Yabasic строки — это не объекты. replace$ вообще нет в версии 2.91.1 (ее завезли только в 2.92.0). strip() нет.
Как решал: trim$() — это встроенная функция (работает). А замену replace пришлось писать руками через instr и mid$.

Миф 4: Режим for append
Что предлагал ИИ:

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

open "test.txt" for append as #1
print #1, "data"
Реальность: В Yabasic нет конструкции for append. Она выдает синтаксическую ошибку.
Как решал: Использовать строковый режим: open #1, "test.txt", "a"

Миф 5: Запятая в line input
Что предлагал ИИ:

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

line input #1, line$
Реальность: Запятая ломает парсер Yabasic. Это не QBasic и не VB.
Как решал: Убирать запятую: line input #1 line$. (Кстати, ИИ выдавал эту ошибку несколько раз, пока я не начал бить его по рукам в промпте).

Миф 6: Оператор остатка от деления %
Что предлагал ИИ:

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

if (i % 2 = 0) then
    // ...
end if
Реальность: В Yabasic нет оператора %. Более того, mod — это не оператор, а встроенная функция.
Как решал: if (mod(i, 2) = 0) then .

Миф 7: Получение длины массива
Что предлагал ИИ:

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

n = len(my_array$())
Реальность: len() в Yabasic работает только со строками. Для массивов такой функции нет.
Как решал: Передавать количество элементов в функцию вручную как аргумент (sub build(arr$(), n)).

Миф 8: Встроенный elseif через пробел
Что предлагал ИИ:

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

if x > 0 then
    // ...
else if x < 0 then
    // ...
end if
Реальность: Конструкция else if (два слова) не работает так, как в C или JS. Yabasic ждет слитное написание elsif.
Как решал: Менять на elsif.

Вывод

ИИ отлично справляется с рутиной: написать цикл, расставить скобки, сгенерировать однотипный HTML-тег. Но как только дело касается специфики Yabasic, он врет примерно в 30% случаев. И это кстати считаю заслуга нашего форума! Т.к. пару лет назад он врал на 90% :lol:

Если будете писать на Yabasic с помощью ИИ, обязательно давайте ему в контекст мануал (2484.de/yabasic) или файл с вашими заметками (я прям в проекте обычно веду YABASIC_NOTES, собирая там костыли да грабли). И всегда запускайте код перед тем, как вставлять его в проект.
Аватара пользователя
Anton
Site Admin
Сообщения: 254
Зарегистрирован: Чт фев 08, 2024 7:03 pm
Откуда: Москва

Как тестировать Yabasic (и зачем нужны бенчмарки)

Сообщение Anton »

Когда пишешь проект на более чем 5,5к строк, рано или поздно наступает момент, когда страшно что-то менять. Меняешь одну функцию в calc.yab — и внезапно перестает генерироваться HTML в html.yab. В нормальных языках для этого есть фреймворки и юнит-тесты. В Yabasic нет даже функции assert().

Поэтому пришлось сделать папку _tools/ и написать 10 тестовых скриптов вручную. Каждый скрипт — это просто набор вызовов функций с выводом в консоль ожидаемого и фактического результата. Если глазом видишь расхождение — значит, что-то сломал.

Но самое интересное — это тесты на поведение самого языка. Два из них спасли меня от очень неприятных багов.

1. Грабля: утечка dim из подпрограмм

В документации Yabasic не было четко написано, сбрасывается ли локальный массив, объявленный через dim внутри sub, при следующем вызове. Я написал маленький скрипт test_dim.yab, который вызывал подпрограмму три раза подряд и проверял, остались ли старые значения:

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

sub test_string_arr$()
    dim s$(3)
    // Проверяем, пустой ли массив
    if (s$(1) <> "" or s$(2) <> "" or s$(3) <> "") then
        print "  STALE (string): массив не очистился!"
    end if
    s$(1) = "A"
    s$(2) = "B"
    s$(3) = "C"
end sub

Результат шокировал: массив не очистился. То есть если бы я в html.yab объявил dim внутри функции генерации отчета, то при генерации пачки клиентов (команда html.yab all) данные первого клиента «протекли» бы в отчеты второго и третьего.

Спасение нашлось в функции local. Но test_local.yab показал вторую грань: local a$(10) работает и массив действительно сбрасывается, а вот local a$() без указания размера вызывает runtime error. Пришлось захардкодить размеры везде.

2. Бенчмарки строк

Вторая категория тестов — stress_test.yab и str_test.yab. Мне нужно было доказать (в первую очередь самому себе), что конкатенация строк через + — это зло.

Замерять в Yabasic сложно: функция time$ имеет формат HH-MM-SS-D, где D — это десятые доли секунды. Точнее 100 мс измерить нельзя. Но даже с такой точностью цифры впечатляют:
  • s$ = s$ + "x" в цикле на 100 000 итераций: 3300 мс.
  • string$(100000, "x"): 0 мс (быстрее, чем разрешение таймера).
  • Присваивание через mid$(s$, i, 1) = "x" в цикле на 100 000 итераций: 5400 мс.
Именно после этого теста в html.yab появился жесткий запрет на сборку HTML в строку. Только построчная запись через print #1 напрямую в файл.

Выжимка из кода test_local.yab

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

// ------------------------------------------------------------
// Тест 1: local массив — сброс между вызовами
// ------------------------------------------------------------
sub t1()
    local a$(5)
    local stale
    stale = 0
    if (a$(1) <> "" or a$(2) <> "" or a$(3) <> "") stale = 1
    a$(1) = "X"
    a$(2) = "Y"
    a$(3) = "Z"
    return stale
end sub

// ... (в главной программе) ...
print "[1] local array — reset between calls"
r = t1()
print "  call 1: stale = ", r, "   (expected 0)"
r = t1()
print "  call 2: stale = ", r, "   (expected 0)"
Бенчмарк конкатенации (str_test.yab)

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

// Время в десятых долях секунды (dt). Формат time$: HH-MM-SS-D.
sub time_dt()
    local p$(4), dummy
    dummy = split(time$, p$(), "-")
    return val(p$(1)) * 36000 + val(p$(2)) * 600 + val(p$(3)) * 10 + val(p$(4))
end sub

// ... (в главной программе) ...
N = 100000

print "[A] Concat via + - ", N, " appends"
t0 = time_dt()
s$ = ""
for i = 1 to N
    s$ = s$ + "x"
next i
t1 = time_dt()
print "  ", (t1 - t0) * 100, " ms (len = ", len(s$), ")"

print "[B] string$(", N, ", \"x\")"
t0 = time_dt()
s$ = string$(N, "x")
t1 = time_dt()
print "  ", (t1 - t0) * 100, " ms (len = ", len(s$), ")"
Аватара пользователя
Anton
Site Admin
Сообщения: 254
Зарегистрирован: Чт фев 08, 2024 7:03 pm
Откуда: Москва

Итоги, архитектурные слабости и что дальше

Сообщение Anton »

Вот и всё. Проект YABCT на 5.5к (5588 код программы + 2600 код тестов + 3893 документация) строк собран, протестирован и работает. Писал я его 34 дня, и за это время собрал не только рабочий инструмент, но и неплохой опыт взаимодействия с Yabasic + ИИ.

Если честно, когда я начинал, я думал, что главной проблемой станет отсутствие ООП или примитивная работа с файлами. Но оказалось, что интерпретатор вполне способен переварить проект такого размера, если правильно разложить модули. Основные сложности возникали не из-за математики или парсинга, а из-за странных решений самого языка (вроде dim в sub).

Тем не менее, если бы я начинал проект заново, кое-что я бы сделал иначе. Вот список архитектурных ошибок, которые я признаю:
  1. Кэширование clients.csv.
    Сейчас функция find_client$ каждый раз открывает файл clients.csv и читает его построчно с начала. Когда мы генерируем отчёты для всей базы (html.yab all), программа для каждого клиента заново читает файл. При базе из 500 человек это превращается в O(N²) и десятки секунд ожидания. Нужно было сразу при старте store.init() считывать базу в массив в оперативной памяти.
  2. Монолит html.yab.
    Файл разросся до 1400 строк. Сейчас в нём вперемешку лежат CSS, логика вывода таблиц, расчёт осей для графиков и ручная отрисовка SVG. Надо было разбить его на report.yab, charts.yab и fmt.yab.
  3. Глобальные переменные для ошибок.
    Система с last_error_code$ и last_error_detail$ работает, но это костыль. Приходится всегда помнить, что после вызова store.add_client() надо проверить, не равна ли ошибка нулю. В языках с исключениями это делается элегантнее.
  4. Свои велосипеды для строк.
    Своя функция int_to_str$ (из-за того, что str$ выдает экспоненту на больших числах), своя subst$ вместо replace$ (которой нет в 2.91.1). Если бы я знал про эти грабли сразу, заложил бы утилиту str_utils.yab в самом начале.
  5. Пять разных форматтеров чисел.
    В коде есть round1_field$, round2_field$, fmt_num$, fmt_num2$ и fmt2$. Они делают почти одно и то же, но писались в разное время для разных модулей. Надо было свести к двум базовым функциям.
В целом, язык справился. Да, местами он заставляет страдать (особенно когда дело касается строк и работы с датами), но для написания CLI-утилит и генерации HTML/CSV он вполне пригоден. Если правильно спроектировать модули и не пихать всё в один файл, на нём можно писать сложные вещи.

Весь исходный код (включая тесты и бенчмарки) выложен в этой ветке постах. Лицензия MIT, можно брать и разбирать.

Если есть вопросы по конкретным функциям, по архитектуре или по тому, как заставить нейросети писать на Yabasic — спрашивайте в этой теме. Отвечу по мере возможности.

Всем спасибо за внимание. =)

P.S. Версию v0.2.0 буду пилить после сбора обратной связи от пользователей. Но уже в процессе работы над v0.1.0 BACKLOG составил 520 строк, работать в принципе уже есть над чем :)