Форум программистов, компьютерный форум, киберфорум
Наши страницы

С++ для начинающих

Войти
Регистрация
Восстановить пароль
 
 
Рейтинг: Рейтинг темы: голосов - 1509, средняя оценка - 4.80
#pragma
Временно недоступен
955 / 226 / 6
Регистрация: 12.04.2009
Сообщений: 921
#1

Пишем свой интерпретатор языка BASIC - C++

20.06.2009, 20:03. Просмотров 197186. Ответов 464
Метки нет (Все метки)

*****************
Благодаря форуму и Evg в частности интерпретатор развивается, потихоньку превращаясь в простенький интерпретатор QBASIC.
Некоторые из самых старых версий сохранились в теме и ссылки на них будут добавлены в это сообщение,а также ссылки на другие темы,связанные с этой.

Репозиторий с проектом находится тут, там же есть возможность в браузере посмотреть историю ревизий (английский в логах весьма примитивен,комментарии и рекомендации можете писать в личку),а также скачать самый последний архив репозитория в формате .tar.gz
Если кто-то пользуется Subversion,скачать исходники можно так:
Код
svn co https://basin.svn.sourceforge.net/svnroot/basin basin
Эти темы возникли в результате моих вопросов по ходу написания:
Технический приём для формирования согласованных данных
Makefile: как с использованием gcc строить автоматические зависимости от .h файлов?
Вопрос по svn (Subversion)
Создание системы тестирования ПО.
Вопрос про разные реализации бэйсиков
Можно ли выразить порядковый номер элемента массива через индексы?
[C++] Какие флаги указать линкеру для компиляции программы?
Как можно определить переменную в файле configure.in,чтобы её можно было использовать в Makefile?
Странный SIGSEGV, или что зависит от порядка написания интерфейса класса
Можно ли как-то указать в Makefile, чтобы часть файлов компилировал компилятор C?
Альтернативная версия интерпретатора от Evg на C
Это простая реализация разбора выражений, написанная Evg на C:
Представление выражения в двоичном дереве
*****************
Первое сообщение:
*****************
Задание(Страуструп,из книги,по готовому коду): Введите программу калькулятора и заставьте её работать.Например,при вводе
C++
1
2
r = 2.5
area = pi*r*r
Программа калькулятора выведет:
C++
1
2
2.5
19.635
Получили такой код:
LexicalAnalyzer.h
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// LexicalAnalyzer.h
#ifndef LEXICALANALYZER_H_INCLUDED
#define LEXICALANALYZER_H_INCLUDED
 
#include <cctype>
#include <string>
#include <map>
#include <iostream>
 
enum Token_value {
    NAME,       NUMBER,      END,
    PLUS = '+', MINUS = '-', MUL = '*', DIV = '/',
    PRINT = ';',ASSIGN = '=',LP = '(',  RP = ')'
};
extern Token_value curr_tok;
extern std::map<std::string,double>table;
extern int no_of_errors;
 
Token_value get_token();
 
double expr(bool);
double term (bool);
double prim (bool);
int error(const std::string&);
 
#endif // LEXICALANALYZER_H_INCLUDED

LexicalAnalyzer.cpp
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
// LexicalAnalyzer.cpp
#include "LexicalAnalyzer.h"
 
 
std::map<std::string,double>table;
Token_value curr_tok=PRINT;
 
double expr (bool get)
{
    double left = term(get);
 
    for (;;)
        switch (curr_tok) {
            case PLUS:
                 left += term(true);
            break;
            case MINUS:
                 left-= term(true);
            break;
            default:
                 return left;
        }
}
 
double term (bool get)
{
    double left = prim (get);
 
    for (;;)
        switch (curr_tok) {
            case MUL:
                 left*=prim(true);
            break;
            case DIV:
                 if (double d = prim (true)) {
                     left /= prim (true);
                     break;
                 }
                 return error("Деление на ноль");
            default:
                 return left;
        }
}
 
double number_value;
std::string string_value;
 
double prim (bool get)
{
    if (get) get_token();
    switch (curr_tok){
        case NUMBER:{
            double& v = number_value;
            get_token();
            return v;
        }
        case NAME:{
            double& v = table[string_value];
            if (get_token()==ASSIGN) v = expr(true);
            return v;
        }
        case MINUS:
            return -prim(true);
        case LP:{
            double e = expr(true);
            if (curr_tok!=RP) return error("Ожидалась )");
            get_token();
            return e;
        }
        default:
            return error("Ожидалось первичное выражение");
    }
}
 
Token_value get_token()
{
    char ch = 0;
 
    do {
        if (!std::cin.get(ch)) return curr_tok = END;
    } while (ch!='\n'&&isspace(ch));
 
    switch (ch) {
        case 0:
             return curr_tok = END;
        case ';':case '\n':
             return curr_tok = PRINT;
        case '*':case'/':case '+':case '-':case '(':case ')':case '=':
             return Token_value(ch);
        case '0':case '1':case '2':case '3':case '4' :
        case '5':case '6':case '7':case '8':case '9':case '.':
             std::cin.putback(ch);
             std::cin>>number_value;
             return curr_tok=NUMBER;
        default:
             if (isalpha(ch)) {
                 string_value = ch;
                 while (std::cin.get(ch)&&isalnum(ch)) string_value.push_back(ch);
                 std::cin.putback(ch);
                 return curr_tok = NAME;
             }
             error ("Неправильная лексема");
             return curr_tok = PRINT;
    }
}
int no_of_errors=0;
int error (const std::string& s)
{
    no_of_errors++;
    std::cerr<<"Ошибка: "<<s<<'\n';
    return no_of_errors;
}

main.cpp
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// main.cpp
#include "LexicalAnalyzer.h"
 
 
int main()
{
    table["pi"]=3.1415926535897932385;
    table["e"]=2.7182818284590452354;
    while (std::cin) {
        get_token();
        if (curr_tok==END) break;
        if (curr_tok==PRINT) continue;
        std::cout<<expr(false)<<'\n';
    }
    return no_of_errors;
}

Анализатор-то работает,но конечное значение не вычисляется.Более того,если вводим
C++
1
a = 3 + 6
,то получаем "a", равное первому элементу в выражении,то есть 3.В чём логическая ошибка данной программы?С этими каскадными вызовами она слегка запутана.Уверен,что кто-то уже делал это задание.

Добавлено через 2 часа 5 минут 30 секунд
Пришлось решать влоб с дебаггером.У Страуструпа опечатка (или намеренная ошибка,что более вероятно ) Вот в этом куске кода в функции get_token():
C++
1
2
        case '*':case'/':case '+':case '-':case '(':case ')':case '=':
             return Token_value(ch);
Нехватает смены значения curr_tok,что и приводит к ошибочной работе.
C++
1
2
        case '*':case'/':case '+':case '-':case '(':case ')':case '=':
             return curr_tok=Token_value(ch);
Теперь всё пашет,всем спасибо,вопрос можно считать закрытым,но есть вопрос поважнее: В функциях prim и term возвращается int при ошибке,но ведь они имеют тип double,как вообще это работает?Происходит неявное преобразование типа,так?Мне интересно,почему Страуструп прибег к такому способу,это распространённая практика?

Добавлено через 16 минут 19 секунд
И ещё опечатка была
C++
1
2
3
                 if (double d = prim (true)) {
                     left /= d;// было left /= prim (true)
                     break;
31
Лучшие ответы (1)
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
Similar
Эксперт
41792 / 34177 / 6122
Регистрация: 12.04.2006
Сообщений: 57,940
20.06.2009, 20:03
Я подобрал для вас темы с готовыми решениями и ответами на вопрос Пишем свой интерпретатор языка BASIC (C++):

Пишем свой чекер - C++
Я хочу написать свой чекер, но не знаю с чего начать? Кто знает основные принцип работы чекеров прошу объясните.

пишем свой троян с нуля - C++
Всем привет)))соглашусь, что изобретаю велосипед, но хочется сделать все своими ручками не прибегая к open source и т.п. для повышения...

Пишем свой класс, спецификатор доступа protected - C++
Всем привет! Из книги Р. Лафоре относительно спецификатора доступа protected: Далее пишется следующее: Возникает вопросы:...

Не удается откомпилировать интерпретатор М-языка - C++
Задача: взять интерпретатор М-языка на сайте http://cmcmsu.no-ip.info/2course/model.lang.parser.sample.htm и переработать его, добавив в...

Интерпретатор небольшого языка программирования на С++ - C++
Здравствуйте, уважаемые форумчане! Я тут где-то год назад прочитал тему Evg и #pragma о создании интерпретатора, меня эта тема очень...

Написать Интерпретатор Программного Языка(собственного) - C++
Здраствуйте! Кто знает C++ помогите пожалуйста с реализацией данного задания!!! Пожалуйста, очень надо. сроки поджимают. Есть...

464
niXman
Эксперт С++
3139 / 1451 / 49
Регистрация: 09.08.2009
Сообщений: 3,441
Записей в блоге: 2
27.10.2010, 01:40 #406
Цитата Сообщение от #pragma Посмотреть сообщение
Я хотел переместить функцию memalloc в класс pool,и нужно именно несколько функций с параметрами,и одну без.. А разве так нельзя сделать?
не очень понял вопроса..
ты об этом?:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include <iostream>
#include <string>
 
template<typename T>
T* func() {
   return new T;
}
 
template<typename T, typename A1>
T* func(const A1& a1) {
   return new T(a1);
}
 
template<typename T, typename A1, typename A2>
T* func(const A1& a1, const A2& a2) {
   return new T(a1, a2);
}
 
int main() {
   std::string* s1 = func<std::string>();
   std::cout << "s1: " << *s1 << std::endl;
 
   std::string* s2 = func<std::string>("bla");
   std::cout << "s2: " << *s2 << std::endl;
 
   std::string* s3 = func<std::string>("blabla", 3);
   std::cout << "s3: " << *s3 << std::endl;
}
http://liveworkspace.org/code/0ad678df74523767f3b97eaaf36be157
3
#pragma
Временно недоступен
955 / 226 / 6
Регистрация: 12.04.2009
Сообщений: 921
27.10.2010, 01:56  [ТС] #407
Да,я это имел в виду
1
Evg
Эксперт CАвтор FAQ
18915 / 6874 / 503
Регистрация: 30.03.2009
Сообщений: 19,354
Записей в блоге: 30
27.10.2010, 13:10 #408
Цитата Сообщение от #pragma Посмотреть сообщение
Поэтому у меня есть пару предложений по этому поводу:
1) Оставить всё как есть,"законсервировать",и можно будет двигаться дальше,получив полезный опыт.
2) Переписать всё заново.
Я в общем-то не раз говорил, что нынешняя твоя реализация и идеи для построения вызовов приведут в тупик. Но объяснять это было бы слишком долго. Надо понять это изнутри. Теперь ты это понял. А потому это является очень ценным опытом. Ибо по себе знаю, что отрицательный опыт гораздо более полезен, чем положительный.

Я предлагаю идти по второму пункту. Потому что это тоже большой полезный опыт. Любая программа по мере своего развития рано или поздно сталкивается с тем, что нужно полностью переделывать отдельные компоненты или полностью менять архитектуру программы. Чем раньше ты научишься это делать, тем проще будет в будующем. К тому же такой опыт поможет тебе при написании программ изначально закладываться на будущую переделку и писать коды таким образом, чтобы они проще поддавались переделке.

Многие коммерческие фирмы идут по первому пункту. Практика показывает, что почти всегда это заканчивается закрытием проекта или банкротством

Цитата Сообщение от #pragma Посмотреть сообщение
Evg ,на самом деле я был бы непрочь взглянуть на твой вариант реализации с функциями (если он есть,конечно),а то я глядел на исходные коды gcc,и просто ничего там не понял,вот бы поглядеть на хорошо написанную программу интерпретатора в самом простом варианте с функциями.
Я не стал делать функции именно по той причине, что понимал масштаб геморроя. Потому я их и не делал Всё, что я сделал - это потому, что была неделя условно свободного времени перед отпуском и неделя условно свободного времени где-то внутри рабочего года. А больше я ничего и не делал с тех пор
2
niXman
Эксперт С++
3139 / 1451 / 49
Регистрация: 09.08.2009
Сообщений: 3,441
Записей в блоге: 2
27.10.2010, 19:29 #409
Цитата Сообщение от #pragma Посмотреть сообщение
Да,я это имел в виду
так мною предложенный вариант подходит?
1
#pragma
Временно недоступен
955 / 226 / 6
Регистрация: 12.04.2009
Сообщений: 921
27.10.2010, 22:51  [ТС] #410
Цитата Сообщение от Evg Посмотреть сообщение
Я предлагаю идти по второму пункту.
Заманчивое предложение,при условии наличия свободного времени. Пока что его маловато, и в перспективе не наблюдается
В-общем,я достаточно отчётливо уяснил важность планирования при написании программ,хотя бы намекающих на дальнейшее развитие. Как развить этот навык,я не знаю,ты говоришь,что это приходит с опытом,а может,это талант особый,-ну дальше видно будет.
В ближайшем будущем у меня будет немного времени,и я хотел бы посвятить его тому,чтобы дочитать Beej's Guide to Network Programming (возможно поэкспериментировать немного,ибо это очень интересная тема),немного про устройство Linux,и,может быть,дочитать уже наконец бедного Страуструпа. А то я чувствую,что в кодинге у меня даже некоторые привычки образоваться успели,а теория как была,так почти и осталась на том же уровне,когда я прекратил её изучать.
Цитата Сообщение от nixMan Посмотреть сообщение
так мною предложенный вариант подходит?
Да,это подходит,я даже наверное попытаюсь поменять,что нужно,чтобы хотя бы прога собиралась чисто,а то неохота так вот оставлять.
1
Evg
Эксперт CАвтор FAQ
18915 / 6874 / 503
Регистрация: 30.03.2009
Сообщений: 19,354
Записей в блоге: 30
27.10.2010, 23:09 #411
Цитата Сообщение от #pragma Посмотреть сообщение
В-общем,я достаточно отчётливо уяснил важность планирования при написании программ
Есть хорошее выражение: плохо спланированная работа требует в три раза больше времени, чем планировалось, а хорошо спланированная - только в два раза больше. С программами, в общем-то, то же самое. Как ты ни планируй, если идёт постоянное развитие, то всё равно что-то придётся переделывать
3
taras atavin
3888 / 1762 / 92
Регистрация: 24.11.2009
Сообщений: 27,565
24.11.2011, 17:18 #412
Мне бы азы теории для совсем чайника. Хотя бы термины. Например, понятие "алфавит" совпадает с обыденным? А если нет, то что это такое? Можете дать для примера готовый алфавит, например, c++?
0
Nameless One
Эксперт С++
5783 / 3432 / 255
Регистрация: 08.02.2010
Сообщений: 7,448
24.11.2011, 20:35 #413
Алфавит в общем случае - это любое конечное множество символов. Т.е. для ЯП алфавит языка - это множество символов, которые могут в том или ином виде встречаться в исходнике (отдельный случай - это комментарии). Пример для С++
Теория (и практика) с самых азов: http://ru.wikipedia.org/wiki/Компиля..._и_инструменты
0
taras atavin
3888 / 1762 / 92
Регистрация: 24.11.2009
Сообщений: 27,565
25.11.2011, 07:00 #414
Эйси. Теперь такой вопрос. Опередлил я алфавит, пытаюсь слепить свою кривоподелуху на тему компиляторов (первая пробу клавы приличным компилом точно не будет). Есть нечто, кторое должно стать кривокомпилятором, а потом может быть и компилятором, есть файл исходника. Решено отталкиваться от c++, а в первом примере нет ничего что вышло бы за рамки c++. Есть функция, есть указатель на имя файла с исходником. Надо ли грузить его целиком в оперативу, или работать с диска? Ещё. Подводные камни байт-кода в роли внутреннего представления. Преимущества и недостатки шитого кода в роли целевого языка. Подводные камни обратной польской нотации в роли внутреннего представления арифметических, строковых, адресных и логических выражений. Строки будут поддерживать только конкатенацию, доуступ к символу и функции, но просто для единобезобразия с остальными выражениями.
грамматика должна включать в себя начальный символ , или аксиому, с которой начинается получение любого предложения языка.
То есть? Что то я не знаю ни одного языка, на которых бы каждое предложение начиналось с фиксированного символа, или последовательности таких символов. Вот взять, например, c++.
C++
1
2
3
4
5
6
7
8
9
int main ()
{
 int f;
 int a;
 int r;
 f=2;
 a=4; r=f+a;
 return r;
}
Пердолжения - это, судя по всему,
C++
1
int main()
,
C++
1
{
,
C++
1
int f;
,
C++
1
int a;
C++
1
int r;
C++
1
f=2;
C++
1
a=4;
C++
1
r=f+a;
C++
1
return r;
,
C++
1
}
. Ну и каким символом начинаются все? Или я чего то не так понял?
0
ValeryLaptev
Эксперт С++
1048 / 827 / 48
Регистрация: 30.04.2011
Сообщений: 1,659
25.11.2011, 14:30 #415
taras atavin, ну хоть немного же теорию надо читать!
Рекомендую 2 книги - простые и доступные:
Вирт: http://www.ozon.ru/context/detail/id/4803779/
Свердлов: http://www.ozon.ru/context/detail/id/3056680/
0
Nameless One
Эксперт С++
5783 / 3432 / 255
Регистрация: 08.02.2010
Сообщений: 7,448
25.11.2011, 15:13 #416
Цитата Сообщение от taras atavin Посмотреть сообщение
грамматика должна включать в себя начальный символ , или аксиому, с которой начинается получение любого предложения языка.
в терминологии контекстно-свободных грамматик существует четыре основных понятия:
  1. терминальные символы (токен) - элементарные символы языка, определяемые грамматикой
  2. нетерминалы (синтаксические переменные), описывающие множество строк терминалов способом, описываемым ниже
  3. продукции, состоящие из нетерминала (заголовок, левая часть продукции) стрелки и последовательности терминалов и/или нетерминалов (тело, правая часть продукции)
  4. один из нетерминальных символов, который указывается как стартовый (начальный)
для примера, если уж так хочется, можно рассмотреть С-подобный язык, который позволяет определять функции. Описание его грамматики может иметь такой вид (терминалы заключены в обратные кавычки, `E` - спец. терминал, который обозначает пустую строку символов, '|' - символ, с помощью которого группируются различные продукции для одного и того же нетерминала, с символа '#' начинается комментарий к самой грамматике):

Код
program -> definitions # программа - эта серия определений (функций)

definitions -> definition definitions # серия определений - это одно определение,
                                      # за кот. следует серия определений
             | `E`                    # либо пустая строка

definition -> prototype body # определение состоит из прототипа функции и ее тела

# прототип функции состоит из спецификатора типа
# возвращаемого значения, идентификатора функции,
# терминального символа `(`, списка параметров,
# терминального символа `)`
prototype -> typespec funid `(` paramlist `)` 

# список параметров состоит либо из пустой строки,
# либо из описания одного параметра, за которым следует
# терминал `,` и список параметров
paramlist -> `E` | param `,` paramlist

# описание параметра состоит из спецификатора типа #
# и идентификатора переменной-параметра
param -> typespec varid

# тело функции состоит из (возможно пустого) списка утверждений,
# заключенный в терминалы `{` и `}`
body -> `{` statements `}`

statements -> `E` | statement statements

# и так далее описываются остальные нетерминалы
Цитата Сообщение от taras atavin Посмотреть сообщение
Ну и каким символом начинаются все? Или я чего то не так понял?
а начинается все с нетерминала program
1
Evg
Эксперт CАвтор FAQ
18915 / 6874 / 503
Регистрация: 30.03.2009
Сообщений: 19,354
Записей в блоге: 30
25.11.2011, 15:26 #417
Nameless One,

> # и так далее описываются остальные нетерминалы

Я так понимаю, что именно это "и так далее" в первую очередь интересовало Тараса (т.е. именно про это он изначально и спросил)
1
Nameless One
Эксперт С++
5783 / 3432 / 255
Регистрация: 08.02.2010
Сообщений: 7,448
25.11.2011, 15:50 #418
Evg, так он же спросил про стартовый нетерминал, нет?
Цитата Сообщение от taras atavin Посмотреть сообщение
То есть? Что то я не знаю ни одного языка, на которых бы каждое предложение начиналось с фиксированного символа, или последовательности таких символов. Вот взять, например, c++.
Цитата Сообщение от taras atavin Посмотреть сообщение
Ну и каким символом начинаются все? Или я чего то не так понял?
А если его интересует "и так далее", то пусть гуглит по запросу "c++ formal grammar"

Добавлено через 40 секунд
Или ты про "готовый алфавит"?
0
Evg
Эксперт CАвтор FAQ
18915 / 6874 / 503
Регистрация: 30.03.2009
Сообщений: 19,354
Записей в блоге: 30
25.11.2011, 16:36 #419
Если брать язык типа простого бэйсика, которий реализовал #pragma, то каждый statement начинается с ключевого слова. С точки зрения разбора это просто. Типа, если слово LET, то разбираем правило statement_let, если слово FOR, то разбираем правило statement_for. А в си это не совсем так. Поэтому он хочет понять, как с этим разбираться
0
Nameless One
Эксперт С++
5783 / 3432 / 255
Регистрация: 08.02.2010
Сообщений: 7,448
25.11.2011, 16:44 #420
Цитата Сообщение от Evg Посмотреть сообщение
Если брать язык типа простого бэйсика, которий реализовал #pragma, то каждый statement начинается с ключевого слова. С точки зрения разбора это просто
с этим я соглашусь. А С++, в том виде, который мы имеем сейчас, очень плохо подходит в качестве языка, на основе которого нужно учиться писать компиляторы/интерпретаторы (ИМХО)
0
25.11.2011, 16:44
MoreAnswers
Эксперт
37091 / 29110 / 5898
Регистрация: 17.06.2006
Сообщений: 43,301
25.11.2011, 16:44
Привет! Вот еще темы с ответами:

Написать интерпретатор программного языка -помощь - C++
Здраствуйте! Ребят, кто хорошо разбирается в C++ помогите пожалуйста с реализацией данного задания или хотя бы подтолкните к решению,...

Интерпретатор/компилятор ассемблер-подобного языка - C++
Привет! Чую, что изобрёл велисипед, даже скорее велопарк, но всё же, поделюсь: Некоторое время назад начал изучать кресты, в целом...

Интерпретатор музыки стандарта BASIC PLAY на С++ - C++
У кого нибудь есть функция или класс, который сможет воспроизводить в С++ напрямую музыкальные строки, записанные в стандарте оператора...

Задание: разработать "Интерпретатор языка". С чего начать? - C++
Здравствуйте, вручили темку на курсовик, ну точнее как вручили, не успел взять то, что хотел - пришлось брать то, что осталось. Плоховато...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
420
Закрытая тема Создать тему
Опции темы

КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2018, vBulletin Solutions, Inc.
Рейтинг@Mail.ru