LINUX.ORG.RU

Ответ на: комментарий от anonymous

А откуда ты возьмешь n?

как я понял, n это то, что в eval'е? Тогда из входных данных.

«Программой» называется текст определенного вида. «текстом» называется конечная последовательность символов определенного языка. КОНЕЧНАЯ. Бесконечная последовательность символов не является ни текстом, ни программой.

что ты так прицепился-то к бесконечности: вот тебе ОДНА команда: L: JMP L

вот код после обработки gcc

    .file   "asm.c"
    .text
    .globl  main
    .type   main, @function
main:
.LFB0:
    .cfi_startproc
    pushl   %ebp
    .cfi_def_cfa_offset 8
    .cfi_offset 5, -8
    movl    %esp, %ebp
    .cfi_def_cfa_register 5
#APP
# 3 "asm.c" 1
    L: JMP L
# 0 "" 2
#NO_APP
    movl    $0, %eax
    popl    %ebp
    .cfi_restore 5
    .cfi_def_cfa 4, 4
    ret
    .cfi_endproc
.LFE0:
    .size   main, .-main
    .ident  "GCC: (GNU) 4.8.2"
    .section    .note.GNU-stack,"",@progbits

это два байта:

08048403 <L>:
 8048403:	eb fe                	jmp    8048403 <L>

и да, БЕСКОНЕЧНОЕ время. Команда кстати ёб называется.

emulek
()
Ответ на: комментарий от anonymous

Нет, это не правда. Сколько можно уже? В куче языков есть термы, которые ни в какое количество фиксированных машинных команд не транслируются. Даже в той же сишке.

нет таких термов.

emulek
()
Ответ на: комментарий от anonymous

в какой набор машинных команд транслируется терм «1»?

# заталкиваем в стек константу 1
mov eax, 1
push eax

а терм «*»?

# вынимаем из стека два числа, умножаем, и заталкиваем результат
pop eax
pop edx
imul eax, edx
push eax
emulek
()
Ответ на: комментарий от anonymous

вот навоял тривиальный eval

#include <stdio.h>

#define STACK_SIZE 100

int main()
{
	char terms[] = "1.2.3.*.+.";
	int stack[STACK_SIZE];
	const char *tp = terms;
	int *sp = stack;
	while(*tp)
	{
		switch(*tp++)
		{
			case '0':
			case '1':
			case '2':
			case '3':
			case '4':
			case '5':
			case '6':
			case '7':
			case '8':
			case '9':
				// push constant
				*sp++ = tp[-1] - '0';
				break;
			case '+':
				// add
				sp[-2] = sp[-1] + sp[-2];
				--sp;
				break;
			case '*':
				// mul
				sp[-2] = sp[-1] * sp[-2];
				--sp;
				break;
			case '.':
				// ouput
				printf("%d\n", sp[-1]);
				break;
			default:
				// error
				fprintf(stderr, "invalid terms %c\n", tp[-1]);
				return 1;
		}
	}
	return 0;
}

возвращает

1
2
3
6
7

евалит, ибо 1+2*3=7

emulek
()
Ответ на: комментарий от emulek

как я понял, n это то, что в eval'е? Тогда из входных данных.

Нету никаких входных данных. N - максимальное решение уравнения.

вот код после обработки gcc

И что? Речь идет о том, когда код бесконечен. В данном случае он конечен вполне.

anonymous
()
Ответ на: комментарий от emulek

Замечательно. А теперь имеем обычный сишный код: «1+2;» чему соответствует здесь терм «1»? А терм «+»? Каким машинным командам?

anonymous
()
Ответ на: комментарий от emulek

Индексы отрицательные здесь потому, что стек в процессе обработки растет в сторону меньших адресов в массиве, так или нет?!

Просто не очевидно.

Twissel ★★★★★
()
Ответ на: комментарий от anonymous

Нету никаких входных данных. N - максимальное решение уравнения.

ну не тупи: «входные данные» это то, что внутри eval'а. Когда евал евалит, он из одного решения делает другое, и опять его в себя засовывает. Как змея, которая жрёт свой хвост.

И что? Речь идет о том, когда код бесконечен. В данном случае он конечен вполне.

код (вход и выход eval'а) потенциально бесконечен. IRL можешь юзать mmap(2), и отобразить в память всё свободное место на своём HDD. А код обрабатывающий сам eval — вполне конечен, и не очень большой.

emulek
()
Ответ на: комментарий от anonymous

int x; каким машинным командам соответствует?

sub esp, <размер int>

ты долго меня пытать будешь? Напиши интересующий код, собери gcc -S, и сам посмотри.

emulek
()
Ответ на: комментарий от anonymous

Замечательно. А теперь имеем обычный сишный код: «1+2;» чему соответствует здесь терм «1»? А терм «+»? Каким машинным командам?

в данном случае это просто константа 3.

emulek
()
Ответ на: комментарий от Twissel

Индексы отрицательные здесь потому, что стек в процессе обработки растет в сторону меньших адресов в массиве, так или нет?!

стек тут нужен для промежуточных результатов. Это обратная польская нотация.

При чтении терма <цифра> это число пишется в *sp, а потом sp увеличивается на 1. При чтении терма <операция> выполняется операция над предыдущим(-1) и пре-предыдущим(-2), а затем sp уменьшается на 1. При этом результат пишется в (-2) который после уменьшения sp становится (-1).

Можно сделать и обычную нотацию, как в школе. Но тогда потребуется хранить в стеке и операции тоже, т.к. операции нельзя будет выполнять сразу, например 2+2*2.

emulek
()
Ответ на: комментарий от emulek

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

Че то не совсем понятно твое определение. Есть read-eval-print-loop. Ничего он в себя не засовывает, он ждет входа.

phill
()
Ответ на: комментарий от Twissel

x[i] это сахар для *(x + i), так что если x указывает куда-то в середину массива, то x[0], *x это текущий элемент, x[1], *(x + 1) — следующий, x[-1], *(x - 1) — предыдущий и т.п.

quasimoto ★★★★
()
Последнее исправление: quasimoto (всего исправлений: 2)
Ответ на: комментарий от phill

Че то не совсем понятно твое определение. Есть read-eval-print-loop. Ничего он в себя не засовывает, он ждет входа.

тогда всё тупо и примитивно. Пример на сишечке выше.

А вот если у тебя eval порождает код, и потом опять этот код выполняет, то несколько сложнее: этот хитрый eval нужно с собой тащить. Т.е. компилятор встретив такой eval должен запихать в выходной код реализацию eval'а. Т.е. самого себя.

Такое бывает на практике, но очень редко. И мне не придумать простой пример для демонстрации. Если взять простой ЯП типа МТ или brainfuck'а, то я задолбаюсь писать там реализацию хоть чего-то полезного(да и не хочется ломать мозг). Если взять нормальны сложный ЯП, то eval для него будет необъятный. Поля слишком узки©Ферма.

emulek
()
Ответ на: комментарий от emulek

А вот если у тебя eval порождает код, и потом опять этот код выполняет, то несколько сложнее: этот хитрый eval нужно с собой тащить

Да чо ты фантазируешь? Eval — это обычная функция, по сути.

phill
()
Ответ на: комментарий от emulek

ну не тупи: «входные данные» это то, что внутри eval'а.

Нет. Еще раз - входных данных нету.

код (вход и выход eval'а) потенциально бесконечен.

Еще раз - последовательность машинных команд бесконечна. Не потенциально и актуально. То есть кода нет.

А код обрабатывающий сам eval — вполне конечен, и не очень большой.

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

anonymous
()
Ответ на: комментарий от emulek

sub esp, <размер int>

Неправильно. Еще попытка?

anonymous
()
Ответ на: комментарий от emulek

в данном случае это просто константа 3.

Во-первых, константа не является командой машкода. Во-вторых, я не спрашивал какой машкод соответствует «1+2;», я спрашивал что соответствует терму «1» и «+»

anonymous
()
Ответ на: комментарий от phill

Все очень просто. Допустим у тебя есть интерпретатор Х, который интерпретирует некоторую программу. Емулек говорит: берем эту некоторую программу и кладем ее текст в память. Теперь генерируем код, который вызывает ф-ю евал интерпретатора Х на этой программе. Получившуюся хуету он называется «скомпилированной программой».

anonymous
()
Ответ на: комментарий от emulek

Т.е. компилятор встретив такой eval должен запихать в выходной код реализацию eval'а. Т.е. самого себя.

И значит, программу нельзя скомпилировать. Потому что пока в коде есть евал - это некомпилированная программа.

anonymous
()
Ответ на: комментарий от anonymous

Потому что пока в коде есть евал - это некомпилированная программа.

Вообще-то, javascript, lisp как то компилируются же?

phill
()
Ответ на: комментарий от anonymous

Все очень просто. Допустим у тебя есть интерпретатор Х, который интерпретирует некоторую программу. Емулек говорит: берем эту некоторую программу и кладем ее текст в память. Теперь генерируем код, который вызывает ф-ю евал интерпретатора Х на этой программе. Получившуюся хуету он называется «скомпилированной программой».

Ну так получившаяся программа будет текстом на языке интерпретатора X? Ее же обратно же надо скомпилировать будет или как? А как он скомпилирует то, что заранее не известно? Надо тогда, чтобы выхлоп интерпретатора X был корректным текстом на языке компилятора?

А так, он получит тока выхлоп интерпретатора X, больше ни хрена.

Я запутался:)

phill
()
Ответ на: комментарий от phill

Да чо ты фантазируешь? Eval — это обычная функция, по сути.

да, обычная. Если у нас интерпретатор. А если у нас компилятор, то мы не сможем скомпилировать eval, т.к. не знаем, что там будет внутри. Поэтому для изготовления компилятора нам нужно тащить с собой весь интерпретатор. Или юзать ЯП без eval. Или как в sed/C(ВНЕЗАПНО man system(3)) использовать левый /bin/sh.

emulek
()
Ответ на: комментарий от phill

Для лиспа eval пишется строк в 20-30, да и то, это больше с типами связано.

я знаю. Но это только сам eval без вспомогательных функций. Того же списка из объектов в C/C++ нет(не считая STL, но STL это нечестно).

emulek
()
Ответ на: комментарий от phill

МТ-это вообще-то машина, а не язык.

любая машина имеет свой нативный ЯП. У МТ тоже есть ЯП, хотя и только в обобщённом виде (т.е. МТ это на самом деле семейство машин, а не одна).

emulek
()
Ответ на: комментарий от anonymous

ну не тупи: «входные данные» это то, что внутри eval'а.

Нет. Еще раз - входных данных нету.

не. Ты точно идиот.

Еще раз - последовательность машинных команд бесконечна. Не потенциально и актуально. То есть кода нет.

что за команды ты там нафантазировал из ниоткуда?

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

с какого перепугу не может? Вот тебе твой eval актуально бесконечный: L: CALL L (IRL падает при переполнении стека). Этот eval выполняет код, в котором написано «выполнить eval который...»... Ну ты понял.

emulek
()
Ответ на: комментарий от anonymous

И значит, программу нельзя скомпилировать. Потому что пока в коде есть евал - это некомпилированная программа.

у тебя BASIC головного мозга.

emulek
()
Ответ на: комментарий от phill

Вообще-то, javascript, lisp как то компилируются же?

они тащат за собой интерпретатор. Который для JS'а неотделим от VM.

emulek
()
Ответ на: комментарий от quasimoto

С этим я в курсе. Это же классика :)

Отрицательные индексы сбили с толку

Twissel ★★★★★
()
Ответ на: комментарий от Twissel

я сам такую хрень тыщу лет не писал. Обычно лучше использовать стек в куче с push/pop, но на ЛОРе мне лениво было делать нормальную реализацию стека, а списки с итераторами из STL мне не нравятся.

emulek
()
Ответ на: комментарий от emulek

В стандарте есть std::stack с push/top/pop и автоматической памятью в куче, а если статично, то можно обвернуть std::array операциями и повесить в static:

#include <cassert>
#include <cstdio>

#include <array>

#include <boost/range.hpp>

template <typename T, size_t size>
class Stack {

    std::array<T, size> data;
    size_t sp;

public:

    Stack() :
#ifdef STACK_DEFAULTING
        data(),
#endif
        sp() {}

    void push(T x) { assert(sp < size); data[sp++] = x; }

    T top() const { assert(sp > 0); return data[sp - 1]; }

    T pop() {
#ifdef STACK_DEFAULTING
        T r = top(); data[--sp] = T(); return r;
#else
        assert(sp > 0); return data[--sp];
#endif
    }

};

int main() {
    const char terms[] = "1.2.3.*.+.";
    static Stack<int, STACK_SIZE> stack;
    for (auto const term : boost::make_iterator_range(std::begin(terms), std::end(terms) - 1))
        switch (term) {
        case '0' ... '9': stack.push(term - '0'); break;
        case '+': stack.push(stack.pop() + stack.pop()); break;
        case '*': stack.push(stack.pop() * stack.pop()); break;
        case '.': printf("%d\n", stack.top()); break;
        default: fprintf(stderr, "invalid term: %c\n", term); return 1;
        }
}
quasimoto ★★★★
()
Ответ на: комментарий от quasimoto

static Stack<int, STACK_SIZE> stack; for (auto const term : boost::make_iterator_range(std::begin(terms), std::end(terms) - 1))

не обижайся, но это явное php головного мозга.

emulek
()
Ответ на: комментарий от quasimoto

Тут zero-cost — ассемблер на выходе всё равно такой же.

всё равно за такое убивают. И дело совсем не в ассемблере.

PS: мой компилятор сказал на твой говнокод:

In file included from /usr/include/c++/4.8.2/array:35:0, from ev_qm.cpp:4: /usr/include/c++/4.8.2/bits/c++0x_warning.h:32:2: ошибка: #error This file requires compiler and library support for the ISO C++ 2011 standard. This support is currently experimental, and must be enabled with the -std=c++11 or -std=gnu++11 compiler options. #error This file requires compiler and library support for the \ ^ ev_qm.cpp:6:27: фатальная ошибка: boost/range.hpp: Нет такого файла или каталога #include <boost/range.hpp> ^ компиляция прервана.

emulek
()
Ответ на: комментарий от emulek

4.8.2

У меня тоже 4.8.2, ты не знаешь как собирать C++11 код?

всё равно за такое убивают. И дело совсем не в ассемблере.

А что не так? В лучших традициях PHP же — ДЛЯ (for) каждого ТЕРМА (term) из ДИАПАЗОНА (range) с НАЧАЛА (begin) ТЕРМОВ (terms) до КОНЦА (end) ТЕРМОВ (terms) НЕ СЧИТАЯ (- 1) последнего '\0'. И никаких тебе while(*tp), switch(*tp++), tp[-1], *sp, *sp++, sp[-1], sp[-2] и --sp.

quasimoto ★★★★
()
Последнее исправление: quasimoto (всего исправлений: 1)
Ответ на: комментарий от phill

Ну так получившаяся программа будет текстом на языке интерпретатора X?

Именно так. Но в мире емулека это чистый машкод.

anonymous
()
Ответ на: комментарий от anonymous

А я за кресты зашёл поговорить. Чем отличается компилятор от интерпретатора? (комментарий), Чем отличается компилятор от интерпретатора? (комментарий), тогда как http://www.cplusplus.com/reference/stack/stack/, Чем отличается компилятор от интерпретатора? (комментарий),

    some_stack_adt<...> stack;
    const std::string terms = "1.2.3.*.+.";
    for (auto const term : terms)
    const char terms[] = "1.2.3.*.+.";
    for (auto const term : cstring_range(terms))

Зачем этот код тут в контексте претензий anonymousа к позиции емулька я не знаю (или что тебе не понравилось).

quasimoto ★★★★
()
Ответ на: комментарий от quasimoto

А можно на примере откомпилированной sbcl-ом программы и сохраненной в ELF. Это в какую степь?

anonymous
()
Ответ на: комментарий от anonymous

Выполнение в образе вообще и eval в частности будут делать всё что угодно (eval будет определённым образом интерпретировать код который делает всё что угодно в рамках образа, в т.ч. зовёт компилятор, опять eval и т.п.). Но это нормально. save-lisp-and-die будет делать вполне конкретную вещь. compile во время раскрытия макросов будет делать всё что угодно, но это типа местная особенность — писать базовый лисп в макросах лиспом как угодно, если это пропустить, то дальше компиляция базового лиспа вполне обычна.

P.S. sh (в т.ч. exec*), hibernate и make (пропустить — cc) :)

quasimoto ★★★★
()
Ответ на: комментарий от quasimoto

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

Но, например, можно же (наверное долго и вряд ли кто-то делает) добавить к целям в исходниках pypy свой код и получить бинарь. По-моему довольно похоже («базовый лисп» <-> RPython). Но это же не делает из pypy _компилятор_ питона, почему же sbcl упорно называют компилятором. В любом случае, выхлоп «компилятора» не отпускается в свободное плавание, а обрабатывает вход под чутким контролем образа.

anonymous
()

ну вы не успокоились ищщо?

странно, думал тред таки заглох.

тогда вот вам ещё вброс: курс по метакомпилятору META II впечатления от

это в тему «и внезапно, брюки превращаются.. превращаются.. из интерпретатора в комиплятор»

таки превращаются. по шагам — можете сами курс посмотреть.

если язык метаязык, а не совсем тупой.

anonymous
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.