|
Временно недоступен
957 / 228 / 14
Регистрация: 12.04.2009
Сообщений: 926
|
|||||||||||||||||||||||||||||||||||||||||||||||||||
Пишем свой интерпретатор языка BASIC20.06.2009, 20:03. Показов 261674. Ответов 464
Метки нет (Все метки)
Благодаря форуму и Evg в частности интерпретатор развивается, потихоньку превращаясь в простенький интерпретатор QBASIC.
Некоторые из самых старых версий сохранились в теме и ссылки на них будут добавлены в это сообщение,а также ссылки на другие темы,связанные с этой. Репозиторий с проектом находится тут, там же есть возможность в браузере посмотреть историю ревизий (английский в логах весьма примитивен,комментарии и рекомендации можете писать в личку),а также скачать самый последний архив репозитория в формате .tar.gz Если кто-то пользуется Subversion,скачать исходники можно так:
Технический приём для формирования согласованных данных https://www.cyberforum.ru/c-linux/thread46096.html Вопрос по svn (Subversion) Создание системы тестирования ПО. Вопрос про разные реализации бэйсиков Можно ли выразить порядковый номер элемента массива через индексы? [C++] Какие флаги указать линкеру для компиляции программы? Как можно определить переменную в файле configure.in,чтобы её можно было использовать в Makefile? Странный SIGSEGV, или что зависит от порядка написания интерфейса класса https://www.cyberforum.ru/c-linux/thread61324.html Альтернативная версия интерпретатора от Evg на C Это простая реализация разбора выражений, написанная Evg на C: Представление выражения в двоичном дереве ***************** Первое сообщение: ***************** Задание(Страуструп,из книги,по готовому коду): Введите программу калькулятора и заставьте её работать.Например,при вводе
LexicalAnalyzer.h
LexicalAnalyzer.cpp
main.cpp
Анализатор-то работает,но конечное значение не вычисляется.Более того,если вводим
Добавлено через 2 часа 5 минут 30 секунд Пришлось решать влоб с дебаггером.У Страуструпа опечатка (или намеренная ошибка,что более вероятно ) Вот в этом куске кода в функции get_token():
Добавлено через 16 минут 19 секунд И ещё опечатка была
31
|
|||||||||||||||||||||||||||||||||||||||||||||||||||
| 20.06.2009, 20:03 | |
|
Ответы с готовыми решениями:
464
Пишем свой интерпретатор языка BASIC
Пишем свой чекер |
|
Временно недоступен
957 / 228 / 14
Регистрация: 12.04.2009
Сообщений: 926
|
||||||
| 30.06.2009, 22:56 [ТС] | ||||||
|
Спасибо.Есть над чем поразмыслить.
Пару ньюансов хочу спросить.Я понимаю,это просто твой набросок,и всё же любопытно. 1)Для этих переменных :
2) Почему для функций используется слово extern(для глобальных переменных понятно),ведь они и так должны прекрасно работать?
0
|
||||||
|
|
|
| 30.06.2009, 23:14 | |
|
Ответ на оба вопроса - потому что я всю жизнт на Си писал. Конечно используй string. А слово extern обычно для красоты пишут, чтобы подчеркнуть, что в данном моодуле (куда этот интерфейс будет подключаться) этой функции нет. Фактически я описал тебе файл Parser.h, а тебе к нему надо написать Parser.c
1
|
|
|
Временно недоступен
957 / 228 / 14
Регистрация: 12.04.2009
Сообщений: 926
|
|
| 01.07.2009, 02:11 [ТС] | |
|
Evg, появилась у меня такая мысль,после того,как немного прочитал про парсеры и т.д. Делаем таблицу,или вектор(можно состоящий из структур),в котором записаны заведомо ложные последовательности символов/можно и token'ов,и при каждом вызове парсера будем сверять текущее положение дел с этой таблицей.При нахождении ошибки можно выдать сообщение,продолжить парсить и т.д.И как вариант делать инкремент некой переменной в объекте структуры(коим является сама ошибка).Затем сортировать таблицу->в итоге получим,что самые частые ошибки будут лежать в начале вектора/таблицы.А при вызове алгоритма поиска эти ошибки на поверхности.Типа мини-предсказательный механизм.
Не по теме: Но в-общем я не представляю ещё весь механизм,если это плохая идея,не отвечай зазря,неохота просто так дёргать вопросами...
0
|
|
|
|
|
| 01.07.2009, 08:17 | |
|
Э... я просто не совсем понял, идею чего ты мне описал
Как сделать вариант с продолжением работы после ошибки? Это называется error recovering (восстановление из ошибочных ситуаций). На твоём месте я бы пока этого не делал, т.к. это довольно сложная проблема и решать её имеет смысл только тогда, когда у тебя интерпретатор в более-менее серьёзном виде заработает
1
|
|
|
Временно недоступен
957 / 228 / 14
Регистрация: 12.04.2009
Сообщений: 926
|
||||||||||||||||||||||||||||||||||||||||||||||
| 02.07.2009, 06:40 [ТС] | ||||||||||||||||||||||||||||||||||||||||||||||
|
Воодушевившись твоим интерфейсом,начал потихоньку писать с header-ов,чтобы хоть немного навести порядок.Захотел для начала сделать нормальный дебаггер,чтобы сразу было видно,где возможная ошибка и т.д.
Структуру дебаггера задумал сделать так,чтобы был некий переключатель
.Ещё подумал,было бы неплохо иметь таблицу зарезервированных слов,чтобы легче искать было и код не разрастался. Ну,конечно всё ещё в процессе,но если есть какие-то замечания,лучше,пока далеко не ушёл,выслушать их.Вот пока код.(Да,английский у меня хромает )debugger.h
debugger.cpp
parser.h
parser.cpp
driver.h
driver.cpp
Входной файл: source.bsc
Добавлено через 23 минуты 40 секунд P.S.Да,кстати,я тут подумал,почему была вообще эта проблема со съеденной точкой - по ходу дела функция .get() вынимает неформатированный размер данных в 1 char из потока,а .putback() отдаёт форматированный поток,поэтому следовало пользоваться функцией .unget().Это вроде не написано в объяснении библиотеки,я просто так подумал. Добавлено через 1 час 30 минут 36 секунд P.S.2 Чёт не пойму,как это я так сделал,как это работает?
0
|
||||||||||||||||||||||||||||||||||||||||||||||
|
|
|||||||||||
| 02.07.2009, 11:04 | |||||||||||
|
Отсмотрю попозже или вечером. Вкратце пока то, что с ходу могу ответить
Чтобы не пуатть терминологию, не слудует называть деяние "дебаггером". Более правильно - "внутренние отладочные печати" или "внутренняя отладка", чтобы не путать с программой-отладчиком (которая и есть "дебаггер") Удобнее для разных фич делать внутреннюю отладку под разными макросами. Т.е. парсер печатать под одним макросом, таблицу переменных под другим и т.п. Когда программа разрастётся, то суммарная выдача станет очень большой, а потому надо уже делить мух от котлет Комментарии лучше пищи на русском. По собственному опыту знаю, когда слабо владеешь языком, то уже через неделю сложно будет понять, что же ты там описал ломанным английским ![]() > // Что и каким образом передаётся в функцию? В функцию не должно передаваться ничего. Смысл следующий. Ты вызываешь parser_GetToken, которая в процессе работы взводит 4 глобальных переменных. А процедура parser_PrintCurToken просто печатает эти 4 значения. Т.е. пара этих процедур работает симметрично: одна записывает результат, другая его читает и печатает > если ещё не надоел Попробую пояснить попроще. Как-то меня начальник спросил, считаю ли я себя хорошим специалистом. Я ответил что нет, потому как считаю, что без того, чтобы кого-то обучать (обучить) хорошим специалистом никогда не станешь. Заниматься преподаванием мне как-то неинтересно, ибо обучшение материала строго по плану и по расписанию - это попросут затратить кучу усилий практически впустую. В твоём же случае немного по другому. Ты сам в этом процессе заинтересован, пинать тебя не надо. Программирование - работа творческая, она не может идти по расписанию. Когда ты хочешь - ты программируешь. И программируешь то, что интересно, а не то, что записано в плане. Когда есть желание и заинтересованность - то производительность работы очень высокая. Поэтому я считаю, что всё (или почти всё), что я тебе рассказываю - оно оказывается с пользой для дела. Поэтому в том числе и в моих интересах, чтобы ты довёл эту работу до какого-то логически завершённого состояния. Добавлено через 51 минуту 10 секунд Ну будещее. Выкладывай исходники в двух экземплярах: 1. Так, как ты уже делаешь (чтобы с форума просто посомтреть можно было) 2. Заархивируй весь каталог с исходниками (чтобы к себе по простуму скопировать и запустить) Добавлено через 2 минуты 4 секунды Да, вот ещё. Посмотри ещё у меня в исходниках судоку как сделано ASSERT. Это внутренние самопроверки. Для порядку тоже учись этой вещью пользоваться. Если непонятно, что это - поясню Добавлено через 5 минут 49 секунд Ну и сразу концептуальное замечание. В debugger_Print у тебя стоит switch. В таких случаях надо обязательно лепить ветку default, в которой помещать вызов FatalError (ну или как там её назвать). В один прекрасный расстановка таких условий в недостижимых точках и ASSERT'ы сэкономят тебе кучу времени при отладке Добавлено через 14 минут 23 секунды Ну и ещё момент. У тебя отладочная печать принудительно срётся в файл. Здесь бы по аналогии с разделением флага DEBUG сделал разделение выходного файла. Т.е. завёл бы глобалы "ofstream debug_FileParser, debug_FileVarTable", сделал бы процедуру инициализации debug_Init, в котором бы делал открытие данных файлов. А дальше уже по ситуации. Если тебе удобно раздельная печать - открываешь два разных файла. Если тебе удобно видеть их совместно (когда надо понять, что в каком порядке запускается), то один файл открываешь, а для второго делаешь типа "debug_FileVarTable = debug_FileParser". Если нужно печатаь на экран, а не в файл, то просто "debug_FileParser = cout; debug_FileVarTable = cout;". В итоге получается, что у тебя всё гибко, а менять код надо только в одной процедуре Кстати, выше я писал, что в parser_PrintCurToken не надо ничего подавать (я обычно исхожу из того, что печать всегда идёт на экран). В данном случае ты поступил абсоютно правильно, подав сюда указатель на файл (правда назвал его неправильно и имя параметра у тебя перекрывает имя глобальной переменной). Процедура печати должна только печатать. А куда печатать - это пускай разбираются "наверху" и подают файл параметром. Содержимое печати (т.е. то, что ты увидишь) удобнее было бы не тупо 4 значения (при этом надо помнить, что на какой позиции находится), а что-то типа следующего. Для строки "LET a=51" file="source.bsc", line=1, token=KW_LET, token_str="LET" file="source.bsc", line=1, token=IDENT, token_str="a" file="source.bsc", line=1, token=DELIM_EQUAL, token_str="=" file="source.bsc", line=1, token=CONST_INT, token_str="51" Кстати, при правильном построении синтаксического разборщика TokenNext, который ты добавил, он не нужен. По крайней мере в теории пока выглядит так Добавлено через 38 секунд Нумерацию строк принято начинать с 1, а не с 0 Добавлено через 21 минуту 25 секунд Посмотрел твой вариант GetToken - моск сразу же сломался ![]() Ещё раз формальное описание грамматики (чтобы перед глазами было)
1
|
|||||||||||
|
Временно недоступен
957 / 228 / 14
Регистрация: 12.04.2009
Сообщений: 926
|
||||||||||||||||||||||||||||||||||||||||||||||
| 05.07.2009, 04:30 [ТС] | ||||||||||||||||||||||||||||||||||||||||||||||
|
Итак,потихоньку едем далее.Немного помучавшись,я всё-же оставил переменную NextToken.Не знаю почему,мне кажется это удобным.В некоторых моментах я опирался на эту переменную..
Если имеется такой код:
Про assert я почитал,конечно,интересная штука,но хотелось бы сделать подобное силами С++,а это надо углубиться в тему,я ещё даже не знаю генерацию исключений. Кое-какие мелочи исправил в отладочной печати,обработку ошибок даже пока не знаю,как делать.Хочется сделать получше,надо подумать.Как делаются такие вещи в нормальных программах? Ну вот,теперь помоему,самое время приступить к организации рекурсивного спуска.Если,конечно,на данном этапе не сделал таких ошибок в проектировании,которые потом запутают донельзя. Т.е.самое сложное ещё впереди. Код,надеюсь,не очень запутан. Что есть пока выкладываю,вот: driver.h
driver.cpp
parser.h
parser.cpp
debugger.h
debugger.cpp
Входные данные: source.bsc
0
|
||||||||||||||||||||||||||||||||||||||||||||||
|
Временно недоступен
957 / 228 / 14
Регистрация: 12.04.2009
Сообщений: 926
|
|
| 05.07.2009, 05:02 [ТС] | |
|
P.S.Я так полагаю,мне придётся искать инфу по бейсику в сети?
Честно,не помню уже этот язык.Да,ещё забыл-NextToken почему ещё оставил: подумал,что будет удобно проверять составные операторы,типа << , += , := , и т.д.
0
|
|
|
|
|||||||||||||||||||||
| 05.07.2009, 11:42 | |||||||||||||||||||||
|
> то есть важно только количество "токенов",а не строк и др
Да. Именно исходя из этих соображений должен писаться парсер. Ведь синтаксическому разборщику всё остальное неважно (ему не нужны комментарии, лишние проблеы и т.п.) > Последующие вызовы функции (когда "токенов" уже нет) бесполезны Вопрос в том, а как понимать, что мы имеем последний токен. EOF нужен только для этого. EOL нужен для обозначения конца оператора (statement). Чтобы конструкция "LET a=1 LET b=2" трактовалась как ошибочная (ибо считаем, что положено не более одного оператора в строке). Так же EOL служит разделителем между операторами, что упростит процесс синтаксического разбора. Но, оставь так, как сделал. Потому что более полезно учиться на своих ошибках, а не на том, как другие тебе их указывают > обработку ошибок даже пока не знаю,как делать Предлагаю такой вариант. Для начала сделай хоть как-то, чтобы оно работало в самом примитивном варианте. Лишь бы потом можно было просто найти все места, которые надо исправить. КОгда интерпретатор более-менее задышит и будет поддерживать несколько конструкций - тогда будет проще спроектировать конкретную реализацию обработки ошибок, потому как уже можно будет охватить широким взглядом всё то, что уже есть (а не виртуально то, что предположительно будет, как это сейчас) > Как делаются такие вещи в нормальных программах? Все интерпретаторы работают до первой ошибки. Компилятор (поскольку он не исполняет программу, а лишь преобразует её из языкового вида в машиннный код) работает до конца, и ищет по пути максимальное количество ошибок. Но, возможно сам наблюдал, что пропущенная запятая после описания внешней процедуры способна навести две страницы сообщений об ошибках. Как аккуратно восстанавливаться из ошибочных ситуаций - написаны целые научные труды. Я бы тебе пока не рекомендовал лезть в это дело, дабы не забивать голову вещами, которые тебе пока не нужны > Ну вот,теперь помоему,самое время приступить к организации рекурсивного спуска Можно конечно и так. Но есть ещё две компоненты, которые неплохо было бы реализовать. Это таблица переменных и пакет констант. Таблица переменных сейчас у тебя сделана просто ввиде шаблонного массива, хотя удобнее было бы её выделить в отдельную компоненту и накрыть интерфейсом. Константы у тебя представлены в виде double'а, а нужно их сделать в виде класса, в котором хранится вся информация (её тип и значение), а так же реализованы операции над константами (чтобы не заниматься этим при синтаксическом разборе) Хотя действительно, понятнее всего было бы сделать так. Для начала сделать разбор синтаксиса, полгатаь, что константы у нас только double, сделать какой-то примтивный вариант, чтобы работал, а потом уже станет видно, как из всего этого отделить рабуоту с константами и работу с таблицей переменных > Я так полагаю,мне придётся искать инфу по бейсику в сети? А зачем. У тебя же не стоИт задача реализовать полноценный бэйсик. Ты пока только учишься, а потому можно сделать что-то очень простое бэйсикоподобное. Когда ты реализуешь до конца свою простую задачу, я тебя уверяю, ты сам сможешь её расширить до нормального интерпретатора. И синтаксис потом поменять как угодно Щас погляжу твоё творение Добавлено через 22 минуты 6 секунд Для твоего исходника напечаталось так
В parser.cpp есть фрагмент (строки 89-93)
Добавлено через 14 минут 37 секунд Вот примерное формальное описание синтаксиса на текущий момент
Так что предлагаю тебе сначала реализовать такой синтаксис, а потом начать выделять константы и таблицу переменных Сейчас получается всё просто. Верхний уровень (по сути дела обработка StatementList) что-то типа того. Пишу в своём предположенииналичия EOL'ов и EOF'ов
Добавлено через 6 минут 57 секунд На начальном этапе с EOL'ами можешь не заморачиваться, но тогда у тебя будут разрешены операции типа "LET a=1 LET b=2". В скриптовых языках принято операции разделять энтером или знаком ";" Добавлено через 14 минут 52 секунды Кстати, в 20-м посте я ещё в терминологии по ходу дела ошибся. Тут куча тонких моментов, но по ходу дела общепринятые термины такие. Парсер является лексическим анализатором (т.е. процесс формирования токенов из символов называется lexical analyser). А вот построение операторов - это синтаксический разбор (а правила называются syntax rules)
1
|
|||||||||||||||||||||
|
Временно недоступен
957 / 228 / 14
Регистрация: 12.04.2009
Сообщений: 926
|
||||||||||||||||
| 06.07.2009, 23:30 [ТС] | ||||||||||||||||
|
Пытаюсь сделать такой шаблон (это набросок,пока не представляю как будет):
0
|
||||||||||||||||
|
|
|
| 07.07.2009, 08:54 | |
|
Не совсем понятно, зачем тут шаблон, если процедура у тебя будет присутсвовать в одном экземпляре. Попробуй всё-таки пояснить, а я возможно укажу тебе ошибку в твоих рассуждениях
Ну а коли его сделал, то шаблон в качестве параметра имеет тип. У тебя тут типа нет никакого среди параметров, а потому компилятору непонятно, как выбирать функцию. Для void функции по идее надо так: "syntax_parserPrimary<int>();" Ну или тип нужный указываешь Добавлено через 2 минуты 31 секунду Код функции при первом просмотре не смотрел, а сейчас явно вижу ошибку. Ты почему-то считаешь, что функция у тебя может возвращать и int и double - такого быть не может Не говоря уж о том, что по ветке default у тебя ничего не возвращаетсяПо поводу синтаксического нисходящего разбора - всё-таки поковыряйся вот тута Представление выражения в двоичном дереве В 3-м посте исходник, в 5-м комментарии к нему. Читать именно в части синтаксического разбора, грамматический там сделан излишне навороченно, тот вариант, который у тебя, он более удобен Добавлено через 2 минуты 15 секунд Я так понимаю, что ты добрался до того, как же аккуратно поддерживать константы разных типов? Если так, то могу рассказать вкратце
1
|
|
|
Временно недоступен
957 / 228 / 14
Регистрация: 12.04.2009
Сообщений: 926
|
|
| 07.07.2009, 23:47 [ТС] | |
|
Я думал так: когда у нас константа,нужно вернуть её значение,но ведь тип возвращаемого не ясен с самого начала.Я надеялся это можно решить с помощью шаблонов,но,похоже ошибся.
Тут я вижу 2 выхода из ситуации(трудно абстрактно мыслить на много шагов вперёд ):1)Сами функции ничего не возвращают,но изменяют переменные в объекте класса,который и требуется разработать.Возможно придётся идти на какие-то хитрости,вроде того,какая переменная изменилась при предыдущем вызове- int или double) 2)Для каждой переменной типа,будь то int или double,есть своя функция,которая с этим типом работает.То есть типизация по сути добавит кучу экземпляров функций,и получится что-то вроде древовидного ветвления,где будут вызываться нужные функции согласно типу. 3) Первый вариант мне не нравится из-за опасности запутаться в бесконтрольном изменении переменных,второй тоже запутан и сложен.Есть какой-то третий вариант?Я твои исходники специально не стал смотреть пока,хочется скорее совета,навроде заданного курса,чтобы потом не заниматься мазохизмом (по крайней мере не слишком много),переписывая и переписывая.
0
|
|
|
|
||||||
| 08.07.2009, 11:32 | ||||||
|
На самом деле тебе нужно разработать две вещи:
1. Класс, описывающий константу произвольного типа. На каждую константу (возникшую в процессе интерпретации) создаётся новый экземпляр данного класса. Ну и в идеале удаляются ненужные экземпляры, но с оптимальным потреблением памяти я бы пока не стал заморачиваться. Потом это будет проще понять. Кстати, в проект рядом с исходниками положи текстовый файл, и записывай туда все идеи, которые отложены на "потом". 2. Некую таблицу, которая описывает пары "переменная-константа", где записаны текущие значения переменных По поводу мазохизма. В каких-то местах я намеренно веду тебя по тому пути, что придётся потом переделывать код в деталях (потому что ты с ходу не видишь будущих проблем, а я проблемы вижу только в теории, что получится - пока не знаю). Это тебя научит изначально писать код так, чтобы он легко подвергался переделкам в случае необходимости. В учебниках пишут только правильный итоговый код, поэтому по учебникам ты этому не научишься. Только набив шишки на практике. Поэтому не надо бояться ошибиться. Не ошибается тот, кто ничего не делает. Что касается представления класса коснтант, то я бы сделал примерно вот так:
Надеюсь, что смысл донеёс Добавлено через 1 минуту 3 секунды Если непотяна какая-то часть именно в плане технической реализации - лучше спрашивай сейчас ![]() Добавлено через 7 минут 13 секунд Кстати, а на чём ты пишешь? Просто все твои примеры с ходу идут у меня на gcc. При этом тесты исходников явно записаны под виндами (там перевод строки не такой, как в линухе). Т.е. у тебя какая-то gcc-образная среда из разряда Dev-C++, mingw или всё-таки в стандартных билдерах ухитряешься так аккуратно написать? Добавлено через 9 минут 6 секунд Да, вот ещё. В MyConst надо сдеать конструкторы, которые строят константу по её строковому представлению. Т.е. синтаксический анализатор имеет токен, содержащий константу, он просто дёргает соотвествующий конструктор из MyConst и не заморачивается там, чтобы самому выгребать значение из строкового представления. Для полноты картины в этой части должны быть два конструктора: создание целого значения по строковому представлению, создание плавающего значения по строковму представлению. Для полноты картины теоретически можно добавить просто "создание значения по строковму представлению", а тип будет разбираться внутри, но это означает, что фактически туда засовывается небольшой кусочек парсера, что не есть хорошо. Но в какойто другой программе (в которую ты, к примеру, в будущем затащишь свой модуль констант), это может пригодиться Добавлено через 1 час 40 минут 24 секунды Кстати, если исходить из простоты, то в случае деления на 0 и прочих ошибок можно сразу вызывать функцию, выдающую пользовательскую ошибку и завершать работу. В этом случае модуль констант будет не абсолютно независимым, а у него будет зависимость от модуля ошибок, что не страшно
1
|
||||||
|
Временно недоступен
957 / 228 / 14
Регистрация: 12.04.2009
Сообщений: 926
|
|
| 08.07.2009, 14:12 [ТС] | |
|
Вроде теперь образуется некая структура,просто до этого не мог представить в теории реализацию.Есть некоторые вопросы.Для начала,примерно так я представляю будущую структуру:
1)Значит,будет класс,описывающий произвольный тип переменной. 2)Сами функции рекурсивного спуска будут возвращать объект класса,а все ошибочные ситуации обрабатываются внутри самого объекта. 3)Конструктор,я полагаю,будет принимать готовую строку,плюс логическое значение(зависит от TOKEN_CONST_***),(или сам токен),которое передаётся в переменную is_float,которое,в свою очередь,влияет на работу конструктора.Но в принципе можно сделать 3 конструктора просто для удобства. Поскольку ты написал,что не стоит заморачиваться с памятью,я так понимаю,что во-первых,каждый объект будет создаваться в динамической памяти,но не понимаю,зачем это нужно,разве не достаточно локальных копий во время рекурсии(?),во-вторых,я думаю,там будет какой-то автоматический сборщик мусора,а как это реализовать,я понятия не имею(пока),я просто немного знаком с темой классов и немного с темой динамической памяти. Зачем будет нужен конструктор-копировщик,пока для меня тоже загадка. Пишу я в среде Code::Blocks,компилятор gcc,ты не ошибся,но правда пишу код в Linux,уж не знаю,что там не так с переводом строки.Но также стараюсь не использовать специфических вызовов типа как в винде system(***) ,код по идее должен скомпилироваться и в билдере на винде,но точно не знаю,я просто пользуюсь только Линуксом
0
|
|
|
|
||||||||
| 08.07.2009, 14:39 | ||||||||
|
С переменными в этой части можно не заморачиваться и считать, что тип переменной есть текущее значение типа константы. Т.е. записали в переменную целое число, значит в этот момент переменная целая, записали плавающее - значит плавающая. Т.е. тип переменной по сути дела отсутсвует, существует только тип текущего значения. Либо считать, что при первой записи определяется тип переменной, а при дальнейших записях значение всегда будет сводиться к этоу типу. Либо явно в языке ввести тип переменной. На том бейсике, на котором я сто лет назад работыл было так, что A% - целая, A! - плавающая одинарной точности (float), A - плавающая двойной точности (double), A$ - строковая. Ты сам выбирай поведение, которое кажется тебе логичным, но первый вариант (когда тип переменной определяется по записанному в него значению), вроде бы наиболее прост в реализации и начать можно с него. А потом переделать (тоже полезный опыт по переделыванию)
1
|
||||||||
|
Временно недоступен
957 / 228 / 14
Регистрация: 12.04.2009
Сообщений: 926
|
||||||||||||||||||||||||||
| 09.07.2009, 23:47 [ТС] | ||||||||||||||||||||||||||
|
Я тут подумал,не лучше ли будет для переменных организовать свой класс,в который,в свою очередь,будет содержать в себе экземпляр класса констант?То есть,когда потребуется выполнить
Еще небольшая мелочь в оформлении-проектировании: В syntax_parser.h у меня записано: syntax_parser.h
То есть я делаю тобой предложенный enum частью синтаксического анализатора(ну не в класс констант же его включать). Мне не нравится,что пришлось так неэстетично записать включение "const_class.h" прямо посередине header-а,как-то не смотрится,да и запись class ConstVar; тоже не к месту.Это всё потому,что enum Types используется в классе ConstVar,который и подключается с помощью "const_class.h".Как бы так сделать поэлегантнее,чтобы в то же время сохранить некий порядок и структуру разделения? Модульное программирование для меня пока не совсем ясно,вот такие ньюансы немного сбиваю с толку.На всякий случай вот код(я надеюсь компилить его не нужно): syntax_parser.cpp
const_class.h
const_class.cpp
Добавлено через 29 минут 46 секунд P.S. Сейчас увидел только: я по ходу ошибся в перегрузке операторов,возвращать нужно было this а не *this,так? Просто я на лету делал,пришлось попутно читать про перегрузку ,union и т.д.
0
|
||||||||||||||||||||||||||
|
|
|||||||||||||||||||||||||
| 10.07.2009, 14:28 | |||||||||||||||||||||||||
|
Программистов условно можно разделить на два типа. Первые как правило сначала всё долго и тщательно всё планируют, а потом реализовывают. Вторые сначала в голове имеют общую картину, сразу же начинают реализовывать наброски, по ходу дела постоянно что-то корректируя, и в процессе работы начинает вырисовываться чёткая картина. Программисты первого типа как правило страдают тем, что их код очень трудно поддаётся дальнейшей переделке, а времени на раздумья уходит слишком много, потому как в процессе работы появляется куча ньюансов, которые теоретически сложно предугадать. Программисты второго типа изначально пишут код с учётом дальнейших переделок, а потому код оказывается гибким. Я отношу себя ко второму типу. Если тебе ближе первый тип, то мне надо будет немного по другому вести "обучение", ибо я исхожу из того, что при необходимости мы всегда можем подточить и подпилить, а затем аккуратно переделать.
Правильное ощущение выработается со временем, а здесь опять та же проблема - ты пока ещё не понимаешь, как в и тоге всё получится В общем поэтому я и говорю, что не надо бояться эксприментировать, ошибаться и переделывать, ибо пока на своём опыте не увидишь, как делается "неправильно", то никогда на уровне ощущений не поймёшь, как делать "правильно"Добавлено через 13 минут 16 секунд Немного поглядел исходники - у тебя с терминологией что-то в кучу намешано
Т.е. что-то типа того
Добавлено через 18 минут 27 секунд По поводу твоего кода
Операция сложения должна выглядеть так (по отношению именно к твоему коду):
Кстати, когда есть ситуация, которая кажется тебе ошибочной (ты отметил её "Types messing Error?"), сразу влепи туда вызов своей процедуры error, а то потом забудешь и долго будешь отлаживать, программу Добавлено через 7 минут 4 секунды Класс, который я назвал Constant можно назвать Value, чтобы тебе меньше было путанницы. Тогда у тебя остаётся только два понятия: Value и Variable, а понятия Constant как такового не будет Добавлено через 2 минуты 50 секунд syntax_parserPrimary написан правильно (в итоге он получается предельно примитивным) Добавлено через 2 часа 2 минуты 40 секунд
Да, по поводу видимости переменных. Попробую условно объяснить на пальцах, как это в компиляторах делается (чтобы ты имел представление о том, как тебе гибко написать код). Заводится некое понятие "таблица переменных", которая содержит в себе список переменных (или массив - не суть). Для глобальных объектов заводится одна таблица. Обрабатываем процедуру, заводим вторую таблицу и локалы процедуры затаскаваем в неё. Обрабатываем лексический блок (фигурные скобки), заводим третью таблицу и локалы лексического блока пишем уже туда. таким образом тыблицы у тебя образуют что-то типа стека. Ну и далее когда встречается использование переменной, то ищес в таблице, которая на вершине стека, если не нашли - то дальше и т.п. Тебе пока предлагаю ограничиться лишь одними глобмальными переменными. При этом ты заводишь понятие таблицы переменных, но таблица у тебя будет одна. А потом в случае чего добавишь
1
|
|||||||||||||||||||||||||
|
Временно недоступен
957 / 228 / 14
Регистрация: 12.04.2009
Сообщений: 926
|
|
| 10.07.2009, 20:54 [ТС] | |
|
Сейчас читаю про неявные преобразования.
Есть одна неувязка.При смешении типов,где гарантия,что это не приведет к потере данных и их искажению? То есть,если мы плюсуем 4 (long) и число,заведомо большее,чем границы диапазона long,то что произойдёт с этими данными? Я считаю,тут нужно пересмотреть смешение типов,или сделать какой-то алгоритм,проверяющий выход за границы,exeption,или приводить тип long к doube,но не наоборот. Ведь такая проблема может возникнуть? P.S. Но в принципе я сам уже ответил на свой вопрос,что все исключения добавляю потом,как всё заработает,так что зря написал,наверное.Просто первой мыслью было запретить смешение типов,вот и всполошился,но уже вижу,что просто можно добавить исключения потом.
0
|
|
|
|
|||||||||||
| 11.07.2009, 00:25 | |||||||||||
|
Сечас мы делаем ровно то, что делается в языках Си\Си++. Т.е.
Ну и действительно, ты сам всегда в момент вычисления результат операции можешь сам всё проверить и запрограммировать так, как душе угодно. И всё это будет внутри модуля констант. Ну а пока наша задача сделать синтаксический разбор и добраться-таки до print'а. Добавлено через 2 минуты 27 секунд Т.е. мне кажется, что когда мы это допилим, а потом, допустим, сделаем if'ы, то тебе сразу многие вещи станут гораздо понятнее, после того, как ты их реализуешь. В том числе и вещи, которые мы ещё только будем делать потом, но у тебя скорее всего должно появиться понимание, как оно будет сделано. Так это или нет - скоро, думаю, узнаем
1
|
|||||||||||
|
Временно недоступен
957 / 228 / 14
Регистрация: 12.04.2009
Сообщений: 926
|
|
| 12.07.2009, 02:33 [ТС] | |
|
Не по теме: перед этим сообщением было сообщение-вопрос,и оно было удалено модератором В моём коде нет функции __tmain,или ты что-то поменял,или среда поменяла за тебя сама.Возможно,функция main в проектах на VS должна называться tmain,но точно не знаю.
0
|
|
| 12.07.2009, 02:33 | |
|
пишем свой троян с нуля Пишем свой класс, спецификатор доступа protected Интерпретатор небольшого языка программирования на С++ Не удается откомпилировать интерпретатор М-языка
Искать еще темы с ответами Или воспользуйтесь поиском по форуму: |
|
Новые блоги и статьи
|
|||
|
Особенности gif сгенерированных в Си
russiannick 21.10.2026
Рассмотрим генерацию gif файлов, которые в Си имеют свои особенности.
Gif представляет собой графический формат популярный в интернете. Посмотрим на палитру. Gif файл сгенерированный в си отличается. . .
|
FPS контроллер с анимированными Y-Bot и 3D-звуком miniaudio (Box3D, Recast, BehaviorTree.CPP, ozz-animation, ImGui, OpenGL и SDL3)
8Observer8 18.10.2026
Содержание блога | English Version
Полное мультиплатформенное руководство по стартовому проекту с FPS-контроллером игрока, анимированными роботами Y-Bot с 4-слойным блендингом, 3D-физикой Box3D,. . .
|
Локальный http сервер
Argus19 17.10.2026
Локальный http сервер
На работе, на двух локальных компьютерах круглосуточно работают программы опроса приборов с записью в книги Excel,которые раз в сутки записываются на сервер компании. Т. к. . . .
|
Копировать Windows на новый диск на UEFI
damix 17.10.2026
https:/ / uriesk. wordpress. com/ 2016/ 11/ 01/ move-window-10-to-ssd-on-a-uefi-dualboot-system-with-basic-linux-tools/
TL DR
Вставляем диск, а старый подключаем через переходник, грузимся с лайва. . .
|
|
ИИ не может найти нужный язык в списке
Supersumestria 05.10.2026
Я ему даю вот такое изображение и прошу найти и подчеркнуть немецкий язык.
Возвращает он вот это:
https:/ / i. **********/ vqBWLe2. png
Нужную строчку в 3й колонке просто выдумал. .
Это. . .
|
Новая последняя моя музыка в SUNO
zorxor 05.10.2026
Здравствуйте, дорогие мои друзья! С большой радостью я хотел бы представить вам свою новую последнею музыку, которую сгенерировала мне по моей просьбе нейросеть SUNO. С уважением, zorxor.
Это. . .
|
Программный домашний кинотеатр
russiannick 27.09.2026
Сподобился на программный домашний кинотеатр. В качестве ЯВУ по традиции выбрал js.
В помощники взял Яндекс-Алису.
Было создано три зала на разные интересы.
исторические и ретро
сериал Хичкок. . .
|
Беседа с ИИ о программистах, недопускающих к созданию и правке кода генеративные ИИ и причины этого
zorxor 21.09.2026
Раньше я радовался или получал некоторые эмоции, пусть небольшие, но всё же, от самого процесса написания кода, рекомпиляции и запуска, видя постепенное развитие программы и прочее. А теперь лень. . .
|