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

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

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

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

20.06.2009, 20:03. Просмотров 191811. Ответов 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, или что зависит от порядка написания интерфейса класса
[C++]Можно ли как-то указать в 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;
30
Лучшие ответы (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
#pragma
Временно недоступен
952 / 223 / 6
Регистрация: 12.04.2009
Сообщений: 921
13.07.2009, 09:42  [ТС] #46
Цитата Сообщение от Evg Посмотреть сообщение
А в каких случаях тебе нужно создавать переменную со значением?

Добавлено через 22 минуты 36 секунд
syntax_parser.c строки 49-50. Ты пишешь, что создаёшь переменную, но это не правильно. Ты в данный момент парсишь ПРАВУЮ часть присваивания, т.е. там, где нужно читать значения. Т.е. все переменные уже должны быть созданы, а ты в этот момент НЕ создаёшь новую переменую, а достаёшь в таблице уже СУЩЕСТВУЮЩУЮ переменную и берёшь от неё значение. Если переменная не существует, то тут выбираешь как действовать: либо выдавать ошибку, либо считать, что для несуществующеё переменной значение равно нулю. Я бы выбрал первый вариант.


Добавлено через 5 минут 59 секунд
Я понял, почему у тебя получалось создание переменной непосредственно со значением. Но нужно было бы это делать с Value (т.е. распарсили строковое имя переменной, вытащили правую часть в виде Value, создали переменную с Value). Но засада в том, что тут переменную надо НЕ создавать, а записывать в неё (т.е. если переменная уже создана, то новая переменная не нужна)
Да нет же я же написал,что создаю константу,то есть Value ,передаю в неё значение готовой переменной,и сам объект константы возвращаю .Но я понял твою мысль,можно просто сделать Value внутри класса Variable,и просто перегрузить конструктор класса Value,чтобы был и по умолчанию,что позволит создание пустой Value внутри Variable.

Я сделал перегрузку оператора = в классе Variable,так что можно будет просто записать Variable a = syntax_parserExpr();
0
Evg
Эксперт CАвтор FAQ
17944 / 6175 / 411
Регистрация: 30.03.2009
Сообщений: 16,963
Записей в блоге: 27
13.07.2009, 09:44 #47
Тогда ещё раз поясни ситуацию из поста #44. При каких условиях возникает необходимость в таком действии?
1
#pragma
Временно недоступен
952 / 223 / 6
Регистрация: 12.04.2009
Сообщений: 921
13.07.2009, 10:00  [ТС] #48
Уже не возникает,то есть я перегрузил оператор = в классе переменных,а также сделал своё поле значений внутри переменной,и теперь правомерна запись Variable a = syntax_parserPrimary();
Но раз лучше всё-таки сделать членом класса экземпляр Value,то я просто переделаю,перегружу конструктор Value,и всё будет ок.
0
Evg
Эксперт CАвтор FAQ
17944 / 6175 / 411
Регистрация: 30.03.2009
Сообщений: 16,963
Записей в блоге: 27
13.07.2009, 10:07 #49
Цитата Сообщение от #pragma Посмотреть сообщение
Уже не возникает,то есть я перегрузил оператор = в классе переменных,а также сделал своё поле значений внутри переменной,и теперь правомерна запись Variable a = syntax_parserPrimary();
Так в том-то и дело, я это уже объяснял, но особого внимания не заострял. Здесь у тебя "a" - локальный объект в твоей процедуре (в интерпретаторе). Т.е. при выходе из этой процедуры "a" разрушится, и в при разборе следующих конструкций у тебя переменная (которая уже на бэйсике) пропадёт
1
#pragma
Временно недоступен
952 / 223 / 6
Регистрация: 12.04.2009
Сообщений: 921
13.07.2009, 10:10  [ТС] #50
Наверное,ты просто не заметил,все мои переменные будут храниться в контейнере,а он же в динамической памяти,так?
(variable_class.h)
0
Evg
Эксперт CАвтор FAQ
17944 / 6175 / 411
Регистрация: 30.03.2009
Сообщений: 16,963
Записей в блоге: 27
13.07.2009, 11:00 #51
А... или initialized_vars это у тебя глобальная таблица. Т.е. ты сначала создаёшь локальный объект для переменной, а потом его засовываешь в таблицу? Ну можно и так. В этомс случае у тебя получается избыточный код в syntax_parser.cpp в строках 49-64 (я его понять особо не смог). Вместо всего этого паровоза должно стоять что-то типа того, что я обозначал как Var.GetValue(); Потому как в этом месте ты не должен пытаться понять тип переменной. А то добавишь ещё один тип и во все такие места ндо будет втыкать дополнительный код, а так дополнитеельный код будет только внутри модуля value

Кстати, по поводу отдельного модуля для типов. В теории так и надо делать, но реально у тебя тип используется только в модуле value. Да и если выносить, то кроме enum'а пока вынести больше нечего

Добавлено через 38 секунд
Цитата Сообщение от #pragma Посмотреть сообщение
Наверное,ты просто не заметил,все мои переменные будут храниться в контейнере,а он же в динамической памяти,так?
(variable_class.h)
Я не сразу это понял. Просто я в других терминах мыслю, а потому с ходу не догнал

Добавлено через 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
#pragma
Временно недоступен
952 / 223 / 6
Регистрация: 12.04.2009
Сообщений: 921
13.07.2009, 19:26  [ТС] #52
У меня тоже эта опция стоит,но предупреждений нет.Странно,почему?... В принципе можно использовать static_cast (я их пока путаю,какой из них какими особенностями обладает).
0
Evg
Эксперт CАвтор FAQ
17944 / 6175 / 411
Регистрация: 30.03.2009
Сообщений: 16,963
Записей в блоге: 27
13.07.2009, 21:08 #53
dynamic_cast имеет смысл только тогда, когда есть виртуальное наследование коассов, во всех осатльных случаях можно static_cast. У меня есть предупреждения, а у теюя нет вероятно потому, что у нас версии gcc разные. Мне вот наоборот кажется, что это мой gcc не по делу warning выдаёт
0
#pragma
Временно недоступен
952 / 223 / 6
Регистрация: 12.04.2009
Сообщений: 921
13.07.2009, 21:23  [ТС] #54
Сделаю со static_cast.Версия "gcc версия 4.3.3 ".У тебя более ранняя?
P.S. Я установил svn - по ней целая книга есть,довольно сложная система управления.Пока мне удалось просто на одном диске слелать хранилище,создать рабочую копию.Но пока не понятно,как обновляется хранилище? Там читать и читать...
0
Evg
Эксперт CАвтор FAQ
17944 / 6175 / 411
Регистрация: 30.03.2009
Сообщений: 16,963
Записей в блоге: 27
13.07.2009, 22:04 #55
у меня где-то 4.1.3
Про svn - я всё по online документации разбирался. Правда у меня был опыт работы с cvs, так что там проще всё пошло. Если ссылку найду - кину. Если что непонятно - спрашивай, но создай лучше отдельную тему тут http://www.cyberforum.ru/linux/
Там и другие помогут, а в эту тему врядли кроме нас с тобой смотрит

Добавлено через 3 минуты 25 секунд
Если я ничего не путаю, то читал я здесь http://svnbook.red-bean.com/
Далее тыкаешь в "Открыть многостраничную HTML версию книги". Что там хорошо, чуть ниже версия на английском языке. Перевод вроде нормальный, но временами бывает, что пока оригинал не увидишь - не поймёшь
1
#pragma
Временно недоступен
952 / 223 / 6
Регистрация: 12.04.2009
Сообщений: 921
14.07.2009, 02:57  [ТС] #56
Кажется,до меня дошло,что ты хотел мне донести.В функции syntax_parserPrimary()
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
            case TOKEN_IDENT: {
 
                int i = FindNameOverVector (::parser_CurTokenStr);
 
                if (i >= 0)
                {
                    // Если находим переменную,создаём константу с нужным
                    // значением,и возвращаем его.
                    Value v = initialized_vars.at(i).GetValue();
                    return v;
                }
                else
                {
                    // Если не находим,то создаём константу,                      
                    // приравниваем её к выражению,создаём переменную,
                    // передаём данные туда и возвращаем константу. 
                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
                   // Вместо того,что выше,видимо,должна быть ошибка инициализации.       
                }
            }
Я так понимаю,на данном этапе парсинга вообще не должно быть варианта else,а должна быть ошибка неинициализированной переменной. Потому что если мы встретим внутри выражения неинициализированную переменную,будет попытка её создания. Так же мы не можем знать,была ли эта находка новой переменной после ключевого слова LET или просто посередине строки.Поэтому,нужно,чтобы у stmnt_kw_LET() была своя ветка рекурсии. Я правильно понял?

Добавлено через 1 час 13 минут 50 секунд
P.S.Да,насчёт сообщения #47, я как-то пропустил,прочитал просто,но сейчас вник и согласен,лучше разделить всё это на функции (я про syntax_IdentLValue (void),syntax_IdentRValue (void) и syntax_Ident() ) ,то есть лучше следовать принципу "разделяй и властвуй",так понятнее код.Я это учту и переделаю.
0
Evg
Эксперт CАвтор FAQ
17944 / 6175 / 411
Регистрация: 30.03.2009
Сообщений: 16,963
Записей в блоге: 27
14.07.2009, 09:49 #57
Цитата Сообщение от #pragma Посмотреть сообщение
Я так понимаю,на данном этапе парсинга вообще не должно быть варианта else,а должна быть ошибка неинициализированной переменной. Потому что если мы встретим внутри выражения неинициализированную переменную,будет попытка её создания.
Именно так

Цитата Сообщение от #pragma Посмотреть сообщение
Так же мы не можем знать,была ли эта находка новой переменной после ключевого слова LET или просто посередине строки.Поэтому,нужно,чтобы у stmnt_kw_LET() была своя ветка рекурсии. Я правильно понял?
Судя по всему, ты уже понял про концепции rvalue и lvalue, так что ответ на этот вопрос тебе вроде бы уже понятен. Где-то я уже писал, что на каждый statement по хорошему должна быть своя процедура. Да и вообще. ка каждое синтаксическое правило нужна своя процедура. Ибо так будет проще, особенно если имя процедуры содержит в себе название правила, а в исходнике где-то в головной части или в отдельном текстовом файле ты аккуратно выпишешь все лексические и синтаксические правила, а в комментариях будешь ссылаться на них

Цитата Сообщение от #pragma Посмотреть сообщение
P.S.Да,насчёт сообщения #47, я как-то пропустил,прочитал просто,но сейчас вник и согласен,лучше разделить всё это на функции (я про syntax_IdentLValue (void),syntax_IdentRValue (void) и syntax_Ident() ) ,то есть лучше следовать принципу "разделяй и властвуй",так понятнее код.Я это учту и переделаю.
Потому я тебе и не навязываю свои варианты, а лишь выкладываю "к размышлению". После того, как сделаешь неправильно, только тогда на уровне ощущений начнёшь понимать, как делать правильно. Именно поэтому говорю, что не надо бояться переделывать код, ибо это единственный способ научиться делать правильно

Добавлено через 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  <-- в теории в будущем здесь будет ещё и элемент массива
Добавлено через 10 минут 53 секунды
Кстати, пока не забыл, если в моё отсутсвие нечем будет заняться, то напишу дальнейшее развитие. Да и чтобы сам потом вспомнил, что хотел.
  1. Ввести строковые константы
    Лексика: ConstString = <знак "> { Any symbols } <знак ">
    Синтаксис не меняется - всё это попадает под правило CONST

    Итого получается, что у тебя появляется ещё один тип констант. Т.е. в теории модификации следующие:
    • поддерживаешь в парсере
    • в синтаксисе правка минимальная - только в правиле Const добавть ещё проверку на TOKEN_CONST и всё
    • поддерживаешь операции над строковыми константами. При этом можно работать в таком варианте:
      "abc" + "def" = "abcdef"
      "abc" + 1 = "abc1"
      "abc" + 5.5 = "abc5.5"
      1 + "abc" - запрещённая операция
      5.5 + "abc" - запрещённая операция
      все остальные арифметические операции над строками запрещены
  2. Добавляем операцию INPUT. Синтаксис:
    Код
    InputStatement = INPUT Ident <-- Здесь Ident в позиции Lvalue
  3. Подумай над тем, как реализовывать условное исполнение. Синтаксис:
    Код
    IfStatement = IF RelationExpr THEN Statemen [ ELSE Statement ]
1
#pragma
Временно недоступен
952 / 223 / 6
Регистрация: 12.04.2009
Сообщений: 921
14.07.2009, 17:51  [ТС] #58
Не могу оставить без внимания один момент. Делаю функцию Variable syntax_parserIdentLValue (); ,находится она должна,понятное дело,в парсере,чтобы поддержать модульность.
Но вот только вписать её туда не получается .Ошибки кросс компиляции.
Что я пытаюсь сделать
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
#ifndef SYNTAX_PARSER_H_INCLUDED
#define SYNTAX_PARSER_H_INCLUDED
 
 
 
   #include "value_class.h"
   class Value;
   #include "variable_class.h"  // вот тут я пытаюсь сделать её доступной.
   class Variable;                 
 
 
 
  /** @brief Function creates object
   *  of specific type,that got from
   *  parser_GetToken(),also used string
   *  value parser_CurTokenStr,or return
   *  initialized variable value.
   *  @return Object of Value class,
   *  implemented in value_class.h    */
   extern Value syntax_parserPrimary ();
 
   extern Value syntax_parserTerm ();
 
   extern Value syntax_parserExpr ();
 
  /** @brief Only for inside expression parsing.
   *  Will find variable in container.
   *  In case of variable not found
   *  error will be generated.
   *  @return Return variable's value,
   *  that found over the container */
   extern Value syntax_parserIdentRValue ();
 
   extern Variable syntax_parserIdentLValue (); // вот она
 
#endif // SYNTAX_PARSER_H_INCLUDED
И получаю такое
Код
/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 ===|
Ошибку указывает в интерфейсе класса Variable.
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
#ifndef VARIABLE_CLASS_H_INCLUDED
#define VARIABLE_CLASS_H_INCLUDED
 
#include <string>
   using std::string;
#include <vector>
   using std::vector;
#include <map>
   using std::map;
#include <algorithm>
   // find()
 
#include "value_class.h"
 
   class Variable
   {
       private:
 
              Value val;
 
              string var_name;
 
       public:
              Variable ();
              Variable (const string);
             ~Variable (){};
 
              string GetVarName () const {return var_name;};
 
              Value GetValue () const {return val;};
 
 
              Value operator = (const Value& val);
 
   };
 
   extern vector<class Variable>initialized_vars;
 
   // Find position of variable if it was initialized before
   /// @return On success,return position of variable in vector,
   /// on failure return -1
   extern int FindNameOverVector (const string str);
 
#endif // VARIABLE_CLASS_H_INCLUDED
Как тут быть? Как разрешаются подобные конфликты? Можно,конечно,поместить эту функцию в файлы класса Variable,но мне не по душе такая мешанина.
0
Evg
Эксперт CАвтор FAQ
17944 / 6175 / 411
Регистрация: 30.03.2009
Сообщений: 16,963
Записей в блоге: 27
14.07.2009, 17:54 #59
Такое ощущение, что в value_class.h ты не описал класс Value
Или кинь все исходники в архиве, а то так на воздухе что-то непонятно. Дома попробоую скомпилить
1
#pragma
Временно недоступен
952 / 223 / 6
Регистрация: 12.04.2009
Сообщений: 921
14.07.2009, 17:57  [ТС] #60
Вот
0
Вложения
Тип файла: rar interpreter.rar (5.8 Кб, 104 просмотров)
MoreAnswers
Эксперт
37091 / 29110 / 5898
Регистрация: 17.06.2006
Сообщений: 43,301
14.07.2009, 17:57
Привет! Вот еще темы с ответами:

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

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

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

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


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

Или воспользуйтесь поиском по форуму:
60
Yandex
Объявления
14.07.2009, 17:57
Закрытая тема Создать тему
Опции темы

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