• code FORTH вызов-принят
    Решение задачи из #2745090 на gforth. На оптимальность решения не претендую (оно наивное), также интересно было самому написать - в реализации стандартных слов (например, .) не подглядывал. Замечания приветствуются. Комментариями снабжать сперва начал (первая строка), но это выглядело странно, так что оставил только карты стека. Выполнять например так: -234 n>s type
    
    \ create string buffer with 20 cells alloting
    create string-buffer 20 cells allot
    
    : n>s ( n --  c-addr u )
    	dup 0< if negate '-' swap else 0 swap then 0 ( minus-char number length )
    	begin 1+ swap 10 /mod swap '0' + -rot tuck 0= until nip
    	1+ dup roll swap ( chars length )
    	dup begin 2dup - string-buffer + 1 4 roll fill 1- dup 0= until drop
    	string-buffer swap
    ;
    ♡ recommended by @mabu, @agr

Replies (70)

  • @Renha, 1. Это без библиотек времени выполнения?
    2. Каков размер получаемого екзешника?
    3. Какие функции импортируются в екзешнике?
  • @Renha, т.к. @mabu #2745090 решал без какого-то RTL, замечу, что я решал без FFL
  • @mabu, вот фиг знает. По-моему с компиляцией экзешника у gforth всё плохо. Даже image file не удалось под вендой сконпелять. Так что это не к соревновательности по байтам. Ну можно скорость посравнивать разве что. Дополнительные словари не подключались, что такое RTL в контексте forth я затрудняюсь сказать, ну я ничего не подключал, использовал только стандартный словарь
  • @Renha, import Data.List
    import Data.Char

    numtochar :: Int -> String
    numtochar 0 = "0"
    numtochar a
    | a < 0 = '-':numtochar (negate a)
    | otherwise = reverse $ unfoldr go a

    go 0 = Nothing
    go x = let (q,r) = x `divMod` 10
    in Just (intToDigit r, q)


    если забить на кодировки, то intToDigit можно было б заменить на арифметику.
  • @qnikst, а, размер программы не важен. Можно поиграться, но скорее всего лень будет
  • @Renha, RTL — это RuntimeLibrary, библиотеки времени выполнения. В них содержится множество встроенных функций, таких как ToString и Print, а также проводится инициализация аргументов командной строки, критических секций и прочего.
  • @Renha, Вот полученный ассемблерный листинг этого Моего когда pastebin.com
  • @mabu, кодаБыстрофикс.
  • @Renha, попробовал под SPF запустить (чтобы потом екзешник сделать), так он мало того что регистрозависим, ещё и какие-то слова иначе реализованы, из-за чего недобор в стеке происходит
  • @Renha, Как тогда им пользоваться, если он не делает исполняемые файлы? Как интерпретатором?
  • @mabu, в общем случае да. Даже если экзешник я получу, это будет в лучшем случае интерпретатор байткода с байткодом. Встраиваемоприменяемость форта обеспечивается тем, что интерпретатор этот очень простой и прогается даже самостоятельно за день, весит мало, и т.д
  • @Renha, Фрибейсик компилирует прямо в инструкции для процессора, никакого байткода, интерпретации и виртуальной машины.
  • @mabu, а тебе не будет сложно натравить на программу аналог ldd?
    (http://stackoverflow.com/questions/1993673/what-is-the-equivalent-of-linuxs-ldd-on-windows)
  • @qnikst, Что такое ldd?
  • @qnikst, Это утилита, которая выдаёт импортируемые и экспортируемые имена функций?
  • @qnikst, Например, в левой колонке таблица импорта функций.
  • @mabu, исписок подгружаемых shared objects, я вот просто в асмовыхлопе вижу call, и хочу понять откуда адреса функций, которые вызываются.
  • @mabu, ясно
  • @qnikst, Это которые?
    call _GetStdHandle@4
    call _GetProcessHeap@0
    call _HeapAlloc@12
    call _WriteConsoleW@20
    call _HeapFree@12
    call _ExitProcess@4
    Так это же WinAPI-функции, их адреса заполняет загрузчик. А все остальные три функции в ассемблерном листинге.
  • @qnikst, Под shared objects ты подразумеваешь динамически загружаемые библиотеки DLL? Тут только одна, это kernel32.dll
  • @qnikst, интересно.. асмо выхлоп сишной выхлоп меньше, рапортуется, что размер больше.. из-за размера блоков fs чтоли?
  • @qnikst, Что чего меньше и больше? ЯННП.
  • @mabu, raw асмовыхлоп: dpaste.com
    человекочитаемый асмовыхлоп: dpaste.com
    исходный код: dpaste.com
    (единственное, что не проверяется это размер буффера, но делается в пару строк, и никакого бреда типа размера числа не нужно)

    libc ипользуется, причем жирная glibc, т.к. обёрка ядровызовов это дело userspace

    соотв к rtl подход немного другой (а не catch22.net ).
  • @qnikst, А ты можешь без всяких libc написать? Я ведь мог бы всесто всего этого нагромождения кода сделать всё одной строкой:
    Print WStr(3123)
    Но этот однострочник в exe-виде превращается в восемь килобайт против 2048 байт. Всё потому, тут используются встроенные функции, которые тянут за собой всю библиотеку времени выполнения.

    (Размер числа нужен только для того, чтобы правильно определить буфер, куда будет записываться строка. Можно было взять буфер большего размера и не делать эту функцию.)
  • @mabu, Могу, если напишешь без использования kernel.dll. не вижу правда в этом никакого практического смысла..
  • @qnikst, Без kernel32.dll написать нельзя, потому что в этой библиотеке лежат функции выделения памяти из кучи и вывод на консоль.
  • @mabu, А чо руками сделать сискол никак? Вот и не ругайся тогда на используемый мной putc и c function call convention.
  • @qnikst, пристыженный, я оптимизировал код forth-варианта, убрав бред типа размеров числа заодно:

    20 constant buffer-length
    create string-buffer buffer-length cells allot

    : n>s ( n — c-addr u )
    dup abs string-buffer buffer-length + tuck
    begin 1- swap 10 /mod -rot '0' + over 1 rot fill over 0= until nip
    rot 0< if 1- dup 1 '-' fill else then tuck —
    ;
  • @mabu, Вообще без kernek32.dll и функций управления кучами и выводом на консоль Я могу обойтись только если Я нативное приложение. Хотя в этом случае будет привязка к ntdll.dll.
  • @qnikst, Лол, ну ты даёшь. Все функции из /19 — это и есть системные вызовы WinAPI операционной системы.
    А всякие твои putc лишь слои ваты над виндовой WriteConsole.
  • @Renha, Лаконичненько. Жаль, что не компилируется в екзешник.
  • @mabu, я работаю над этим
  • @mabu, получилось с помощью bigforth, но ничего радостного: объём 12 с лишним килобайт. Зато с ростом объёма кода размер будет расти не так быстро как в случае с free basic, и однажды обгонит :)
  • @Renha, Лол, фрибейсик проигрывает по размеру, потому что он встраивает всю библиотеку времени выполнения. Я лишь показал, что избавившись от этой библиотеки можно получить вместо пятнадцати килобайт очень компактное приложение в два килобайта.
  • @mabu, к сожалению, интерпретатор из интерпретируемого языка выкинуть нельзя :(

    я кстати пилил свою forth-машину (это должен сделать каждый фортер), там хорошо с размером было, но даже для такой программы словаря реализованного не хватило бы
  • @Renha, А, это же и компилятор и интерпретатор в одном лице. Вот оно что.
  • @Renha, во, что получилось:

    qnikst@localhost ~/tmp/ats $ wc -c hello
    1128 hello

    qnikst@localhost ~/tmp/ats $ ldd hello
    not a dynamic executable
    (все в .text)

    исходник: dpaste.com
    заглушка для _start: dpaste.com

    сборка gcc -Os -nostdlib stubstart.S -o hello numtostr2.c

    я таки разобрался как использовать inline asm в сях и как делается syscall на x86_64.
  • @qnikst, ответить на вопрос "нафига?" я не могу.
  • @qnikst, А, у тебя там буфер статический. Если б Я буфер статический использовал, то половину четыре виндовых функции и пару моих собственных можно было бы не писать.
    А что у тебя за операционка? У Меня-то винда.
  • @mabu, Ну мне не лень сискол для mmap сделать (лень но сделаю), это вообще не проблема. У меня linux, как можно было догадаться.
  • @qnikst, Может линукс, может фрибиздя какая. Или федорино горе. Я же не знаю.
  • @mabu, Да какая разница? Сисколы вроде одинаковые будут. Вообще я подумал, что мне лень гуглить флаги для mmap. Поэтому в данную бредовую игру я больше играть не хочу. :)
  • @qnikst, Ассемблерные вставки в кавычках? Почему?
  • @mabu, Синтаксис сишный (gcc-шный?), он позволяет параметры из сишки передавать/получать.
  • @qnikst, Суть игры была не в том, чтобы написать свою реализацию метода ToString, а чтобы написать программу без встроенных в компилятор функций.
  • @mabu, мда.. Во-первых, define встроенные в компилятор функции, во-вторых, ты не А ты не заметил, что эт
  • @qnikst, это выполнено в общем. Ещё в первой версии.
  • @Renha, dbp-consulting.com (использую тред как записную книжку)
  • @mabu, Кстати, в первой версии можно сделать так:

    qnikst@localhost ~/tmp/ats $ gcc -Os -nostartfiles -ffreestanding stubstart.S numtostr.c
    qnikst@localhost ~/tmp/ats $ wc -c a.out
    5352 a.out
  • @mabu, к сожалению, читабельность проигрывает тому, на чём тут @qnikst писал. Наверняка можно переписать помногословнее, но почитабельней-попонятней.
  • @qnikst, а возможна реализация без 'import'?
  • @Renha, ну если unfoldr руками сделать (просто) и digitToInt (просто но многословно или не просто). Вообще в хацкеле ввиду того что стандартная библиотека модульная все программы содержат большое количество import. unfoldr f s = case f s of Nothing -> []; Just (k,v) -> k: unfoldr f v.
  • @Renha, Мне кстати стало интересно, как грузится виндовые бинарники, что там dynloader подтащил нужные символы с низким оверхэдом по месту.
  • @qnikst, Про формат PE и его загрузку в винде говорит педивикия ru.wikipedia.org
    Но лучше всё же почитать Руссиновича.
  • @Renha, а знаете что самое прикольное? Мне такая функция оказывается нужна, я просто забыл об этом т.к. из-за её отсутствия заморозил проект :) а стандартной не нашёл (только вывод, не сохранение в строку), самому лень было писать.
  • @Renha, только мне надо с десятичной точкой, но думаю мне хватит fixed point
  • @Renha, С десятичной точной чуть сложнее.
  • @mabu, потому хочу fixed =) благо большая точность не нужна
  • @Renha, там ещё один цикл нужно организовывать, чтобы выделить дробную часть по цифрам.
  • @Renha, В смысле не IEEE754 представление?
  • @qnikst, я не знаю :) Я для вывода хочу домножить на 10^x, затем за x до конца вывести точку. Это неправильно?
  • @mabu, можно, наоборот домножая на 10 и забирая целую часть, так?
  • @Renha, Можно. Лишь бы получить цифры.
  • @Renha, Можно, просто fixed point это обычно о другом формате хранения :)
  • @qnikst, Но я бы посмотрел как это обычно реализуют, чот мне кажется есть алгоритмы более эффективные чем деление на 10
  • @qnikst, Если бы компьютер работал в десятеричной системе, то можно было бы просто делать shr.
  • @mabu, Капитанская фуражка не давит?
  • @qnikst, Конечно давит. Мне даже череп жмёт.
  • @mabu, Только не говори, что ты до всего сам додумался! А то меня зависть грызет.
  • @qnikst, Нет, прочитал в какой-то умной книжке.