Код (ASM): В интернете очень много написано об обратной польской записи это запись где арифметический оператор записывается не между операндами а после них например утрированное выражение: 3*(40-(2+3)*4/2) последовательно в лоб это выражение не решить потому что надо сначала выполнять приоритетные операции для этого 100 лет тому назад (если я не ошибаюсь) придумали особый вид записи выражения которое потом последовательно можно легко решить следуя определённому алгоритму выражение записанное выше переводится в обратную польскую запись 3 40 2 3 + 4 * 2 / - * следуя по записи надо дойти до арифметической команды и произвести это действие между двумя операндами которые стояли перед этой арифметической командой потом эти два операнда убираются из записи а вместо записи этой арифметической команды прописывается результат выполнения в квадратных скобках справо полученный результат 3 40 2 3 + 4 * 2 / - * --> 3 40 [5] 4 * 2 / - * 3 40 [5] 4 * 2 / - * --> 3 40 [20] 2 / - * 3 40 [20] 2 / - * --> 3 40 [10] - * 3 40 [10] - * --> 3 [30] * 3 [30] * --> [90] результат - 90 с получением результата от обратной польской записи всё понятно а вот как из стандартной записи выражения получить саму эту обратную польскую запись это вопрос в интернете на разных площадках иногда вроде бы объясняют как но то ли не договаривают то ли сами не всё знают не понятно даже есть онлайн переводчики из стандартного выражения в обратную польскую запись но они справляются не со всеми выражениями хотя я не исключаю что где нибудь есть более точная информация Вопрос к тем кто в теме Кто нибудь занимался этим вопросом Или кто то даст ссылку где есть более полные объяснения как правильно перевести выражение в обратную польскую запись
В.Н.Лебедев Введение в системы программирования. М.Статистика. 1975 стр.134 и далее... так нас учили...
Так а что сложного тут? Все стековые виртуальные машины (CLR, JVM, CPython и тд) так работают, не говоря уже о языках типа Форта или Фактора. Просто число - кладет это число на стек, оператор - берет с верхушки стека два числа, применяет к ним оператор и результат кладет на верхушку стека. Вопрос то какой? И я не знаю, как можно не справляться со всеми выражениями. Приведи пример, все выражения можно представить в такой записи. И еще, закаким тебе вообще это нужно в 2021 году?
под обычными выражениями я имел ввиду пример описанный выше 3*(40-(2+3)*4/2) а вот если его немного усложнить например добавить минус перед скобкой (2+3) 3*(40--(2+3)*4/2) то есть надо не только раскрыть скобки (2+3) а потом ещё надо у этого результата поменять знак vitokop спасибо за наводку почитаю
assch, в ресурсах лежит "Handbook of Floating-Point Arithmetic" от Jean-Michel Muller и других еще есть сайт http://www.ray.masmcode.com/ с FPU TUTORIAL и FPU LIBRARY от Raymond Filiatreault Конвертер арифметических выражений в команды на ассемблере (FASM) от UniverseIsASimulation из Хорватии
Код написал на masm32 постарался ничего не упустить для быстроты только для обычных операций (+ - * /) но при желании можно дополнить какими нибудь битовыми операциями так же добавил операцию смены знака после раскрытия скобок что к моему удивлению почему то нет в онлайн конверторах на просторах интернета или может быть мне просто попались такие Суть проста если прописать символ минуса перед скобкой то по сути это будет означать что после раскрытия скобок у этого результата нужно поменять знак для этого я произвольно задействовал символ решётки - # разница только в том что при обработки обратной польской записи при встрече обычной арифметической операции нужно задействовать два предпоследних операнда а при встрече символа решётки - # нужно задействовать только один предпоследний операнд по сути у этого операнда сменить знак командой ассемблера (neg) Код (ASM): ;--------------------------------------------------------------------- ;--------------------------------------------------------------------- ;--------------------------------------------------------------------- .data txt db "3*-(40-(2+3)*4/2)+2" tmp dd 0 .data? buf dd 256 dup(?) .code ;--------------------------------------------------------------------- ;--------------------------------------------------------------------- ;--------------------------------------------------------------------- push esi push edi ;-------------------------------------------- push 0 push offset tmp push offset txt push 0 call MessageBox ;-------------------------------------------- lea edi,buf lea esi,txt xor edx,edx ;-------------------------------------------- Loc__exp: ;-------------------------------------------- mov eax,dword ptr [esi] ;-------------------------------------------- ;-------------------------------------------- ;-------------------------------------------- .if al == 0 Loc__nul: pop eax ;--------------------------- .if eax == 28h ; "(" .elseif eax mov word ptr [edi],0A0Dh add edi,2 movzx eax,al mov byte ptr [edi],al inc edi jmp Loc__nul .endif ;--------------------------- mov dword ptr [edi],0 ;-------------------------------------------- ;-------------------------------------------- ;-------------------------------------------- .elseif ax == 282Dh && edx == 0 ; "-(" push 28h ; "(" push 23h ; "#" inc esi jmp Loc__inc ;-------------------------------------------- ;-------------------------------------------- ;-------------------------------------------- .elseif ax == 282Bh && edx == 0 ; "+(" inc esi push 28h ; "(" jmp Loc__inc ;-------------------------------------------- ;-------------------------------------------- ;-------------------------------------------- .elseif al == 2Bh || al == 2Dh ; или "+" или "-" ;--------------------------- .if edx == 0 jmp Loc__rec .endif ;--------------------------- mov word ptr [edi],0A0Dh add edi,2 mov cl,byte ptr [esp] ;--------------------------- .if cl == 2Bh || cl == 2Dh || cl == 2Ah || cl == 2Fh ; "+" или "-" или "*" или "/" pop ecx mov byte ptr [edi],cl mov word ptr [edi+1],0A0Dh add edi,3 movzx eax,al push eax ;--------------------------- .else movzx eax,al push eax .endif ;--------------------------- xor edx,edx jmp Loc__inc ;-------------------------------------------- ;-------------------------------------------- ;-------------------------------------------- .elseif al == 2Ah || al == 2Fh ; "*" или "/" mov word ptr [edi],0A0Dh add edi,2 mov cl,byte ptr [esp] ;--------------------------- .if cl == 2Ah || cl == 2Fh ; "*" или "/" pop ecx mov byte ptr [edi],cl mov word ptr [edi+1],0A0Dh add edi,3 movzx eax,al push eax ;--------------------------- .else movzx eax,al push eax .endif ;--------------------------- xor edx,edx jmp Loc__inc ;-------------------------------------------- ;-------------------------------------------- ;-------------------------------------------- .elseif al == 28h ; "(" xor edx,edx movzx eax,al push eax jmp Loc__inc ;-------------------------------------------- ;-------------------------------------------- ;-------------------------------------------- .elseif al == 29h ; ")" Loc__skb: pop eax ;--------------------------- .if eax != 28h ; "(" mov word ptr [edi],0A0Dh mov byte ptr [edi+2],al add edi,3 jmp Loc__skb .endif ;--------------------------- jmp Loc__inc ;-------------------------------------------- ;-------------------------------------------- ;-------------------------------------------- .else ;--------------------------- Loc__rec: ;--------------------------- mov edx,1 mov byte ptr [edi],al inc edi ;--------------------------- Loc__inc: ;--------------------------- inc esi jmp Loc__exp .endif ;-------------------------------------------- ;-------------------------------------------- ;-------------------------------------------- push 0 push offset tmp push offset buf push 0 call MessageBox @dBox &buf ;--------------------------------------------- pop edi pop esi ;--------------------------------------------------------------------- ;--------------------------------------------------------------------- ;--------------------------------------------------------------------- Всем спасибо за участие
Конструкции типа Код (ASM): .if ... jmp .label .elseif ... jmp .label .endif избыточные. Если у тебя внутри кейса безусловный переход, надо закрывать .if и открывать новый.
Посмотрите код повнимательней если я уберу эти безусловные переходы то при встрече первой же арифметической операции алгоритм выйдет из перебора символов выражения потому что вы наверное просто не заметили что переход на начало просмотра следующего символа у меня стоит именно в теле оператора - .else --- Сообщение объединено, 4 сен 2021 --- Не большое дополнение для тех кому это конечно интересно не критично но желательно сначала обрабатываемый массив выражения прогнать через алгоритм который пересоберёт массив выражения естественно в новый буфер чтобы убрать из массива все пробелы если конечно они там есть попутно проверив правильное ли количество скобок прописано в выражении то есть чтобы у каждой открывающей скобки была закрывающая скобка сделать это не трудно достаточно поставить счётчик по принципу плюс открывающая скобка минус закрывающая скобка если на выходе на счётчике ноль значит всё в порядке если счётчик плюсовой значит не хватает скобок а если счётчик ушёл в минус значит по ошибке закрывающую скобку поставили впереди только этот минус счётчика нужно ловить не в конце а сразу же как только счётчик ушёл в минус После этот уже пересобранный массив выражения можно скормить коду прописанному выше
Код (ASM): .elseif ax == 282Bh && edx == 0 ; "+(" inc esi push 28h ; "(" jmp Loc__inc ;-------------------------------------------- ;-------------------------------------------- ;-------------------------------------------- .elseif al == 2Bh || al == 2Dh ; или "+" или "-" У меня вообще-то буквально написано было что надо сделать. Это про внимательность. .elseif это безусловный переход к .endif'у и следующие за ним проверки. Сам этот безусловный переход никакого смысла не имеет, потому что будет следовать за выставленным вручную безусловным переходом.
я специально убрал лишнее Код (ASM): Loc__exp: mov eax,dword ptr [esi] .if al == 0 ... .elseif ax == 282Bh && edx == 0 ; "+(" inc esi push 28h jmp Loc__inc .else Loc__inc: inc esi jmp Loc__exp .endif теперь посмотрите что будет если убрать как вы считаете лишним безусловный переход - jmp Loc__inc алгоритм просто выйдет за пределы - .endif и соответственно код не выполнит свою задачу до конца вы пишите: .elseif это безусловный переход к .endif и следующие за ним проверки а где вы увидели после .endif какие то проверки их там нет в .elseif прописан безусловный переход на метку в теле - .else подчёркиваю три раза красным на метку в теле - .else и уже из этого тела идёт безусловный переход на начало
Да, понял уже, что воткнуть похожий безусловный переход в конце первого обработчика мешает какая-то непреодолимая сила, не действующая во 2,3,4,5 и 6м. Докапываюсь, но просто не мог пройти мимо. У меня получился точно такой же алгоритм, но на 24 байта дешевле и в основном за счет убранных бесполезных переходов, которые никогда не будут выполнены: Код (ASM): push esi push edi push 0 push tmp push txt push 0 call [MessageBox] lea edi,[buf] lea esi,[txt] xor edx,edx Loc__exp: mov eax,dword[esi] test al,al .if ZERO? Loc__nul: pop eax .if eax<>28h & eax mov word[edi],0A0Dh add edi,2 movzx eax,al mov byte[edi],al inc edi jmp Loc__nul .endif mov dword[edi],0 jmp Loc__done .endif test edx,edx .if ZERO? & ax='-(' push 28h push 23h inc esi jmp Loc__inc .endif test edx,edx .if ZERO? & ax='+(' inc esi push 28h jmp Loc__inc .endif .if al='+' | al='-' test edx,edx je Loc__rec mov word[edi],0A0Dh add edi,2 mov cl,byte[esp] .if cl = '+' | cl = '-' | cl = '*' | cl = '/' pop ecx mov byte[edi],cl mov word[edi+1],0A0Dh add edi,3 movzx eax,al push eax .else movzx eax,al push eax .endif xor edx,edx jmp Loc__inc .endif .if al='*' | al='/' mov word[edi],0A0Dh add edi,2 mov cl,byte[esp] .if cl = 2Ah | cl = 2Fh pop ecx mov byte[edi],cl mov word[edi+1],0A0Dh add edi,3 movzx eax,al push eax .else movzx eax,al push eax .endif xor edx,edx jmp Loc__inc .endif .if al = '(' xor edx,edx movzx eax,al push eax jmp Loc__inc .endif .if al = ')' Loc__skb: pop eax .if eax <> '(' mov word[edi],0A0Dh mov byte[edi+2],al add edi,3 jmp Loc__skb .endif jmp Loc__inc .endif Loc__rec: mov edx,1 mov byte[edi],al inc edi Loc__inc: inc esi jmp Loc__exp Loc__done: push 0 push tmp push buf push 0 call [MessageBox] ;@dBox &buf pop edi pop esi
Ну это зависит от приоритета операторов и ЯП, но я не вижу причин, почему это не должно корректно парситься. Унарный и бинарный минус как бы своего рода разные операторы.
Спасибо за интересный тред. f13nd, проверьте пожалуйста ваш вариант кода, а-то вариант assch в MASM скомпилировался, а ваш ни в MASM, ни в FASM не могу скомпилировать. Мне для общего развития, если что. Хотел вникнуть сравнить ваш вариант и assch'а. P.S. У варианта assch'а 161 строку нужно закомментировать ( 161 @dBox &buf ), а-то MASMу не нравится.
Посмотрел код - f13nd разница только в том что вы переход на начало просмотра следующего символа разместили в конце после операторов (макросов) условного сравнения просто для примера сильно утрированно: Код (ASM): ;-------------------------- Loc__exp: ;-------------------------- .if al jmp Loc__inc .endif ;-------------------------- .if al jmp Loc__inc .endif ;-------------------------- Loc__inc: jmp Loc__exp ;-------------------------- я же разместил этот переход не в конце а в теле - .else так же просто для примера сильно утрированно: Код (ASM): ;-------------------------- Loc__exp: ;-------------------------- .if al jmp Loc__inc ;-------------------------- .elseif al jmp Loc__inc ;-------------------------- .else Loc__inc: jmp Loc__exp .endif ;-------------------------- оптимизация в вашем случае оптимизация байт-кода дело конечно хорошее я тоже иногда задумываюсь об этом но это уже процесс отладки я же свой код выставил как первый черновой вариант можно ли мой код оптимизировать да конечно наверное можно для этого нужно просто проанализировать код я же своей главной целью ставил чтобы алгоритм просто выполнял свою задачу на мой взгляд (который конечно же может быть ошибочным) оптимизацию байт-кода не всегда нужно ставить во главе угла например если вы загляните под капот какой нибудь API функции вы просто ужаснётесь сколько там перегруженного байт-кода и дело даже не в проверках на возможные ошибки просто инженеры-программисты которые разрабатывают алгоритмы на перегруженность байт-кода не смотрят потому что на данный момент времени это уже далеко не актуально их главная цель это скорость выполнения алгоритма кода я кстати проверил ваш код и например у выражения с унарным минусом -(2+3) он выдал не корректные данные -2 3 + которые в результате дадут (1) обратная польская запись для этого выражения должна быть 2 3 + # чтобы получился правильный результат (-5) я может быть что то напутал потому что синтаксис платформ у нас немного разные на всякий случай если я не ошибся проверьте свой код --- Сообщение объединено, 5 сен 2021 --- По поводу строки (161) @dBox &buf на которую обратил внимание - GRAFik я просто забыл её убрать из кода это мой макрос - @dBox который при отладке кода показывал мне дамп по адресу - buf
Да нет никакого тела, этими переходами в соседний кейс ты ломаешь композицию .if/.elseif/.endif, и видимо используешь их тупо для удобства. Отсюда и кривизна получившегося кода - эти макросы просто не задуманы чтобы использовать их так.
У нас с вами разные представления о ветвлении условных конструкций Особенно умиляют ваши смелые высказывания что нет ни какого тела у этих операторов но по сути в этом нет ничего страшного так как каждый сам делает свой выбор Вы исправили ошибку в своём коде с унарным минусом? или пологаете что и так сойдёт --- Сообщение объединено, 5 сен 2021 --- обнаружил падение стека на четыре байта чтобы этого не происходило лучше использовать немного подправленный код Код (ASM): ;--------------------------------------------------------------------- ;--------------------------------------------------------------------- ;--------------------------------------------------------------------- .data txt db "3*-(40-(2+3)*4/2)+2" tmp dd 0 .data? buf dd 256 dup(?) .code ;--------------------------------------------------------------------- ;--------------------------------------------------------------------- ;--------------------------------------------------------------------- push esi push edi ;-------------------------------------------- push 0 push offset tmp push offset txt push 0 call MessageBox ;-------------------------------------------- lea edi,buf lea esi,txt xor edx,edx push 0 ; положим в стек нулевую метку ;-------------------------------------------- Loc__exp: ;-------------------------------------------- mov eax,dword ptr [esi] ;-------------------------------------------- ;-------------------------------------------- ;-------------------------------------------- .if al == 0 Loc__nul: pop eax ;--------------------------- .if eax ; пока не встретится нулевая метка пишем в буфер mov word ptr [edi],0A0Dh add edi,2 movzx eax,al mov byte ptr [edi],al inc edi jmp Loc__nul .endif ;--------------------------- mov dword ptr [edi],0 ;-------------------------------------------- ;-------------------------------------------- ;-------------------------------------------- .elseif ax == 282Dh && edx == 0 ; "-(" push 28h ; "(" push 23h ; "#" inc esi jmp Loc__inc ;-------------------------------------------- ;-------------------------------------------- ;-------------------------------------------- .elseif ax == 282Bh && edx == 0 ; "+(" inc esi push 28h ; "(" jmp Loc__inc ;-------------------------------------------- ;-------------------------------------------- ;-------------------------------------------- .elseif al == 2Bh || al == 2Dh ; или "+" или "-" ;--------------------------- .if edx == 0 jmp Loc__rec .endif ;--------------------------- mov word ptr [edi],0A0Dh add edi,2 mov cl,byte ptr [esp] ;--------------------------- .if cl == 2Bh || cl == 2Dh || cl == 2Ah || cl == 2Fh ; "+" или "-" или "*" или "/" pop ecx mov byte ptr [edi],cl mov word ptr [edi+1],0A0Dh add edi,3 movzx eax,al push eax ;--------------------------- .else movzx eax,al push eax .endif ;--------------------------- xor edx,edx jmp Loc__inc ;-------------------------------------------- ;-------------------------------------------- ;-------------------------------------------- .elseif al == 2Ah || al == 2Fh ; "*" или "/" mov word ptr [edi],0A0Dh add edi,2 mov cl,byte ptr [esp] ;--------------------------- .if cl == 2Ah || cl == 2Fh ; "*" или "/" pop ecx mov byte ptr [edi],cl mov word ptr [edi+1],0A0Dh add edi,3 movzx eax,al push eax ;--------------------------- .else movzx eax,al push eax .endif ;--------------------------- xor edx,edx jmp Loc__inc ;-------------------------------------------- ;-------------------------------------------- ;-------------------------------------------- .elseif al == 28h ; "(" xor edx,edx movzx eax,al push eax jmp Loc__inc ;-------------------------------------------- ;-------------------------------------------- ;-------------------------------------------- .elseif al == 29h ; ")" Loc__skb: pop eax ;--------------------------- .if eax != 28h ; "(" mov word ptr [edi],0A0Dh mov byte ptr [edi+2],al add edi,3 jmp Loc__skb .endif ;--------------------------- jmp Loc__inc ;-------------------------------------------- ;-------------------------------------------- ;-------------------------------------------- .else ;--------------------------- Loc__rec: ;--------------------------- mov edx,1 mov byte ptr [edi],al inc edi ;--------------------------- Loc__inc: ;--------------------------- inc esi jmp Loc__exp .endif ;-------------------------------------------- ;-------------------------------------------- ;-------------------------------------------- push 0 push offset tmp push offset buf push 0 call MessageBox ;--------------------------------------------- pop edi pop esi ;--------------------------------------------------------------------- ;--------------------------------------------------------------------- ;---------------------------------------------------------------------
Это не представления, речь про неэффективное генерирование кода с лишними неиспользуемыми инструкциями. Ассемблер не высокоуровневый ЯП, где компилятор занимается такими вещами сам, их надо контролировать самстоятельно. А макросы просто средство. Закрывая .if после безусловного перехода ты получаешь то же самое, но без лишней инструкции. Если до зарезу надо безусловный переход в .else, просто вставляешь его вручную. Я не собирался под ключ делать, только показывал как код должен был быть устроен. Тем более, если он изначально содержал в себе баги, проще было бы с нуля переписать, но в этом я не вижу смысла. Эти фокусы с пушами-попами вообще скользкая дорожка.
Дискусия была жаркой но в любом случае очень интересной Всем спасибо за участие PS Rel вначале риторически спрашивал за каким это вообще нужно в 2021 году? Отвечаю: Я когда только начинал писать коды на masm32 я естественно столкнулся с рутиной написания кода хотя masm32 это и не чистый ассемблер и в нём есть какие никакие высокоуровневые вшитые макросы всё равно рутина порядком надоедала выход как мне казалось в то время был один это писать собственные макросы и пошло поехало я собрал внушительную библиотеку собственных макросов которые существенно снизили рутину код на порядок стал компактнее да читабельность кода стала намного лучше но в один прекрасный момент на не очень маленьких проектах я столкнулся с тем что время сборки проекта компилятором существенно выросла например время доходило до 1500 миллисекунд (полторы секунды) по сути вроде бы ерунда и не смертельно главное код писался намного проще и быстрее но я прекрасно понимал почему это происходит а происходит это потому что существенно выросла нагрузка на компилятор из за прописанных в коде макросов на тот момент я уже професионально писал макросы и прекрасно знал что они могут очень многое но к сожелению не всё в частности нужно строго соблюдать правила синтаксиса при написании алгоритма действий в макросе да и ограничение употребления определённых символов в этом макросе и кстати размер данных в макросе тоже имел свой предел Ради интереса я этот код который был компактный но компилировался как я уже говорил 1500 миллисекунд (полторы секунды) я переписал не просто на masm32 а на чистом ассемблере и скормил этот код компилятору и время компила составила всего 50 миллисекунд в 30 раз быстрее поразмыслив я решил написать интерпретатор который бы брал мой привычный компактный код переводил бы его в листинг ассемблера и уже этот листинг скармливал бы основному компилятору - ml.exe большой плюс этого интерпретатора состоял бы и в частности в том что никаких (от слово вообще) ограничений в написании кода по сути хоть пиши в коде на русском главное научить интерпретатор понимать код и синтаксически правильно переводить это в ассемблерный листинг Я понимал что задача эта не тривиальная но всё же полгода тому назад я взялся за этот проект интерпретатора вначале работы я представлял себе что мой интерпретатор сократит время сборки в два а то и в три о то и в четыре раза дальше я даже и не мечтал недавно я закончил основную часть проекта остались только небольшие ньюансы результат превзошёл все мои ожидания тот самый проект скомпилировался за 70 миллисекунд в 22 раза быстрее из них первые 20 миллисекунд ушло на работу интерпретатора а остальные 50 миллисекунд потратил - ml.exe Я понял одно что макросы это конечно хорошо но интерпретатор гораздо лучше да и описывать в функциях интерпретатора поведение гораздо легче чем то же самое описывать в макросе разбор и вычесление выражений у меня в интерпретаторе правильное но немного другое по этому я и заинтересовался обратной польской записью и соответственно попробую перевести разбор и вычесление выражений в интерпретаторе на эти рельсы то что это будет немного проще это уже и сейчас мне понятно а насчёт скорости не знаю но думаю что медленней точно не будет Получилось очень длинно мне просто захотелось более подробно рассказать о том почему я заинтересовался обратной польской записью многие конечно не поймут по этому сразу оговорюсь что занимаюсь этим просто ради любопытства Интересно кто нибудь занимался подобной тематикой
Ну Форты, конечно, быстро парсятся, и их можно интерпретировать без дополнительных структур данных, таких как AST (абстрактное синтаксическое дерево). Но ты реально хочешь перекрутить себе яйки использованием Форта? Ты потом сам возненавидишь свой интерпретатор.