Временно недоступен
957 / 228 / 14
Регистрация: 12.04.2009
Сообщений: 926
|
||||||||||||||||||||||||||||||||||||||||||||||
1 | ||||||||||||||||||||||||||||||||||||||||||||||
Пишем свой интерпретатор языка BASIC20.06.2009, 20:03. Показов 242535. Ответов 464
Метки нет (Все метки)
Благодаря форуму и Evg в частности интерпретатор развивается, потихоньку превращаясь в простенький интерпретатор QBASIC.
Некоторые из самых старых версий сохранились в теме и ссылки на них будут добавлены в это сообщение,а также ссылки на другие темы,связанные с этой. Репозиторий с проектом находится тут, там же есть возможность в браузере посмотреть историю ревизий (английский в логах весьма примитивен,комментарии и рекомендации можете писать в личку),а также скачать самый последний архив репозитория в формате .tar.gz Если кто-то пользуется Subversion,скачать исходники можно так: Код
svn co https://basin.svn.sourceforge.net/svnroot/basin basin Технический приём для формирования согласованных данных 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 Пишем свой strlen Пишем свой чекер пишем свой троян с нуля |
12.07.2009, 23:11 | 41 |
По поводу вот этой темы Разыменование итератора
Я бы не стал в переменной хранить непосредственно значение (без дополнительного класса Value). Это чисто к сведению. Если тебе видится, что так будет лучше - реализовывай именно так. Из вариантов "блее правильный" и "более понятный" в данном случае уместен "более понятный"
1
|
Временно недоступен
957 / 228 / 14
Регистрация: 12.04.2009
Сообщений: 926
|
||||||
12.07.2009, 23:49 [ТС] | 42 | |||||
Хм,я объясню,почему выбрал этот вариант. Сначала я делал именно с Value как членом класса,но потом выяснилось,что необходим вызов конструктора Value,то есть требовалась запись вроде такой
0
|
Временно недоступен
957 / 228 / 14
Регистрация: 12.04.2009
Сообщений: 926
|
|||||||||||||||||||||||||||||||
13.07.2009, 01:36 [ТС] | 43 | ||||||||||||||||||||||||||||||
Хочу поделиться дальнейшим ходом написания.Некоторые рассуждения написаны в комментариях.
Ещё подумалось,что лучше бы сделать отдельный модуль types.h,и вырезать туда все перечисления,мне кажется,так будет правильней.Промежуточный результат такой пока: driver.h
driver.cpp
debugger.h
debugger.cpp
parser.h
parser.cpp
0
|
Временно недоступен
957 / 228 / 14
Регистрация: 12.04.2009
Сообщений: 926
|
|||||||||||||||||||||||||||||||
13.07.2009, 01:37 [ТС] | 44 | ||||||||||||||||||||||||||||||
Всё сообщение уже не влазит в одно. А есть сервисы такие,что можно тексты вставлять туда?
value_class.h
value_class.cpp
variable_class.h
variable_class.cpp
syntax_parser.h
syntax_parser.cpp
0
|
13.07.2009, 09:36 | 45 | |||||||||||||||
А в каких случаях тебе нужно создавать переменную со значением? Просто для конструкции "LET a=5" получается что-то типа того:
syntax_parser.c строки 49-50. Ты пишешь, что создаёшь переменную, но это не правильно. Ты в данный момент парсишь ПРАВУЮ часть присваивания, т.е. там, где нужно читать значения. Т.е. все переменные уже должны быть созданы, а ты в этот момент НЕ создаёшь новую переменую, а достаёшь в таблице уже СУЩЕСТВУЮЩУЮ переменную и берёшь от неё значение. Если переменная не существует, то тут выбираешь как действовать: либо выдавать ошибку, либо считать, что для несуществующеё переменной значение равно нулю. Я бы выбрал первый вариант. Возможно мне следовало пояснить момент, который для тебя совсем неочевиден. Имя переменной (в нашем случае это синтаксическое правило Ident) может встретиться в присваивании в правой части или в левой. В этих двух случаях оно трактуется по разному. Устоявшиеся технические термины для этого lvalue (от слова left) и rvalue (от right). Когда имя переменной стоИт в позиции rvalue, то это означает, что нам нужно прочитать ЗНАЧЕНИЕ этой переменной, а когда в позиции lvalue (т.е. фактически мы хотим записать в переменную), то говорят, что нужно АДРЕС переменной. Адрес не в буквальном смысле слова, а в абстрактном - т.е. это нечто, при помощи которого мы можем записать значение в переменную. Иногда rvalue и lvalue называют соотвественно VALUE POSITION и NAME POSITION, но это несколько неудобная терминология из-за слова NAME Вот примеры того, как переменная "AAA" находится в разных позициях
============================================ Ещё момент по твоему коду: value_class.h строки 85 и 86. Именно то место, где слодовало бы поставить ASSERT (внутренний контроль), что от целой переменной нельзя брать плавающее значение и наоборот. Хотя в теории эти методы вообще не должны использоваться за пределами модуля Value (потому как все операции перегружены, печать значения реализована в виде интерфейса, больше снаружи вроде бы как незачем) Добавлено через 5 минут 59 секунд Я понял, почему у тебя получалось создание переменной непосредственно со значением. Но нужно было бы это делать с Value (т.е. распарсили строковое имя переменной, вытащили правую часть в виде Value, создали переменную с Value). Но засада в том, что тут переменную надо НЕ создавать, а записывать в неё (т.е. если переменная уже создана, то новая переменная не нужна)
1
|
Временно недоступен
957 / 228 / 14
Регистрация: 12.04.2009
Сообщений: 926
|
|
13.07.2009, 09:42 [ТС] | 46 |
Да нет же я же написал,что создаю константу,то есть Value ,передаю в неё значение готовой переменной,и сам объект константы возвращаю .Но я понял твою мысль,можно просто сделать Value внутри класса Variable,и просто перегрузить конструктор класса Value,чтобы был и по умолчанию,что позволит создание пустой Value внутри Variable.
Я сделал перегрузку оператора = в классе Variable,так что можно будет просто записать Variable a = syntax_parserExpr();
0
|
Временно недоступен
957 / 228 / 14
Регистрация: 12.04.2009
Сообщений: 926
|
|
13.07.2009, 10:00 [ТС] | 48 |
Уже не возникает,то есть я перегрузил оператор = в классе переменных,а также сделал своё поле значений внутри переменной,и теперь правомерна запись Variable a = syntax_parserPrimary();
Но раз лучше всё-таки сделать членом класса экземпляр Value,то я просто переделаю,перегружу конструктор Value,и всё будет ок.
0
|
13.07.2009, 10:07 | 49 |
Так в том-то и дело, я это уже объяснял, но особого внимания не заострял. Здесь у тебя "a" - локальный объект в твоей процедуре (в интерпретаторе). Т.е. при выходе из этой процедуры "a" разрушится, и в при разборе следующих конструкций у тебя переменная (которая уже на бэйсике) пропадёт
1
|
Временно недоступен
957 / 228 / 14
Регистрация: 12.04.2009
Сообщений: 926
|
|
13.07.2009, 10:10 [ТС] | 50 |
Наверное,ты просто не заметил,все мои переменные будут храниться в контейнере,а он же в динамической памяти,так?
(variable_class.h)
0
|
13.07.2009, 11:00 | 51 |
А... или initialized_vars это у тебя глобальная таблица. Т.е. ты сначала создаёшь локальный объект для переменной, а потом его засовываешь в таблицу? Ну можно и так. В этомс случае у тебя получается избыточный код в syntax_parser.cpp в строках 49-64 (я его понять особо не смог). Вместо всего этого паровоза должно стоять что-то типа того, что я обозначал как Var.GetValue(); Потому как в этом месте ты не должен пытаться понять тип переменной. А то добавишь ещё один тип и во все такие места ндо будет втыкать дополнительный код, а так дополнитеельный код будет только внутри модуля value
Кстати, по поводу отдельного модуля для типов. В теории так и надо делать, но реально у тебя тип используется только в модуле value. Да и если выносить, то кроме enum'а пока вынести больше нечего Добавлено через 38 секунд Я не сразу это понял. Просто я в других терминах мыслю, а потому с ходу не догнал Добавлено через 45 минут 46 секунд Прогнал твои исходники, gcc выдаёт warning'и Я обычно в gcc подаю опции -Wall -Werror Первая опция выдаёт абсолютно все предепреждения, а вторая ломается в случае их (предупреждений) наличии. Временами это задалбывает по мелочи, но хорошо чистит программу от мусора Код
value_class.cpp: In member function 'Value Value::operator+(const Value&)': value_class.cpp:102: warning: converting to 'long int' from 'double' value_class.cpp: In member function 'Value Value::operator-(const Value&)': value_class.cpp:146: warning: converting to 'long int' from 'double' value_class.cpp: In member function 'Value Value::operator*(const Value&)': value_class.cpp:190: warning: converting to 'long int' from 'double' value_class.cpp: In member function 'Value Value::operator/(const Value&)': value_class.cpp:234: warning: converting to 'long int' from 'double'
1
|
Временно недоступен
957 / 228 / 14
Регистрация: 12.04.2009
Сообщений: 926
|
|
13.07.2009, 19:26 [ТС] | 52 |
У меня тоже эта опция стоит,но предупреждений нет.Странно,почему?... В принципе можно использовать static_cast (я их пока путаю,какой из них какими особенностями обладает).
0
|
13.07.2009, 21:08 | 53 |
dynamic_cast имеет смысл только тогда, когда есть виртуальное наследование коассов, во всех осатльных случаях можно static_cast. У меня есть предупреждения, а у теюя нет вероятно потому, что у нас версии gcc разные. Мне вот наоборот кажется, что это мой gcc не по делу warning выдаёт
0
|
Временно недоступен
957 / 228 / 14
Регистрация: 12.04.2009
Сообщений: 926
|
|
13.07.2009, 21:23 [ТС] | 54 |
Сделаю со static_cast.Версия "gcc версия 4.3.3 ".У тебя более ранняя?
P.S. Я установил svn - по ней целая книга есть,довольно сложная система управления.Пока мне удалось просто на одном диске слелать хранилище,создать рабочую копию.Но пока не понятно,как обновляется хранилище? Там читать и читать...
0
|
13.07.2009, 22:04 | 55 |
у меня где-то 4.1.3
Про svn - я всё по online документации разбирался. Правда у меня был опыт работы с cvs, так что там проще всё пошло. Если ссылку найду - кину. Если что непонятно - спрашивай, но создай лучше отдельную тему тут https://www.cyberforum.ru/linux/ Там и другие помогут, а в эту тему врядли кроме нас с тобой смотрит Добавлено через 3 минуты 25 секунд Если я ничего не путаю, то читал я здесь http://svnbook.red-bean.com/ Далее тыкаешь в "Открыть многостраничную HTML версию книги". Что там хорошо, чуть ниже версия на английском языке. Перевод вроде нормальный, но временами бывает, что пока оригинал не увидишь - не поймёшь
1
|
Временно недоступен
957 / 228 / 14
Регистрация: 12.04.2009
Сообщений: 926
|
||||||
14.07.2009, 02:57 [ТС] | 56 | |||||
Кажется,до меня дошло,что ты хотел мне донести.В функции syntax_parserPrimary()
Добавлено через 1 час 13 минут 50 секунд P.S.Да,насчёт сообщения #47, я как-то пропустил,прочитал просто,но сейчас вник и согласен,лучше разделить всё это на функции (я про syntax_IdentLValue (void),syntax_IdentRValue (void) и syntax_Ident() ) ,то есть лучше следовать принципу "разделяй и властвуй",так понятнее код.Я это учту и переделаю.
0
|
14.07.2009, 09:49 | 57 |
Именно так
Судя по всему, ты уже понял про концепции rvalue и lvalue, так что ответ на этот вопрос тебе вроде бы уже понятен. Где-то я уже писал, что на каждый statement по хорошему должна быть своя процедура. Да и вообще. ка каждое синтаксическое правило нужна своя процедура. Ибо так будет проще, особенно если имя процедуры содержит в себе название правила, а в исходнике где-то в головной части или в отдельном текстовом файле ты аккуратно выпишешь все лексические и синтаксические правила, а в комментариях будешь ссылаться на них Потому я тебе и не навязываю свои варианты, а лишь выкладываю "к размышлению". После того, как сделаешь неправильно, только тогда на уровне ощущений начнёшь понимать, как делать правильно. Именно поэтому говорю, что не надо бояться переделывать код, ибо это единственный способ научиться делать правильно Добавлено через 10 минут 1 секунду Кстати, в 30-м посте я ошибся с правилом. Правильно вот так: LetStatement = LET IDENT "=" Expr Да и вообще, сейчас для порядку ещё раз свалю рядом лексические и синтаксические правила. Немного их подкорректирую, всвязи с текущим положением вещей ================================================ Лексика: Код
Const = ConstInt | ConstFloat ConstInt = Digit { Digit } ConstFloat = Digit { Digit } "." Digit { Digit } Ident = Letter { Letter | Digit } Letter = "A" | "B" | ... | "y" | "z" Digit = "0" | "1" | ... | "9" KeywordLet = "LET" KeywordPRINT = "PRINT" Синтаксис. Сделаны следующие изменения: 1. Поправлена ошибка в LetStatement 2. IDENT (лексическая конструкция) заменил на Ident (синтаксическая конструкция), потому как в Ident будет ещё и элемент массива (а то и структуры, если осилим). Аналогично с CONST'ом 3. В Expr добавил унарные плюс и минус Ранее я забыл упомянуть. Слова с заглавной буквы - синтаксические правила, из всех заглавных букв или в кавычках - лексические единицы. Лексические и синтаксические правила НЕ живут в одном пространстве. Т.е. Ident в лексических правилах и Ident в синтаксических - это разные вещи Код
StatementList = Statement { EOL Statement } EOF Statement = LetStatement | PrintStatement LetStatement = "LET" Ident "=" Expr <-- здесь Ident на позиции Lvalue PrintStatement = "PRINT" Expr Expr = ["+"|"-"] Term { "+" | "-" Term } Term = Factor { "*" | "/" Factor } Factor = Const | Ident | "(" Expr ")" <-- здесь Ident на позиции Rvalue Const = CONST Ident = IDENT <-- в теории в будущем здесь будет ещё и элемент массива Кстати, пока не забыл, если в моё отсутсвие нечем будет заняться, то напишу дальнейшее развитие. Да и чтобы сам потом вспомнил, что хотел.
1
|
Временно недоступен
957 / 228 / 14
Регистрация: 12.04.2009
Сообщений: 926
|
|||||||||||
14.07.2009, 17:51 [ТС] | 58 | ||||||||||
Не могу оставить без внимания один момент. Делаю функцию Variable syntax_parserIdentLValue (); ,находится она должна,понятное дело,в парсере,чтобы поддержать модульность.
Но вот только вписать её туда не получается .Ошибки кросс компиляции. Что я пытаюсь сделать
Код
/home/user/svn/variable_class.h|19|ошибка: поле ‘val’ имеет неполный тип| /home/user/svn/variable_class.h||In member function ‘Value Variable::GetValue() const’:| /home/user/svn/variable_class.h|30|ошибка: тип результата ‘struct Value’ неполный| /home/user/svn/variable_class.h|30|ошибка: нет декларации ‘val’ в этой области видимости| ||=== Build finished: 3 errors, 0 warnings ===|
0
|
Временно недоступен
957 / 228 / 14
Регистрация: 12.04.2009
Сообщений: 926
|
|
14.07.2009, 17:57 [ТС] | 60 |
Вот
0
|
14.07.2009, 17:57 | |
14.07.2009, 17:57 | |
Помогаю со студенческими работами здесь
60
Пишем свой класс, спецификатор доступа protected Интерпретатор небольшого языка программирования на С++ Не удается откомпилировать интерпретатор М-языка Интерпретатор музыки стандарта BASIC PLAY на С++ Написать интерпретатор программного языка -помощь Интерпретатор/компилятор ассемблер-подобного языка Искать еще темы с ответами Или воспользуйтесь поиском по форуму: |