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

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

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

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

20.06.2009, 20:03. Просмотров 189600. Ответов 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;
Лучшие ответы (1)
Similar
Эксперт
41792 / 34177 / 6122
Регистрация: 12.04.2006
Сообщений: 57,940
20.06.2009, 20:03     Пишем свой интерпретатор языка BASIC
Посмотрите здесь:
Пишем свой чекер C++
C++ пишем свой троян с нуля
Пишем свой класс, спецификатор доступа protected C++
C++ Интерпретатор небольшого языка программирования на С++
Не удается откомпилировать интерпретатор М-языка C++
Написать Интерпретатор Программного Языка(собственного) C++
Интерпретатор/компилятор ассемблер-подобного языка C++
После регистрации реклама в сообщениях будет скрыта и будут доступны все возможности форума.
Evg
Эксперт CАвтор FAQ
17537 / 5775 / 370
Регистрация: 30.03.2009
Сообщений: 15,904
Записей в блоге: 26
07.07.2009, 08:54     Пишем свой интерпретатор языка BASIC #31
Не совсем понятно, зачем тут шаблон, если процедура у тебя будет присутсвовать в одном экземпляре. Попробуй всё-таки пояснить, а я возможно укажу тебе ошибку в твоих рассуждениях

Ну а коли его сделал, то шаблон в качестве параметра имеет тип. У тебя тут типа нет никакого среди параметров, а потому компилятору непонятно, как выбирать функцию. Для void функции по идее надо так: "syntax_parserPrimary<int>();" Ну или тип нужный указываешь

Добавлено через 2 минуты 31 секунду
Код функции при первом просмотре не смотрел, а сейчас явно вижу ошибку. Ты почему-то считаешь, что функция у тебя может возвращать и int и double - такого быть не может Не говоря уж о том, что по ветке default у тебя ничего не возвращается

По поводу синтаксического нисходящего разбора - всё-таки поковыряйся вот тута Представление выражения в двоичном дереве В 3-м посте исходник, в 5-м комментарии к нему. Читать именно в части синтаксического разбора, грамматический там сделан излишне навороченно, тот вариант, который у тебя, он более удобен

Добавлено через 2 минуты 15 секунд
Я так понимаю, что ты добрался до того, как же аккуратно поддерживать константы разных типов? Если так, то могу рассказать вкратце
#pragma
Временно недоступен
952 / 223 / 6
Регистрация: 12.04.2009
Сообщений: 921
07.07.2009, 23:47  [ТС]     Пишем свой интерпретатор языка BASIC #32
Я думал так: когда у нас константа,нужно вернуть её значение,но ведь тип возвращаемого не ясен с самого начала.Я надеялся это можно решить с помощью шаблонов,но,похоже ошибся.
Тут я вижу 2 выхода из ситуации(трудно абстрактно мыслить на много шагов вперёд ):
1)Сами функции ничего не возвращают,но изменяют переменные в объекте класса,который и требуется разработать.Возможно придётся идти на какие-то хитрости,вроде того,какая переменная изменилась при предыдущем вызове- int или double)
2)Для каждой переменной типа,будь то int или double,есть своя функция,которая с этим типом работает.То есть типизация по сути добавит кучу экземпляров функций,и получится что-то вроде древовидного ветвления,где будут вызываться нужные функции согласно типу.
3) Первый вариант мне не нравится из-за опасности запутаться в бесконтрольном изменении переменных,второй тоже запутан и сложен.Есть какой-то третий вариант?Я твои исходники специально не стал смотреть пока,хочется скорее совета,навроде заданного курса,чтобы потом не заниматься мазохизмом (по крайней мере не слишком много),переписывая и переписывая.
Evg
Эксперт CАвтор FAQ
17537 / 5775 / 370
Регистрация: 30.03.2009
Сообщений: 15,904
Записей в блоге: 26
08.07.2009, 11:32     Пишем свой интерпретатор языка BASIC #33
На самом деле тебе нужно разработать две вещи:

1. Класс, описывающий константу произвольного типа. На каждую константу (возникшую в процессе интерпретации) создаётся новый экземпляр данного класса. Ну и в идеале удаляются ненужные экземпляры, но с оптимальным потреблением памяти я бы пока не стал заморачиваться. Потом это будет проще понять. Кстати, в проект рядом с исходниками положи текстовый файл, и записывай туда все идеи, которые отложены на "потом".

2. Некую таблицу, которая описывает пары "переменная-константа", где записаны текущие значения переменных

По поводу мазохизма. В каких-то местах я намеренно веду тебя по тому пути, что придётся потом переделывать код в деталях (потому что ты с ходу не видишь будущих проблем, а я проблемы вижу только в теории, что получится - пока не знаю). Это тебя научит изначально писать код так, чтобы он легко подвергался переделкам в случае необходимости. В учебниках пишут только правильный итоговый код, поэтому по учебникам ты этому не научишься. Только набив шишки на практике. Поэтому не надо бояться ошибиться. Не ошибается тот, кто ничего не делает.

Что касается представления класса коснтант, то я бы сделал примерно вот так:

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
class MyConst
{
  private:
    bool is_float; // плавающая или целая
    union
    {
      long long ival; // здесь храним целое значение, если константа целая
      double fval; //  здесь храним плавающее значение, если константа плавающая
    } val;
 
  public:
    // Констркуторы MyConst(long long), MyConst(double), конструктор копирования.
    // При этом конструктор по умолчанию должен вызывать ошибку, ибо понятие
    // константы без значения смысла не имеет
    // Перегруженные операторы плюс, минус и т.д.
    // как конкретно разруливать ошибочные ситуации (деление на ноль, битовые
    // операции над плавающими числами и т.п.) - решать тебе, самый простой
    // метод - через исключение. Можно вместо операторов реализовывать функции
    // plus, minus и т.п. - в этом случае можно возвращать код ошибки, а не
    // работать через исключения. Это ты решай сам, как тебе удобнее и что
    // понятнее.
    //
    // Сложение, например, целого и плавающего, можно реализовать двояко:
    // - преобразование второго операнда к типу первого делать внутри
    //   перегруженных операторов (или функций plus, minus...)
    // - операции разрешить только над константами одного типа, а преобразоване
    //   типов делать в процессе синтаксического разбора
    // Первый вариант хорош своей простотой использования класса - во время
    // синтаксического разбора надо будет меньше делать телодвижений
    // (соответсвенно, интерпретатор будет работать более надёжно).
    // Второй вариант концептуально более правильный, тбо это правило (второй
    // операнд должен приводиться к типу первого) - это свойство языка, а не
    // свойство констант.
    // 
    // Если бы делал я, я бы пошёл по второму варианту, т.к. я делаю код на Си,
    // а работу с типами сделал бы в виде дополнительной прокладки между
    // синтаксическим анализатором и реализацией констант. С точки зрения
    // концепции Си++ это, видимо, означает создание класс-наследника
    // и перегрузку операторов или что-то ещё, в итоге получится тот случай,
    // когда объектный код усложнит проблему, чем её упростит. Да и поскольку
    // ты пока только учишься, я бы тебе рекомендовал первый вариант
    //
    // Соотвественно, в случае выбора первого варианта, появляется дополнительный
    // метод для преобразования типа константы. Теоретически он должен быть
    // private, поскольку это внутреннее действо, но практически может понадобиться
    // использовать его снаружи
    //
    // Отладочный код печати константы (печатает тип и значение, возможно что-то ещё,
    // что появится в процессе работы
    // Пользовательский код печати константы (грубо говоря, его будешь дёргать
    // в процессе работы оператора PRINT)
}
В итоге сейчас у тебя есть независимый модуль parser, который режет входной текст на токены. Теперь у тебя появится независимый модуль работы с константами (который так же независимо от всего можно отлаживать). Потом появится модуль таблицы переменных, который зависит только от модуля констант (и опять-таки его можно будет отлаживать независимо от парсера и синтаксического анализатора). В итоге программа должна быть как конструктор: состоять из отдельных кубиков, внутреннюю реализацию кубиков можно отлаживать независимо друг от друга, а взаимодействовать между собой кубики должны посредством простых интерфейсов

Надеюсь, что смысл донеёс

Добавлено через 1 минуту 3 секунды
Если непотяна какая-то часть именно в плане технической реализации - лучше спрашивай сейчас

Добавлено через 7 минут 13 секунд
Кстати, а на чём ты пишешь? Просто все твои примеры с ходу идут у меня на gcc. При этом тесты исходников явно записаны под виндами (там перевод строки не такой, как в линухе). Т.е. у тебя какая-то gcc-образная среда из разряда Dev-C++, mingw или всё-таки в стандартных билдерах ухитряешься так аккуратно написать?

Добавлено через 9 минут 6 секунд
Да, вот ещё. В MyConst надо сдеать конструкторы, которые строят константу по её строковому представлению. Т.е. синтаксический анализатор имеет токен, содержащий константу, он просто дёргает соотвествующий конструктор из MyConst и не заморачивается там, чтобы самому выгребать значение из строкового представления. Для полноты картины в этой части должны быть два конструктора: создание целого значения по строковому представлению, создание плавающего значения по строковму представлению. Для полноты картины теоретически можно добавить просто "создание значения по строковму представлению", а тип будет разбираться внутри, но это означает, что фактически туда засовывается небольшой кусочек парсера, что не есть хорошо. Но в какойто другой программе (в которую ты, к примеру, в будущем затащишь свой модуль констант), это может пригодиться

Добавлено через 1 час 40 минут 24 секунды
Кстати, если исходить из простоты, то в случае деления на 0 и прочих ошибок можно сразу вызывать функцию, выдающую пользовательскую ошибку и завершать работу. В этом случае модуль констант будет не абсолютно независимым, а у него будет зависимость от модуля ошибок, что не страшно
#pragma
Временно недоступен
952 / 223 / 6
Регистрация: 12.04.2009
Сообщений: 921
08.07.2009, 14:12  [ТС]     Пишем свой интерпретатор языка BASIC #34
Вроде теперь образуется некая структура,просто до этого не мог представить в теории реализацию.Есть некоторые вопросы.Для начала,примерно так я представляю будущую структуру:
1)Значит,будет класс,описывающий произвольный тип переменной.

2)Сами функции рекурсивного спуска будут возвращать объект класса,а все ошибочные ситуации обрабатываются внутри самого объекта.

3)Конструктор,я полагаю,будет принимать готовую строку,плюс логическое значение(зависит от TOKEN_CONST_***),(или сам токен),которое передаётся в переменную is_float,которое,в свою очередь,влияет на работу конструктора.Но в принципе можно сделать 3 конструктора просто для удобства.


Поскольку ты написал,что не стоит заморачиваться с памятью,я так понимаю,что во-первых,каждый объект будет создаваться в динамической памяти,но не понимаю,зачем это нужно,разве не достаточно локальных копий во время рекурсии(?),во-вторых,я думаю,там будет какой-то автоматический сборщик мусора,а как это реализовать,я понятия не имею(пока),я просто немного знаком с темой классов и немного с темой динамической памяти.

Зачем будет нужен конструктор-копировщик,пока для меня тоже загадка.

Пишу я в среде Code::Blocks,компилятор gcc,ты не ошибся,но правда пишу код в Linux,уж не знаю,что там не так с переводом строки.Но также стараюсь не использовать специфических вызовов типа как в винде system(***) ,код по идее должен скомпилироваться и в билдере на винде,но точно не знаю,я просто пользуюсь только Линуксом
Evg
Эксперт CАвтор FAQ
17537 / 5775 / 370
Регистрация: 30.03.2009
Сообщений: 15,904
Записей в блоге: 26
08.07.2009, 14:39     Пишем свой интерпретатор языка BASIC #35
Цитата Сообщение от #pragma Посмотреть сообщение
1)Значит,будет класс,описывающий произвольный тип переменной.
Не тип переменной, а значение константы.

С переменными в этой части можно не заморачиваться и считать, что тип переменной есть текущее значение типа константы. Т.е. записали в переменную целое число, значит в этот момент переменная целая, записали плавающее - значит плавающая. Т.е. тип переменной по сути дела отсутсвует, существует только тип текущего значения. Либо считать, что при первой записи определяется тип переменной, а при дальнейших записях значение всегда будет сводиться к этоу типу. Либо явно в языке ввести тип переменной. На том бейсике, на котором я сто лет назад работыл было так, что A% - целая, A! - плавающая одинарной точности (float), A - плавающая двойной точности (double), A$ - строковая. Ты сам выбирай поведение, которое кажется тебе логичным, но первый вариант (когда тип переменной определяется по записанному в него значению), вроде бы наиболее прост в реализации и начать можно с него. А потом переделать (тоже полезный опыт по переделыванию)

Цитата Сообщение от #pragma Посмотреть сообщение
2)Сами функции рекурсивного спуска будут возвращать объект класса,а все ошибочные ситуации обрабатываются внутри самого объекта.
Грубо говоря да (только "экземпляр класса", а не "объект класса"). Но только в той части синтаксического разбора, где идёт вычисление выражения. Понятно, что по результату разбора Statement'а ничего этого не нужно

Цитата Сообщение от #pragma Посмотреть сообщение
3)Конструктор,я полагаю,будет принимать готовую строку,плюс логическое значение(зависит от TOKEN_CONST_***),(или сам токен),которое передаётся в переменную is_float,которое,в свою очередь,влияет на работу конструктора.Но в принципе можно сделать 3 конструктора просто для удобства.
Ога, только вариант "или сам токен" не годится, ибо модуль констант не должен зависеть от каких-либо enum'ов парсера. Т.е. два параметра: строка и bool (означающий плавающее или целочисленное). Вообще концептуально честно вместо bool ввести enum { CONST_VAL_INT, CONST_VAL_FLOAT }, а затем может придётся добавить туда ещё и строковые константы, могут понадобиться и булевские константы. Т.е. вариант с enum'ом выглядит более логично для дальнейшего развития

Цитата Сообщение от #pragma Посмотреть сообщение
Поскольку ты написал,что не стоит заморачиваться с памятью,я так понимаю,что во-первых,каждый объект будет создаваться в динамической памяти,но не понимаю,зачем это нужно,разве не достаточно локальных копий во время рекурсии(?)
Правильно, нужно создавать именно локалы. У меня почти отсутсвует практика работы на Си++, а потому мне интуитивно кажется, что в процессе перегрузки операторов создаются новые объекты. Хотя вроде бы должно быть без этого

Цитата Сообщение от #pragma Посмотреть сообщение
во-вторых,я думаю,там будет какой-то автоматический сборщик мусора,а как это реализовать,я понятия не имею(пока),я просто немного знаком с темой классов и немного с темой динамической памяти.
Всё будет создаваться в локальных объектах, так что удаляться будут автоматически

Цитата Сообщение от #pragma Посмотреть сообщение
Зачем будет нужен конструктор-копировщик,пока для меня тоже загадка.
Опять-таки следствие моей работы на Си, а не Си++, может он и не нужен (т.е. компилятор сам его построит правильно). Смысл примерно такой. Экземпляры класса MyConst будут рожаться только при непосредственном вызове конструктора, при построении операций и возврате из процедуры. Чтобы не оставлись просто висячие локальные объекты, в которые ничего не записано (ибо мы будем иметь константу без значения). Хотя можно ввести дополнительный признак, проинциализирована константа значением или нет. Всё это мудодейство нужно лишь с одной целью - внутренний контроль при написании интерпретатора. Чем больше контроля, тем быстрее будут находиться ошибки

Цитата Сообщение от #pragma Посмотреть сообщение
правда пишу код в Linux
Отлично. Значит с командной строкой ты более-менее дружишь. Значит проще будет создать систему тестирования (ну это в будущем, когда хоть что-то более-менее заработает нормально). Да и установить svn в этом случае не помешало бы, но настроитьего именно в варианте работы с сетью (обращаться через 127.0.0.1) и под отдельным пользователем - чтобы случайно репозиторий не грохнуть
#pragma
Временно недоступен
952 / 223 / 6
Регистрация: 12.04.2009
Сообщений: 921
09.07.2009, 23:47  [ТС]     Пишем свой интерпретатор языка BASIC #36
Я тут подумал,не лучше ли будет для переменных организовать свой класс,в который,в свою очередь,будет содержать в себе экземпляр класса констант?То есть,когда потребуется выполнить
PureBasic
1
LET a = 1
То просто будет создаваться экземпляр класса переменных,а в конструктор констант будет передаваться строка,и вся ответственность за типизацию,инициализацию и прочее уже ляжет на реализацию класса.Единственная возможная проблема мне видится с видимостью переменных,что данный объект не будет видно из функций,вызываемых далее,а если создавать какой-то глобальный экземпляр,то встаёт вопрос сколько их нужно...Может тут namespace как-то спасёт?
Еще небольшая мелочь в оформлении-проектировании: В syntax_parser.h у меня записано:
syntax_parser.h
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#ifndef SYNTAX_PARSER_H_INCLUDED
#define SYNTAX_PARSER_H_INCLUDED
 
 
 
   // Contain all types of
   // constants,that constructor
   // will get in create of
   // class object.
   enum Types {
       CONST_VAL_INT,   // Тип константы,передаваемый
       CONST_VAL_FLOAT  // конструктору класса
   };
 
   extern Types VarType;
   
   #include "const_class.h"
   class ConstVar;
 
   extern ConstVar syntax_parserPrimary();
 
#endif // SYNTAX_PARSER_H_INCLUDED

То есть я делаю тобой предложенный enum частью синтаксического анализатора(ну не в класс констант же его включать). Мне не нравится,что пришлось так неэстетично записать включение "const_class.h" прямо посередине header-а,как-то не смотрится,да и запись class ConstVar; тоже не к месту.Это всё потому,что enum Types используется в классе ConstVar,который и подключается с помощью "const_class.h".Как бы так сделать поэлегантнее,чтобы в то же время сохранить некий порядок и структуру разделения? Модульное программирование для меня пока не совсем ясно,вот такие ньюансы немного сбиваю с толку.На всякий случай вот код(я надеюсь компилить его не нужно):
syntax_parser.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
#include <cstdlib>
#include <fstream>
#include <iostream>
#include "syntax_parser.h"
#include "parser.h"
#include "debugger.h"
 
//
    using std::ofstream;
    using std::cout;
    using std::endl;
 
    Types VarType;
 
 
    ConstVar syntax_parserPrimary()
    {
        switch (parser_GetToken ())
        {
            case TOKEN_CONST_INT: {
 
                ConstVar int_x(::parser_CurTokenStr,CONST_VAL_INT);
 
                return int_x;
            }
            case TOKEN_CONST_FLOAT: {
 
                ConstVar float_x(::parser_CurTokenStr,CONST_VAL_FLOAT);
 
                return float_x;
            }
            case TOKEN_IDENT: {
                
                ;
            }
            default: ;
                //Error
        }
    }

const_class.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
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
#ifndef CONST_CLASS_H_INCLUDED
#define CONST_CLASS_H_INCLUDED
 
 
#include "syntax_parser.h"
#include "parser.h"
#include <string>
#include <iostream>
 
 
 
 
   class ConstVar
   {
       private:
              // is_float will be used for
              // setting type of the constant
              // 0 means integer constant
              bool is_float; // плавающая или целая
 
              // "Pure optimisation"
              // Depends of is_float
              union
              {
                  long ival; // здесь храним целое значение, если константа целая
                  double fval; //  здесь храним плавающее значение, если константа плавающая
              } constant;
 
       public:
            /** @brief Costructor receive string from parser
             *  and turn it into variable.The type of variable
             *  depends on VarType value(syntax_parser.h).*/
             ConstVar(const std::string parser_CurToken,Types VarType);
 
            /** Standart destructor */
            ~ConstVar(){};
 
            /** Operators overloading */
             ConstVar operator +  (const ConstVar& prev);
             ConstVar operator -  (const ConstVar& prev);
             ConstVar operator *  (const ConstVar& prev);
             ConstVar operator /  (const ConstVar& prev);
             ConstVar operator =  (const ConstVar& prev);
 
             bool Is_Float() const {return is_float;};
             long Get_ival() const {return constant.ival;};
             double Get_fval() const {return constant.fval;};
   };
 
 
   std::ostream& operator << (std::ostream& os,const ConstVar& curr);
 
#endif // CONST_CLASS_H_INCLUDED

const_class.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
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
#include "const_class.h"
#include "syntax_parser.h"
#include <iostream>
#include <cstdlib>
#include <ostream>
 
//
 
   using std::string;
   using std::cout;
 
   ConstVar::ConstVar(const std::string parser_CurToken, Types VarType)
   {
       switch (VarType)
       {
           case CONST_VAL_INT: {
 
               is_float = 0;
 
               constant.ival = atol(parser_CurToken.c_str());
 
           break;
           }
           case CONST_VAL_FLOAT: {
 
               is_float = 1;
 
               constant.fval = atof(parser_CurToken.c_str());
 
           break;
           }
           default:; // Error;
       }
   }
 
 
   ConstVar ConstVar::operator = (const ConstVar& prev)
   {
       if (is_float == 1&&is_float == prev.Is_Float())
       {
           constant.fval = prev.Get_fval();
           return *this;
       }
       else
         if (is_float == 0&&is_float == prev.Is_Float())
         {
             constant.ival = prev.Get_ival();
             return *this;
         }
         else ;
            // Types messing Error?
         return *this;
   }
 
 
   ConstVar ConstVar::operator + (const ConstVar& prev)
   {
       if (is_float == 1&&is_float == prev.Is_Float())
       {
           constant.fval += prev.Get_fval();
           return *this;
       }
       else
         if (is_float == 0&&is_float == prev.Is_Float())
         {
             constant.ival += prev.Get_ival();
             return *this;
         }
         else ;
            // Types messing Error?
         return *this;
   }
 
 
   ConstVar ConstVar::operator - (const ConstVar& prev)
   {
       if (is_float == 1&&is_float == prev.Is_Float())
       {
           constant.fval += prev.Get_fval();
           return *this;
       }
       else
         if (is_float == 0&&is_float == prev.Is_Float())
         {
             constant.ival -= prev.Get_ival();
             return *this;
         }
         else ;
            // Types messing Error?
         return *this;
   }
 
 
   ConstVar ConstVar::operator * (const ConstVar& prev)
   {
       if (is_float == 1&&is_float == prev.Is_Float())
       {
           constant.fval *= prev.Get_fval();
           return *this;
       }
       else
         if (is_float == 0&&is_float == prev.Is_Float())
         {
             constant.ival *= prev.Get_ival();
             return *this;
         }
         else ;
            // Types messing Error?
         return *this;
   }
 
 
   ConstVar ConstVar::operator / (const ConstVar& prev)
   {
       if (is_float == 1&&is_float == prev.Is_Float())
       {
           constant.fval /= prev.Get_fval();
           return *this;
       }
       else
         if (is_float == 0&&is_float == prev.Is_Float())
         {
             constant.ival /= prev.Get_ival();
             return *this;
         }
         else ;
            // Types messing Error?
         return *this;
   }
 
 
   std::ostream& operator << (std::ostream& output_stream,const ConstVar& curr)
   {
       switch (curr.Is_Float())
       {
           case 1:
 
                output_stream << curr.Get_fval();
                return cout;
           break;
           case 0:
 
                output_stream << curr.Get_ival();
                return cout;
           break;
           default:;
                //Something
           return cout;
       }
   }


Добавлено через 29 минут 46 секунд
P.S. Сейчас увидел только: я по ходу ошибся в перегрузке операторов,возвращать нужно было this а не *this,так? Просто я на лету делал,пришлось попутно читать про перегрузку ,union и т.д.
Evg
Эксперт CАвтор FAQ
17537 / 5775 / 370
Регистрация: 30.03.2009
Сообщений: 15,904
Записей в блоге: 26
10.07.2009, 14:28     Пишем свой интерпретатор языка BASIC #37
Цитата Сообщение от #pragma Посмотреть сообщение
Я тут подумал,не лучше ли будет для переменных организовать свой класс,в который,в свою очередь,будет содержать в себе экземпляр класса констант?
Именно так. Только я это условно называю "таблицей переменных". Хотя ты пока пишешь простой интерпретатор (а не полноценный бэйсик). Я бы пока глубоко с этим не заморачивался и понятие "переменная" наверх не вытаскивал. Сделал бы модуль, а к нему интерфейсы: "записать значение в переменную с такми-то именем", "прочитать значение из переменной с таким-то именем", "проверить, существует ли переменная с таким-то именем". А реализацию поначалу сделал бы простую (типа того, что у тебя уже было), потом при необходимости можно было бы усложнить

Цитата Сообщение от #pragma Посмотреть сообщение
То есть,когда потребуется выполнить
PureBasic
1
LET a = 1
То просто будет создаваться экземпляр класса переменных,а в конструктор констант будет передаваться строка,и вся ответственность за типизацию,инициализацию и прочее уже ляжет на реализацию класса
Что-то слабо понял, о чём ты говоришь. А какую строку передавать в случае "LET a=b+c"? Просто в большой программе всё должно быть функционально отделено. Создание константы - отдельно. Создание переменной, инициализированной константой - отдельно. Создавать переменную через строку, и типа внутри автоматически дёрнется создание константы - это идеологически неправильно, потому как при таком подходе ты свою программу оченб быстро превратишь в помойку, в которой сложно будет разобраться. Не даром я тебе приводил сравнение с кубиками. Когда у тебя есть много маленьких кубиков, ты из них можешь собрать всё что угодно, когда та сразу же начинаешь склеивать кубики в другие конструкции, то из них уже тяжело что-то собрать. И есть хорошее программерское наблюдение: чем более универсальным делается интерфейс, тем меньше остаётся мест, где его удаётся применить".

Цитата Сообщение от #pragma Посмотреть сообщение
Единственная возможная проблема мне видится с видимостью переменных,что данный объект не будет видно из функций,вызываемых далее,а если создавать какой-то глобальный экземпляр,то встаёт вопрос сколько их нужно...Может тут namespace как-то спасёт?
Опять-таки, я бы сразу не стал так далеко заглядывать. Просто по той причине, что ты ещё толком не имеешь понятия о том, как будет выглядеть весь интерпретатор. Я уже несколько раз говорил идею, что для начала бы надо сделать, чтобы хоть как-то заработал вариант с выражениями и PRINT'ами. Т.е на текущий момент ты имеешь такую сложность, что тебе надо реализовывать сразу три вещи: константы, переменные, синтаксический анализатор. При этом ты пока не имеешь чёткого представления о том, что будет, а поэтому у тебя возникает масса затруднений на предмет того, а как же конкретно всё реализовывать.

Программистов условно можно разделить на два типа. Первые как правило сначала всё долго и тщательно всё планируют, а потом реализовывают. Вторые сначала в голове имеют общую картину, сразу же начинают реализовывать наброски, по ходу дела постоянно что-то корректируя, и в процессе работы начинает вырисовываться чёткая картина. Программисты первого типа как правило страдают тем, что их код очень трудно поддаётся дальнейшей переделке, а времени на раздумья уходит слишком много, потому как в процессе работы появляется куча ньюансов, которые теоретически сложно предугадать. Программисты второго типа изначально пишут код с учётом дальнейших переделок, а потому код оказывается гибким. Я отношу себя ко второму типу. Если тебе ближе первый тип, то мне надо будет немного по другому вести "обучение", ибо я исхожу из того, что при необходимости мы всегда можем подточить и подпилить, а затем аккуратно переделать.

То есть я делаю тобой предложенный enum частью синтаксического анализатора(ну не в класс констант же его включать). Мне не нравится,что пришлось так неэстетично записать включение "const_class.h" прямо посередине header-а,как-то не смотрится,да и запись class ConstVar; тоже не к месту.Это всё потому,что enum Types используется в классе ConstVar,который и подключается с помощью "const_class.h".Как бы так сделать поэлегантнее,чтобы в то же время сохранить некий порядок и структуру разделения? Модульное программирование для меня пока не совсем ясно,вот такие ньюансы немного сбиваю с толку.
Опять, я не понял чётко постановку вопроса, но есть реально две разные вещи: TOKEN_CONST_INT, TOKEN_CONST_FLOAT - это один enum, относящийся к лексическому анализатору (парсеру). Есть другой enum: CONST_VAL_INT, CONST_VAL_FLOAT, относящийся к представлению констант. Это две совершенно разные вещи (поэтому я их и выделяю префиксами TOKEN_ и CONST_ чтобы не путать). Или вопрос в чём-то в другом? То что ты сразу же задаёшься вопросами правильного распределения по муодям - это хорошо, но по опыту знаю, что всё равно идеально не получится Правильное ощущение выработается со временем, а здесь опять та же проблема - ты пока ещё не понимаешь, как в и тоге всё получится В общем поэтому я и говорю, что не надо бояться эксприментировать, ошибаться и переделывать, ибо пока на своём опыте не увидишь, как делается "неправильно", то никогда на уровне ощущений не поймёшь, как делать "правильно"

Добавлено через 13 минут 16 секунд
Немного поглядел исходники - у тебя с терминологией что-то в кучу намешано
  • Value (сокращённо val) - это непосредственное числовое значение: 5, 10, 12.345
  • Constant (сокращённо const) - это некое абстрактное понятие, которое содержит в себе значение (Value), но при этом содержит дополнительные атрибуты, описывающие тип значения. В моём понятии Value я не выделяю в отдельный класс, я выделяю лишь класс Constant, который содержит поле value
  • Variable (сокращённо var) - это переменная, имеющая имя, чтобы по этому имени можно было обращаться. Перменная в каждый момент времени содержит в себе какое-то значение, т.е. переменная внутри себя содержит поле класса Constant

Т.е. что-то типа того

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
// ------------------ это интерфейс модуля констант
 
// Тип значения (константы) - этот enum строго относится только к модулю констант
enum ConstantType
{
  CONST_TYPE_NULL = 0,  // ввожу это поле, чтобы "полезные" значения были ненулевыми
  CONST_TYPE_INT,
  CONST_TYPE_FLOAT,
};
 
// Типы для хранения целой и плавающей константы
typedef long long const_Int_t;
typedef double const_Float_t;;
 
// Класс описывает значение, как абстрактную единицу. Над экземплярами класса
// можено осуществлять арифметические операции. Конкретное значение
// и его тип являются полями класса
class Constant
{
  private:
    // Эти два поля описывают значение (value) константы
    ConstantType type;
    union
    {
      const_Int_t ival;
      const_Float_t fval;
    } value;
 
  public:
    // С конструкторами я, возможно, тебе нагнал, ибо опять-таки плохо Си++ знаю
 
    // Конструктор для создания целой константы
    Constant (const_Int_t ival);
 
    // Конструктор для создания плавающей константы
    Constant (const_Float_t fval);
 
    // Конструктор для создания константы по строковому представлению
    // Внутри синтаксического анализатора ты будешь пользоваться именно этим конструктором
    Constant (string str, ConstantType type);
 
...
}
 
// ------------------ это интерфейс модуля переменных
 
// Класс, описывающий именованный объект (т.е. переменную), который
// ХРАНИТ значение. Т.е. в переменную можно только прочитать или записать
// значение. Операций над переменными мы НЕ делаем, операции делаются
// ТОЛЬКО НАД ЗНАЧЕНИЯМИ (константами)
//
// Таким образом для конструкции a=b+1 надор атомарных действий такой
// 1. Прочитать значение переменной b (т.е. получить константу, хрянащуюся в b)
// 2. Создать константу, значение которой в строковой форме представлено как "1"
// 3. Выполнить операцию "+" над константами из пунктов 1 и 2
// 4. Записать константу, полученную в пнкте 3 в качестве текущего значения
//    переменной a
class Variable
{
  private:
    string name;
    Constant value; // текущее значение переменной
...
}
Возможно опять что-то сумбурно, ты справшивай если что непонятно. Но старайся вопрос задать так, чтобы его можно было понять

Добавлено через 18 минут 27 секунд
По поводу твоего кода

C++
1
ConstVar::ConstVar(const std::string parser_CurToken, Types VarType)
не создавай параметр с именем parser_CurToken, ибо это какая-то обязаловка получается. Модуль у тебя независимый, парсер тут не при чём, поэтому пусть имя параметра будет независимым (str или что-то типа того)

Операция сложения должна выглядеть так (по отношению именно к твоему коду):

C
1
2
3
4
5
6
7
8
9
10
11
12
if (is_float && prev.IsFloat())
  // f + f
  constant.fval += prev.GetFVal();
else if (is_float && prev.IsInt())
  // f + i
  constant.fval += prev.GetIVal();
else if (!is_float && prev.IsFloat())
  // i + f
  constant.ival += prev.GetFVal();
else
  // i + i
  constant.ival += prev.GetIVal();
Т.е. при сложении значений разных типов тип результата совпадает с типом первого аргумента

Кстати, когда есть ситуация, которая кажется тебе ошибочной (ты отметил её "Types messing Error?"), сразу влепи туда вызов своей процедуры error, а то потом забудешь и долго будешь отлаживать, программу

Добавлено через 7 минут 4 секунды
Класс, который я назвал Constant можно назвать Value, чтобы тебе меньше было путанницы. Тогда у тебя остаётся только два понятия: Value и Variable, а понятия Constant как такового не будет

Добавлено через 2 минуты 50 секунд
syntax_parserPrimary написан правильно (в итоге он получается предельно примитивным)

Добавлено через 2 часа 2 минуты 40 секунд
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
ConstVar syntax_parserPrimary()
...
            case TOKEN_IDENT: {
                // По имени переменной узнаём Variable. При этом понимаем,
                // что переменные у нас живут всё время работы интерпретатора и
                // как с константами на локальных переменных тут не получится
                Variable *variable = .....
 
                // Возвращаем значение, записанное в переменной
                return variable->getValue();
            }
...
            default: ;
                // Выводим синтаксическую ошибку. gcc пишет что-то типа "syntax error near <TokenStr>"
...
Добавлено через 2 часа 3 минуты 22 секунды
Да, по поводу видимости переменных. Попробую условно объяснить на пальцах, как это в компиляторах делается (чтобы ты имел представление о том, как тебе гибко написать код). Заводится некое понятие "таблица переменных", которая содержит в себе список переменных (или массив - не суть). Для глобальных объектов заводится одна таблица. Обрабатываем процедуру, заводим вторую таблицу и локалы процедуры затаскаваем в неё. Обрабатываем лексический блок (фигурные скобки), заводим третью таблицу и локалы лексического блока пишем уже туда. таким образом тыблицы у тебя образуют что-то типа стека. Ну и далее когда встречается использование переменной, то ищес в таблице, которая на вершине стека, если не нашли - то дальше и т.п.

Тебе пока предлагаю ограничиться лишь одними глобмальными переменными. При этом ты заводишь понятие таблицы переменных, но таблица у тебя будет одна. А потом в случае чего добавишь
#pragma
Временно недоступен
952 / 223 / 6
Регистрация: 12.04.2009
Сообщений: 921
10.07.2009, 20:54  [ТС]     Пишем свой интерпретатор языка BASIC #38
Сейчас читаю про неявные преобразования.
Есть одна неувязка.При смешении типов,где гарантия,что это не приведет к потере данных и их искажению? То есть,если мы плюсуем 4 (long) и число,заведомо большее,чем границы диапазона long,то что произойдёт с этими данными? Я считаю,тут нужно пересмотреть смешение типов,или сделать какой-то алгоритм,проверяющий выход за границы,exeption,или приводить тип long к doube,но не наоборот. Ведь такая проблема может возникнуть?
P.S. Но в принципе я сам уже ответил на свой вопрос,что все исключения добавляю потом,как всё заработает,так что зря написал,наверное.Просто первой мыслью было запретить смешение типов,вот и всполошился,но уже вижу,что просто можно добавить исключения потом.
Evg
Эксперт CАвтор FAQ
17537 / 5775 / 370
Регистрация: 30.03.2009
Сообщений: 15,904
Записей в блоге: 26
11.07.2009, 00:25     Пишем свой интерпретатор языка BASIC #39
Сечас мы делаем ровно то, что делается в языках Си\Си++. Т.е.

C
1
2
3
long a, b;
double c;
a = b + c;
трактуется как

C
1
a = b + (long)c;
По стандарту Си в данном случае если значение "c" выходит за рамки диапазона long'а, то значение "(long)c" неопределено (т.е. стандарт ничего не говорит, что должно быть в этом случае и ситуация по сути дела является некорректной).

Ну и действительно, ты сам всегда в момент вычисления результат операции можешь сам всё проверить и запрограммировать так, как душе угодно. И всё это будет внутри модуля констант. Ну а пока наша задача сделать синтаксический разбор и добраться-таки до print'а.

Добавлено через 2 минуты 27 секунд
Т.е. мне кажется, что когда мы это допилим, а потом, допустим, сделаем if'ы, то тебе сразу многие вещи станут гораздо понятнее, после того, как ты их реализуешь. В том числе и вещи, которые мы ещё только будем делать потом, но у тебя скорее всего должно появиться понимание, как оно будет сделано. Так это или нет - скоро, думаю, узнаем
#pragma
Временно недоступен
952 / 223 / 6
Регистрация: 12.04.2009
Сообщений: 921
12.07.2009, 02:33  [ТС]     Пишем свой интерпретатор языка BASIC #40

Не по теме:

перед этим сообщением было сообщение-вопрос,и оно было удалено модератором


В моём коде нет функции __tmain,или ты что-то поменял,или среда поменяла за тебя сама.Возможно,функция main в проектах на VS должна называться tmain,но точно не знаю.
Evg
Эксперт CАвтор FAQ
17537 / 5775 / 370
Регистрация: 30.03.2009
Сообщений: 15,904
Записей в блоге: 26
12.07.2009, 23:11     Пишем свой интерпретатор языка BASIC #41
По поводу вот этой темы Разыменование итератора
Я бы не стал в переменной хранить непосредственно значение (без дополнительного класса Value). Это чисто к сведению. Если тебе видится, что так будет лучше - реализовывай именно так. Из вариантов "блее правильный" и "более понятный" в данном случае уместен "более понятный"
#pragma
Временно недоступен
952 / 223 / 6
Регистрация: 12.04.2009
Сообщений: 921
12.07.2009, 23:49  [ТС]     Пишем свой интерпретатор языка BASIC #42
Хм,я объясню,почему выбрал этот вариант. Сначала я делал именно с Value как членом класса,но потом выяснилось,что необходим вызов конструктора Value,то есть требовалась запись вроде такой
C++
1
Variable a(paser_CurTokenStr,CONST_INT)
Чтобы сделать возможной вызов конструктора Value,причём получается что нужно,чтобы в paser_CurTokenStr было не имя переменной,а строка для константы. Я не нашёл другого способа,кроме как перегрузить оператор = для Variable. А как тебе видится решение?
#pragma
Временно недоступен
952 / 223 / 6
Регистрация: 12.04.2009
Сообщений: 921
13.07.2009, 01:36  [ТС]     Пишем свой интерпретатор языка BASIC #43
Хочу поделиться дальнейшим ходом написания.Некоторые рассуждения написаны в комментариях.
Ещё подумалось,что лучше бы сделать отдельный модуль types.h,и вырезать туда все перечисления,мне кажется,так будет правильней.Промежуточный результат такой пока:
driver.h
C++
1
2
3
4
5
6
7
8
9
#ifndef DRIVER_H_INCLUDED
#define DRIVER_H_INCLUDED
 
#include <fstream>
 
 
extern std::ifstream source;
 
#endif // DRIVER_H_INCLUDED

driver.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
#include <iostream>
#include "driver.h"
#include "debugger.h"
#include "parser.h"
#include "syntax_parser.h"
#include "value_class.h"
#include "variable_class.h"
 
 
   using std::ifstream;
   using std::cout;
   using std::endl;
 
   std::ifstream source;
 
   int main()
   {
       parser_Init("source.bsc");
       if (DEBUG == 1) debugger_Start();
 
       //Value x("123",CONST_VAL_INT);
 
       //Value y("2",CONST_VAL_INT);
 
       //x = x*y;
 
       //cout << x;
       Variable a("a");
       initialized_vars.push_back(a);
       return 0;
   }

debugger.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
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
#ifndef DEBUGGER_H_INCLUDED
#define DEBUGGER_H_INCLUDED
 
// This switch is to enable
// program global debugging
// mode.1 is enabled and 0
// is disabled.
#define DEBUG 1
 
// Enable and disable of
// debugging print of
// different components
// of the program
#define PARSER_DEBUG 1
#define SYNTAX_PARSER_DEBUG 1
#define VALUE_CLASS_DEBUG 1
 
// When SILENT is 1 printing
// to a file,when 0 - to console.
#define SILENT 1
 
// Printing of full variables names
// 1 - enabled.
#define FULL_NAMES 1
 
// Print on commented and empty lines
// 1 - enabled.
#define PRINT_COMMENTED_LINES 1
#define PRINT_EMPTY_LINES     1
 
#include <fstream>
 
   // Each value of this enum
   // represent function which
   // debugging process is
   // working in.
   enum debug_CurFunc {
       PARSER_INIT,            // parser_Init (const std::string file_name)
       PARSER_GET_TOKEN,       // parser_TokenType parser_GetToken ()
       SYNTAX_PARSER_PRIMARY  // Value syntax_parserPrimary();
   };
 
  /** @brief Clearing the debugging log. */
   extern void debugger_Start();
 
  /** @brief Global printing of variables.
   *  @param debug_CurFunc is name of the function,
   *  that debugger is working with. */
   extern void debugger_Print(debug_CurFunc);
 
   extern std::ofstream debug_data_file;
   extern debug_CurFunc debug_CurFuncName;
 
#endif // DEBUGGER_H_INCLUDED

debugger.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
#include "debugger.h"
#include "parser.h"
#include "value_class.h"
 
//
 
   using std::ofstream;
 
 
   ofstream debug_data_file;
 
 
   void debugger_Start()
   {
       if (SILENT == 0)
             ;
       else
       {
           debug_data_file.open("data_file.dat");
           debug_data_file.clear();
           debug_data_file.close();
       }
   }
 
   void debugger_Print(debug_CurFunc debug_CurFuncName)
   {
           switch (debug_CurFuncName)
           {
                case PARSER_INIT: {
 
                    if (SILENT == 0)
                        parser_PrintCurToken ();
 
                    else
                    {
                        debug_data_file.open("data_file.dat",ofstream::app);
 
                        if (debug_data_file.is_open())
 
                            parser_PrintCurToken (&debug_data_file);
 
                        debug_data_file.close();
                    }
                }
                break;
                case PARSER_GET_TOKEN: {
 
                    if (SILENT == 0)
                        parser_PrintCurToken ();
 
                    else
                    {
 
                       debug_data_file.open("data_file.dat",ofstream::app);
 
                       if (debug_data_file.is_open())
 
                           parser_PrintCurToken (&debug_data_file);
 
                       debug_data_file.close();
                    }
                }
                break;
                case SYNTAX_PARSER_PRIMARY: {
                    if (SILENT == 0);
                }
                default: //Error
                ;
          }
   }

parser.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
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
#ifndef PARSER_H_INCLUDED
#define PARSER_H_INCLUDED
 
#include <string>
#include <map>
 
   enum parser_TokenType
   {
     TOKEN_NULL = 0,
 
     TOKEN_CONST_INT,   // Integer constant
     TOKEN_CONST_FLOAT, // Floating point constant
 
     TOKEN_IDENT,       // Identificator
 
     TOKEN_KW_LET,      // Keyword LET
     TOKEN_KW_PRINT,    // Keyword PRINT
 
     TOKEN_DELIM_EQUAL, // Sign "="
     TOKEN_DELIM_PLUS,  // Sign "+"
     TOKEN_DELIM_MINUS, // Sign "-"
 
     TOKEN_EOL,         // End of line
     TOKEN_EOF,         // End of file
 
     TOKEN_LAST
   };
 
   const std::string parser_TokenTypeNames[] =
   {
     "TOKEN_NULL    ",
 
     "TOKEN_CONST_INT",       // Integer constant
     "TOKEN_CONST_FLOAT",     // Floating point constant
 
     "TOKEN_IDENT    ",           // Identificator
 
     "TOKEN_KW_LET    ",          // Keyword LET
     "TOKEN_KW_PRINT",        // Keyword PRINT
 
     "TOKEN_DELIM_EQUAL",     // Sign "="
     "TOKEN_DELIM_PLUS",      // Sign "+"
     "TOKEN_DELIM_MINUS",     // Sign "-"
 
     "TOKEN_EOL    ",         // End of line
     "TOKEN_EOF    ",         // End of file
 
     "TOKEN_LAST"
   };
 
  /** @brief Parser initialization.
   *  @param file_name contains path
   *  to file with source code,wich parser is working with.
   *  @return In case of problem with lile opening return false,
   *  and true if file was opened */
   extern bool parser_Init (std::string file_name);
 
  /** @brief Procedure parser_GetToken get's the next token from
   *  input stream.
   *  @param As a result writing variables parser_CurToken and
   *  parser_LastTokenStr.
   *  @return Procedure parser_GetToken return value of
   *  parser_CurToken. */
   extern parser_TokenType parser_GetToken ();
 
  /** @var Variables parser_CurFile, parser_CurLine contain
   *  information about place of current token in source
   *  code for debug purposes,parser_CurToken is the current
   *  token in line.parser_CurTokenStr contains value of
   *  token,if available. */
   extern std::string parser_CurFile;
   extern unsigned parser_CurLine;
   extern parser_TokenType parser_CurToken;
   extern parser_TokenType parser_NextToken;
   extern std::string parser_CurTokenStr;
 
  /** @var parser_ResWords contain reserved words
   *  for parser.Initialization is in
   *  parser_Init (std::string file_name) */
   extern std::map<const std::string,int>parser_ResWord;
   extern std::map<const char,int>parser_ResSign;
 
  /** @brief Printer for debugging purposes
   *  of current token state.
   *  @param parser_CurFile is name of the
   *  file,that is being parced. */
   extern void parser_PrintCurToken (std::ofstream *);
   extern void parser_PrintCurToken ();
 
  /** @brief End of parser's work */
   extern void parser_Finish ();
 
#endif // PARSER_H_INCLUDED

parser.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
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
#include <iostream>
#include <cctype>
#include "parser.h"
#include "debugger.h"
#include "driver.h"
 
//
 
   using std::ofstream;
   using std::endl;
   using std::map;
   using std::string;
   using std::cout;
 
   parser_TokenType parser_CurToken;
   parser_TokenType parser_NextToken;
   string parser_CurTokenStr;
   string parser_CurFile;
   unsigned parser_CurLine;
 
   map<const string,int>parser_ResWord;
   map<const char,int>parser_ResSign;
 
   bool parser_Init (const std::string file_name)
   {
       parser_ResWord[ "LET" ] = parser_TokenType(TOKEN_KW_LET);
       parser_ResWord["PRINT"] = parser_TokenType(TOKEN_KW_PRINT);
 
       parser_ResSign[  ' '  ] = parser_TokenType(TOKEN_NULL);
       parser_ResSign[  '='  ] = parser_TokenType(TOKEN_DELIM_EQUAL);
       parser_ResSign[  '+'  ] = parser_TokenType(TOKEN_DELIM_PLUS);
       parser_ResSign[  '-'  ] = parser_TokenType(TOKEN_DELIM_MINUS);
       parser_ResSign[  ';'  ] = parser_TokenType(TOKEN_EOL);
       parser_ResSign[  '\n' ] = parser_TokenType(TOKEN_EOL);
 
       source.open(file_name.c_str());
 
       if (source.is_open())
       {
           ::parser_CurFile  = file_name;
           ::parser_CurLine  = 1;
           ::parser_CurToken = TOKEN_NULL;
           ::parser_NextToken = TOKEN_NULL;
           ::parser_CurTokenStr = "";
 
           return true;
       }
       else
           return false;
   }
 
 
   parser_TokenType parser_GetToken ()
   {
       char c = 0;
       ::parser_CurTokenStr = "";
 
       //if ((PARSER_DEBUG == 1)&&(DEBUG == 1)) debugger_Print(PARSER_GET_TOKEN);
 
       while (!source.eof())
       {
           source.get(c);
 
           // Identificator or
           // reserved word
           if (isalpha(c))
           {
               while (!source.eof()&&isalpha(c))
               {
                    ::parser_CurTokenStr.push_back(c);
                    source.get(c);
               }
 
               map<const char,int>::iterator i = parser_ResSign.find(c);
 
               if (i!=parser_ResSign.end())
                   ::parser_NextToken = parser_TokenType(parser_ResSign[c]);
 
               else
                   ;//Error
 
               map<const string,int>::iterator ii = parser_ResWord.find(::parser_CurTokenStr);
 
               if (ii!=parser_ResWord.end())
                   ::parser_CurToken = parser_TokenType(parser_ResWord[::parser_CurTokenStr]);
 
               else
                   ::parser_CurToken = TOKEN_IDENT;
 
               if (::parser_NextToken == TOKEN_EOL) ++::parser_CurLine;
 
               if ((PARSER_DEBUG == 1)&&(DEBUG == 1)) debugger_Print(PARSER_GET_TOKEN);
 
               return ::parser_CurToken;
           }
           else
 
           // Number
           if (isdigit(c))
           {
               while (!source.eof()&&isdigit(c))
               {
                    ::parser_CurTokenStr.push_back(c);
                    source.get(c);
               }
               // Float
               if (c == '.')
               {
                    ::parser_CurTokenStr.push_back(c);
                    source.get(c);
 
                    if (isdigit(c))
                        while (!source.eof()&&isdigit(c))
                        {
                             ::parser_CurTokenStr.push_back(c);
                             source.get(c);
                        }
                    else
                       ;//Error
 
                    map<const char,int>::iterator i = parser_ResSign.find(c);
 
                    if (i!=parser_ResSign.end())
                        ::parser_NextToken = parser_TokenType(parser_ResSign[c]);
                    else
                       ;//Error
 
                    ::parser_CurToken = TOKEN_CONST_FLOAT;
 
                    if (::parser_NextToken == TOKEN_EOL) ++::parser_CurLine;
 
                    if ((PARSER_DEBUG == 1)&&(DEBUG == 1)) debugger_Print(PARSER_GET_TOKEN);
 
                    return ::parser_CurToken;
               }
 
               // Integer
               map<const char,int>::iterator i = parser_ResSign.find(c);
 
               if (i!=parser_ResSign.end())
                    ::parser_NextToken = parser_TokenType(parser_ResSign[c]);
 
               else
                   ;//Error
 
               ::parser_CurToken = TOKEN_CONST_INT;
 
               if (::parser_NextToken == TOKEN_EOL) ++::parser_CurLine;
 
               if ((PARSER_DEBUG == 1)&&(DEBUG == 1)) debugger_Print(PARSER_GET_TOKEN);
 
               return ::parser_CurToken;
           }
           else
 
           // !"#$%&'()*+,-./:;<=>?@[\]^_`{|}~
           if (ispunct(c))
           {
               map<const char,int>::iterator i = parser_ResSign.find(c);
 
               if (i!=parser_ResSign.end())
               {
                   ::parser_CurToken = parser_TokenType(parser_ResSign[c]);
 
                   if ((PARSER_DEBUG == 1)&&(DEBUG == 1)) debugger_Print(PARSER_GET_TOKEN);
 
                   return ::parser_CurToken;
               }
               else
 
               // Comment
               if (c == '#')
               {
                   while (!source.eof()&&c!='\n') source.get(c);
 
                   if (c == '\n')
                   {
                       ::parser_CurToken = TOKEN_EOL;
                       ::parser_NextToken = TOKEN_NULL;
                       ++::parser_CurLine;
                   }
                   else
                   {
                       ::parser_CurToken = TOKEN_EOF;
                       ::parser_NextToken = TOKEN_NULL;
 
                   }
 
                   // When on commented line
                   // we don't want to return
                   // from function
                   if ((PARSER_DEBUG == 1)&&
                       (DEBUG == 1)&&
                       (PRINT_COMMENTED_LINES == 1)) debugger_Print(PARSER_GET_TOKEN);
 
                   continue;
               }
 
               // ...
           }
 
           else
 
           // ' ' and '\n'
           if (isspace(c))
           {
               if (c == ' ') continue;
               else
               if (c == '\n')
               {
                   ::parser_CurToken = TOKEN_EOL;
                   ::parser_NextToken = TOKEN_NULL;
                   ++::parser_CurLine;
               }
               if ((PARSER_DEBUG == 1)&&
                   (DEBUG == 1)&&
                   (PRINT_EMPTY_LINES ==1)) debugger_Print(PARSER_GET_TOKEN);
 
               continue;
           }
 
           else ;//Error
       }
 
       ::parser_CurToken = TOKEN_EOF;
       ::parser_NextToken = TOKEN_NULL;
 
       if ((PARSER_DEBUG == 1)&&(DEBUG == 1)) debugger_Print(PARSER_GET_TOKEN);
 
       return ::parser_CurToken;
   }
 
 
   void parser_PrintCurToken (std::ofstream *)
   {
       switch (FULL_NAMES)
       {
           case 0: {
 
                debug_data_file << "File: "        << ::parser_CurFile     << ' '
                                << "Line: "        << ::parser_CurLine     << ' '
                                << "CurToken: "    << ::parser_CurToken    << ' '
                                << "NextToken: "   << ::parser_NextToken   << ' '
                                << "TokenStr: "    << ::parser_CurTokenStr << ' '
                                << endl;
           }
           break;
           case 1: {
 
                debug_data_file << "File: "        <<                                          ::parser_CurFile     << ' '
                                << "Line: "        <<                                          ::parser_CurLine     << '\t'
                                << "CurToken: "    << parser_TokenTypeNames[parser_TokenType(::parser_CurToken)]    << '\t'
                                << "NextToken: "   << parser_TokenTypeNames[parser_TokenType(::parser_NextToken)]   << '\t'
                                << "TokenStr: "    <<                                          ::parser_CurTokenStr << ' '
                                << endl;
           }
           break;
           default:  //Some error will be here
               debug_data_file << "Check value of FULL_NAMES in debugger.h";
       }
   }
 
   void parser_PrintCurToken ()
   {
       switch (FULL_NAMES)
       {
           case 0: {
 
                cout << "File: "        << ::parser_CurFile     << ' '
                     << "Line: "        << ::parser_CurLine     << ' '
                     << "CurToken: "    << ::parser_CurToken    << ' '
                     << "NextToken: "   << ::parser_NextToken   << ' '
                     << "TokenStr: "    << ::parser_CurTokenStr << ' '
                     << endl;
           }
           break;
           case 1: {
 
                cout << "File: "        <<                                          ::parser_CurFile     << ' '
                     << "Line: "        <<                                          ::parser_CurLine     << '\t'
                     << "CurToken: "    << parser_TokenTypeNames[parser_TokenType(::parser_CurToken)]    << '\t'
                     << "NextToken: "   << parser_TokenTypeNames[parser_TokenType(::parser_NextToken)]   << '\t'
                     << "TokenStr: "    <<                                          ::parser_CurTokenStr << ' '
                     << endl;
           }
           break;
           default:  //Some error will be here
                cout << "Check value of FULL_NAMES in debugger.h";
       }
   }
Вложения
Тип файла: rar Interpreter.rar (6.2 Кб, 136 просмотров)
#pragma
Временно недоступен
952 / 223 / 6
Регистрация: 12.04.2009
Сообщений: 921
13.07.2009, 01:37  [ТС]     Пишем свой интерпретатор языка BASIC #44
Всё сообщение уже не влазит в одно. А есть сервисы такие,что можно тексты вставлять туда?

value_class.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
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
#ifndef CONST_CLASS_H_INCLUDED
#define CONST_CLASS_H_INCLUDED
 
 
#include "syntax_parser.h"
#include "parser.h"
#include <string>
#include <iostream>
 
 
 
   // Contain all types of
   // constants,that constructor
   // will get in create of
   // class object.
   enum Types {
       CONST_VAL_INT,   // Тип константы,передаваемый
       CONST_VAL_FLOAT  // конструктору класса
   };
 
   // For debug printing only
   const std::string value_CurType   [] =
   {
       "CONST_VAL_INT  ",   // Тип константы,передаваемый
       "CONST_VAL_FLOAT"  // конструктору класса
   };
 
   // For debug printing only.
   // Indicates current action,
   // being performed over the object
   enum Action {
       ACT_CONSTRUCTOR,
       ACT_PLUS,
       ACT_MINUS,
       ACT_MULTIPLY,
       ACT_DIVIDE,
       ACT_ASSIGN
   };
 
   // For debug printing only.
   // Indicates current action,
   // being performed over the object
   const std::string value_classCurActionName [] =
   {
       "ACT_CONSTRUCTOR",
       "ACT_PLUS       ",
       "ACT_MINUS      ",
       "ACT_MULTIPLY   ",
       "ACT_DIVIDE     ",
       "ACT_ASSIGN     "
   };
 
 
   class Value
   {
       private:
              // value_TypeName will be used for
              // setting type of the constant
              Types value_type; // Тип переменной
 
              // "Pure optimisation"
              // Depends of is_float
              union
              {
                  long ival; // здесь храним целое значение, если константа целая
                  double fval; //  здесь храним плавающее значение, если константа плавающая
              } value;
 
       public:
            /** @brief Costructor receive string from parser
             *  and turn it into variable.The type of variable
             *  depends on VarType value(syntax_parser.h).*/
             Value(const std::string str,Types ValType);
 
            /** Standart destructor */
            ~Value(){};
 
            /** Operators overloading */
             Value operator +  (const Value& prev);
             Value operator -  (const Value& prev);
             Value operator *  (const Value& prev);
             Value operator /  (const Value& prev);
             Value operator =  (const Value& prev);
 
             long   Get_ival() const {return value.ival;};
             double Get_fval() const {return value.fval;};
 
            /** @brief Debugging print.void value_classDbgPrint ()
             *  printing to std::cout,or to output file
             *  stream (debug_data_file in debugger.h)  */
             void value_classDbgPrint ();
 
             Types GetTypeName () const {return value_type;};
   };
 
 
 
   extern Action value_classCurAction;
 
 
   extern std::ostream& operator << (std::ostream& os,const Value& curr);
 
 
#endif // CONST_CLASS_H_INCLUDED

value_class.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
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
#include "value_class.h"
#include "syntax_parser.h"
#include "debugger.h"
#include <iostream>
#include <cstdlib>
#include <fstream>
 
 
//
 
   using std::ofstream;
   using std::string;
   using std::cout;
   using std::endl;
 
 
   Action value_classCurAction;
 
 
   Value::Value(const string str, Types ValType)
   {
       value_classCurAction = ACT_CONSTRUCTOR;
 
       switch (ValType)
       {
           case CONST_VAL_INT: {
 
               value_type = CONST_VAL_INT;
 
               value.ival = atol(str.c_str());
 
               value_classDbgPrint ();
 
           break;
           }
           case CONST_VAL_FLOAT: {
 
               value_type =  CONST_VAL_FLOAT;
 
               value.fval = atof(str.c_str());
 
               value_classDbgPrint ();
 
           break;
           }
           default:; // Error;
       }
   }
 
 
   Value Value::operator = (const Value& prev)
   {
       value_classCurAction = ACT_ASSIGN;
 
       if (value_type == CONST_VAL_FLOAT&&prev.GetTypeName () == CONST_VAL_FLOAT)
       {
           value.fval = prev.Get_fval();
 
           value_classDbgPrint ();
 
           return *this;
       }
       else
         if (value_type == CONST_VAL_INT&&prev.GetTypeName () == CONST_VAL_INT)
         {
             value.ival = prev.Get_ival();
 
             value_classDbgPrint ();
 
             return *this;
         }
         else ;
            // Types messing Error?
         return *this;
   }
 
 
   Value Value::operator + (const Value& prev)
   {
       value_classCurAction = ACT_PLUS;
 
       if (value_type == CONST_VAL_FLOAT&&prev.GetTypeName () == CONST_VAL_FLOAT)
       {
           value.fval += prev.Get_fval();
 
           value_classDbgPrint ();
 
           return *this;
       }
       else
       if (value_type == CONST_VAL_INT&&prev.GetTypeName () == CONST_VAL_INT)
       {
           value.ival += prev.Get_ival();
 
           value_classDbgPrint ();
 
           return *this;
       }
       else
       if (value_type == CONST_VAL_INT&&prev.GetTypeName () == CONST_VAL_FLOAT)
       {
           value.ival += prev.Get_fval();
 
           value_classDbgPrint ();
 
           return *this;
       }
       else
       if (value_type == CONST_VAL_FLOAT&&prev.GetTypeName () == CONST_VAL_INT)
       {
           value.fval += prev.Get_ival();
 
           value_classDbgPrint ();
 
           return *this;
       }
            // Types messing Error?
       return *this;
   }
 
 
   Value Value::operator - (const Value& prev)
   {
       value_classCurAction = ACT_MINUS;
 
       if (value_type == CONST_VAL_FLOAT&&prev.GetTypeName () == CONST_VAL_FLOAT)
       {
           value.fval -= prev.Get_fval();
 
           value_classDbgPrint ();
 
           return *this;
       }
       else
       if (value_type == CONST_VAL_INT&&prev.GetTypeName () == CONST_VAL_INT)
       {
           value.ival -= prev.Get_ival();
 
           value_classDbgPrint ();
 
           return *this;
       }
       else
       if (value_type == CONST_VAL_INT&&prev.GetTypeName () == CONST_VAL_FLOAT)
       {
           value.ival -= prev.Get_fval();
 
           value_classDbgPrint ();
 
           return *this;
       }
       else
       if (value_type == CONST_VAL_FLOAT&&prev.GetTypeName () == CONST_VAL_INT)
       {
           value.fval -= prev.Get_ival();
 
           value_classDbgPrint ();
 
           return *this;
       }
            // Types messing Error?
       return *this;
   }
 
 
   Value Value::operator * (const Value& prev)
   {
       value_classCurAction = ACT_MULTIPLY;
 
       if (value_type == CONST_VAL_FLOAT&&prev.GetTypeName () == CONST_VAL_FLOAT)
       {
           value.fval *= prev.Get_fval();
 
           value_classDbgPrint ();
 
           return *this;
       }
       else
       if (value_type == CONST_VAL_INT&&prev.GetTypeName () == CONST_VAL_INT)
       {
           value.ival *= prev.Get_ival();
 
           value_classDbgPrint ();
 
           return *this;
       }
       else
       if (value_type == CONST_VAL_INT&&prev.GetTypeName () == CONST_VAL_FLOAT)
       {
           value.ival *= prev.Get_fval();
 
           value_classDbgPrint ();
 
           return *this;
       }
       else
       if (value_type == CONST_VAL_FLOAT&&prev.GetTypeName () == CONST_VAL_INT)
       {
           value.fval *= prev.Get_ival();
 
           value_classDbgPrint ();
 
           return *this;
       }
            // Types messing Error?
       return *this;
   }
 
 
   Value Value::operator / (const Value& prev)
   {
       value_classCurAction = ACT_DIVIDE;
 
       if (value_type == CONST_VAL_FLOAT&&prev.GetTypeName () == CONST_VAL_FLOAT)
       {
           value.fval /= prev.Get_fval();
 
           value_classDbgPrint ();
 
           return *this;
       }
       else
       if (value_type == CONST_VAL_INT&&prev.GetTypeName () == CONST_VAL_INT)
       {
           value.ival /= prev.Get_ival();
 
           value_classDbgPrint ();
 
           return *this;
       }
       else
       if (value_type == CONST_VAL_INT&&prev.GetTypeName () == CONST_VAL_FLOAT)
       {
           value.ival /= prev.Get_fval();
 
           value_classDbgPrint ();
 
           return *this;
       }
       else
       if (value_type == CONST_VAL_FLOAT&&prev.GetTypeName () == CONST_VAL_INT)
       {
           value.fval /= prev.Get_ival();
 
           value_classDbgPrint ();
 
           return *this;
       }
            // Types messing Error?
       return *this;
   }
 
 
   void Value::value_classDbgPrint ()
   {
       switch (DEBUG)
       {
           case 1: {
 
                 switch (VALUE_CLASS_DEBUG)
                 {
                      case 1: {
 
                            switch (SILENT)
                            {
                                 case 0: {
 
                                       switch (FULL_NAMES)
                                       {
                                            case 1: {
                                                  cout << "Current action with value: "
                                                       << value_classCurActionName[Action(value_classCurAction)] << ' '
                                                       << "Type of value: "                                      << ' '
                                                       << value_CurType[Types(GetTypeName())]                    << ' '
                                                       << "The value is: "                                       << ' ';
 
                                                  if (GetTypeName() == CONST_VAL_INT)
 
                                                      cout  << Get_ival()
                                                            << endl;
                                                  else
                                                  if  (GetTypeName() == CONST_VAL_FLOAT)
 
                                                      cout  << Get_fval()
                                                            << endl;
 
                                            }
                                            break;
                                            case 0: {
                                                  cout << "Current action with value: "
                                                       << Action(value_classCurAction) << ' '
                                                       << "Type of value: "            << ' '
                                                       << Types(GetTypeName())         << ' '
                                                       << "The value is: "             << ' ';
 
                                                  if (GetTypeName() == CONST_VAL_INT)
 
                                                      cout  << Get_ival()
                                                            << endl;
                                                  else
                                                  if  (GetTypeName() == CONST_VAL_FLOAT)
 
                                                      cout  << Get_fval()
                                                            << endl;
 
                                            }
                                            break;
                                            default: {
                                                  cout << "check FULL_NAMES value in debugger.h"
                                                       << endl;
                                            }
                                            break;
                                       }
                                 }
                                 break;
                                 case 1: {
 
                                       debug_data_file.open("data_file.dat",ofstream::app);
 
                                       if (debug_data_file.is_open())
 
                                       switch (FULL_NAMES)
                                       {
                                            case 1: {
                                                  debug_data_file << "Current action with value: "
                                                                  << value_classCurActionName[Action(value_classCurAction)] << ' '
                                                                  << "Type of value: "                                      << ' '
                                                                  << value_CurType[Types(GetTypeName())]                    << ' '
                                                                  << "The value is: "                                       << ' ';
 
                                                  if (GetTypeName() == CONST_VAL_INT)
 
                                                      debug_data_file  << Get_ival()
                                                                       << endl;
                                                  else
                                                  if  (GetTypeName() == CONST_VAL_FLOAT)
 
                                                      debug_data_file  << Get_fval()
                                                                       << endl;
 
                                                  debug_data_file.close();
                                            }
                                            break;
                                            case 0: {
                                                  debug_data_file << "Current action with value: "
                                                                  << Action(value_classCurAction) << ' '
                                                                  << "Type of value: "            << ' '
                                                                  << Types(GetTypeName())         << ' '
                                                                  << "The value is: "             << ' ';
 
                                                  if (GetTypeName() == CONST_VAL_INT)
 
                                                      debug_data_file  << Get_ival()
                                                                       << endl;
                                                  else
                                                  if  (GetTypeName() == CONST_VAL_FLOAT)
 
                                                      debug_data_file  << Get_fval()
                                                                       << endl;
 
                                                  debug_data_file.close();
                                            }
                                            break;
                                            default: {
                                                  debug_data_file << "check FULL_NAMES value in debugger.h"
                                                                  << endl;
                                            }
                                            break;
                                       }
 
                                       else
 
                                          ; // Error opening the file
                                 }
                                 break;
                                 default: {
                                        cout << "check SILENT value in debugger.h"
                                             << endl;
                                 }
                                 break;
                            }
                      }
                      break;
 
                      case 0:
                      break;
 
                      default: {
                             cout << "check VALUE_CLASS_DEBUG value in debugger.h"
                                  << endl;
                      }
                      break;
                 }
           }
           break;
 
           case 0:
           break;
 
           default: {
                  cout << "check DEBUG value in debugger.h"
                       << endl;
           }
           break;
       }
   }
 
   /*************************END OF CLASS MEMBERS***************************/
 
   std::ostream& operator << (std::ostream& output_stream,const Value& curr)
   {
       switch (curr.GetTypeName ())
       {
           case CONST_VAL_FLOAT:
 
                output_stream << curr.Get_fval();
                return cout;
           break;
           case CONST_VAL_INT:
 
                output_stream << curr.Get_ival();
                return cout;
           break;
           default:;
                //Something
           return cout;
       }
   }

variable_class.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
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
#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:
 
              Types var_type;
 
              union {
                     long   i;
                     double f;
                    } var_value;
 
              string var_name;
 
       public:
              Variable (const string);
             ~Variable (){};
 
              void SetVarValue ();
 
              string GetVarName () const {return var_name;};
              Types GetTypeName () const {return value_type;};
 
              long   GetVar_ival () const {return var_value.i;};
              double GetVar_fval () const {return var_value.f;};
 
              Variable 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_class.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
#include <string>
   using std::string;
 
#include "variable_class.h"
#include "value_class.h"
 
 
//
 
 
   vector<class Variable>initialized_vars;
 
   Variable::Variable (const string name)
   {
       var_name = name;
   }
 
 
   Variable Variable::operator = (const Value& val)
   {
       switch (val.GetTypeName ())
       {
           case CONST_VAL_INT: {
 
               var_type = CONST_VAL_INT;
 
               var_value.i = val.Get_ival();
 
               return *this;
           }
           case CONST_VAL_FLOAT: {
 
               var_type = CONST_VAL_FLOAT;
 
               var_value.f = val.Get_fval();
 
               return *this;
           }
           default:
              ; // Error;
       }
       return *this;
   }
 
 
   int FindNameOverVector (const string str)
   {
       for (vector<class Variable>::iterator i = initialized_vars.begin();
                                             i < initialized_vars.end();
                                           ++i                             )
       if ( (*i).GetVarName()  == str)
          return i-initialized_vars.begin();
       return -1;
   }

syntax_parser.h
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#ifndef SYNTAX_PARSER_H_INCLUDED
#define SYNTAX_PARSER_H_INCLUDED
 
 
   #include "value_class.h"
   class Value;
 
 
  /** @brief Function creates object
   *  of specific type,that got from
   *  parser_GetToken(),also used string
   *  value parser_CurTokenStr
   *  @return Object of Value class,
   *  implemented in value_class.h    */
   extern Value syntax_parserPrimary ();
 
 
   extern Value syntax_parserTerm ();
 
#endif // SYNTAX_PARSER_H_INCLUDED

syntax_parser.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
#include <cstdlib>
    // atof(),atol()
#include <fstream>
    using std::ofstream;
#include <iostream>
    using std::cout;
    using std::endl;
#include <vector>
    using std::vector;
#include <algorithm>
    // find()
 
#include "syntax_parser.h"
#include "parser.h"
#include "debugger.h"
#include "variable_class.h"
 
 
 
 
    Value syntax_parserPrimary()
    {
        switch (parser_GetToken ())
        {
            case TOKEN_EOL: {
 
                // Error:Primary expression expected
 
            break;
            }
            case TOKEN_CONST_INT: {
 
                Value int_x(::parser_CurTokenStr,CONST_VAL_INT);
 
                return int_x;
            }
            case TOKEN_CONST_FLOAT: {
 
                Value float_x(::parser_CurTokenStr,CONST_VAL_FLOAT);
 
                return float_x;
            }
            case TOKEN_IDENT: {
 
                int i = FindNameOverVector (parser_CurTokenStr);
 
                if (i >= 0)
                {
                    // Если находим переменную,создаём константу с нужным
                    // значением,и возвращаем его.
 
                    if (initialized_vars.at(i).GetTypeName() == CONST_VAL_INT)
                    {
                        Value v (initialized_vars.at(i).GetVarName(),CONST_VAL_INT);
 
                        return v;
                    }
                    else
                    {
                        Value v (initialized_vars.at(i).GetVarName(),CONST_VAL_FLOAT);
 
                        return v;
                    }
 
                }
                else
                {
                    // Если не находим,то создаём константу,
                    // приравниваем её к выражению,создаём переменную,
                    // передаём данные туда и возвращаем константу.
 
 
                }
            }
            default: ;
                //Error:(EOF??)
        }
        // This is bad
        //это просто чтобе не получать предупр.компилятора
        Value null_x("0",CONST_VAL_INT);
        return null_x;
    }
 
 
    Value syntax_parserTerm ()
    {
        while ((parser_GetToken()) != TOKEN_EOL)
        {
            switch (parser_CurToken)
            {
                // Тут у нас все операции,у которых приоритет
                // сразу после возврата значений констант
                // и переменных.Операции с приоритетом ниже
                // находятся на более "высоких этажах" рекурсии.
                // Ключевые слова имеют самый низкий приоритет,
                // а потому будут в самом начале рекурсивного спуска.
                default:  ;
            }
        }
        // This is bad
        //это просто чтобе не получать предупр.компилятора
        Value null_x("0",CONST_VAL_INT);
        return null_x;
    }
MoreAnswers
Эксперт
37091 / 29110 / 5898
Регистрация: 17.06.2006
Сообщений: 43,301
13.07.2009, 09:36     Пишем свой интерпретатор языка BASIC
Еще ссылки по теме:
C++ Написать интерпретатор программного языка -помощь
C++ Интерпретатор музыки стандарта BASIC PLAY на С++
Задание: разработать "Интерпретатор языка". С чего начать? C++
C++ Перепишите пожалуйста код программы с языка Visual Basic в C++
По русскому названию языка программирования определить английское название этого языка C++

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

Или воспользуйтесь поиском по форуму:
Evg
Эксперт CАвтор FAQ
17537 / 5775 / 370
Регистрация: 30.03.2009
Сообщений: 15,904
Записей в блоге: 26
13.07.2009, 09:36     Пишем свой интерпретатор языка BASIC #45
Цитата Сообщение от #pragma Посмотреть сообщение
Хм,я объясню,почему выбрал этот вариант. Сначала я делал именно с Value как членом класса,но потом выяснилось,что необходим вызов конструктора Value,то есть требовалась запись вроде такой
C++
1
Variable a(paser_CurTokenStr,CONST_INT)
Чтобы сделать возможной вызов конструктора Value,причём получается что нужно,чтобы в paser_CurTokenStr было не имя переменной,а строка для константы. Я не нашёл другого способа,кроме как перегрузить оператор = для Variable. А как тебе видится решение?
А в каких случаях тебе нужно создавать переменную со значением? Просто для конструкции "LET a=5" получается что-то типа того:

C
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/* Ожидаем конструкцию вида Ident "=" Expr */
void
syntax_StmtLET (void)
{
  Variable *var;
  Value val;
 
  /* Читаем имя переменной (девая часть присваивания) */
  var = syntax_Ident(); // <--- здесь реализовано синтаксическое правило Ident
 
  /* Знак "=" */
  if (parser_GetToken() != TOKEN_DELIM_EQUAL)
    error ("syntax error near %s", parser_CurTokenStr);
 
  /* Значение выражения (правая часть присваивания) */
  val = syntax_Expr(); // <--- здесь реализовано синтаксическое правило Expr
 
  /* Строим присваивание */
  var->SetValue(val);
}
Добавлено через 22 минуты 36 секунд
syntax_parser.c строки 49-50. Ты пишешь, что создаёшь переменную, но это не правильно. Ты в данный момент парсишь ПРАВУЮ часть присваивания, т.е. там, где нужно читать значения. Т.е. все переменные уже должны быть созданы, а ты в этот момент НЕ создаёшь новую переменую, а достаёшь в таблице уже СУЩЕСТВУЮЩУЮ переменную и берёшь от неё значение. Если переменная не существует, то тут выбираешь как действовать: либо выдавать ошибку, либо считать, что для несуществующеё переменной значение равно нулю. Я бы выбрал первый вариант.

Возможно мне следовало пояснить момент, который для тебя совсем неочевиден. Имя переменной (в нашем случае это синтаксическое правило Ident) может встретиться в присваивании в правой части или в левой. В этих двух случаях оно трактуется по разному. Устоявшиеся технические термины для этого lvalue (от слова left) и rvalue (от right).

Когда имя переменной стоИт в позиции rvalue, то это означает, что нам нужно прочитать ЗНАЧЕНИЕ этой переменной, а когда в позиции lvalue (т.е. фактически мы хотим записать в переменную), то говорят, что нужно АДРЕС переменной. Адрес не в буквальном смысле слова, а в абстрактном - т.е. это нечто, при помощи которого мы можем записать значение в переменную. Иногда rvalue и lvalue называют соотвественно VALUE POSITION и NAME POSITION, но это несколько неудобная терминология из-за слова NAME

Вот примеры того, как переменная "AAA" находится в разных позициях

PureBasic
1
2
3
4
5
6
7
b = 1 + AAA    <--- rvalue (значение)
b = COS (AAA)  <--- rvalue (значение), имеем значение среди параметров
         (а необязательно после знака присваивания
 
AAA = 1  <--- lvalue (адрес)
INPUT AAA  <--- lvalue (адрес), т.е. здесь знака присваивания нет, но семантика
         оператора INPUT такова, что в переменную нужно записывать
Поэтому технически над синтаксическим правилом Ident можно сделать две настройки

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
Variable*
syntax_Ident (void)
{
  // Разгребаем имя переменной. Создаём переменную и возвращаем
  // на неё указатель. У меня была идея избавиться от создания
  // переменных без константы, но такой вариант принесёт больше гемора,
  // чем пользы. Лучше в переменной иметь дополнительный признак,
  // инициализирована переменная или нет. Поэтому вданной процедуре
  // мы просто заводим неинициализированную переменную
}
 
// Разбор имени переменной в левой части присваивания. Именно эту
// процедуру надо подставить в мой вышестоящий код. Здесь нам нужен
// только АДРЕС переменной (в том понятии, котором я описал выше),
// а поэтому просто возвращаем ссылку на созданную переменную
Variable*
syntax_IdentLValue (void)
{
  return syntax_Ident();
}
 
// Разбор имени переменной в левой части присваивания. Здесь
// нам нужно вернуть значение переменной
Value
syntax_IdentRValue (void)
{
  Variable *var;
 
  var = syntax_Ident();
 
  if (var неинициализирована)
    error ("неинициализированная переменная %s", var->GetName());
 
  return var->GetValue();
}
Вот что-то типа того

============================================

Ещё момент по твоему коду: value_class.h строки 85 и 86.
Именно то место, где слодовало бы поставить ASSERT (внутренний контроль), что от целой переменной нельзя брать плавающее значение и наоборот. Хотя в теории эти методы вообще не должны использоваться за пределами модуля Value (потому как все операции перегружены, печать значения реализована в виде интерфейса, больше снаружи вроде бы как незачем)

Добавлено через 5 минут 59 секунд
Я понял, почему у тебя получалось создание переменной непосредственно со значением. Но нужно было бы это делать с Value (т.е. распарсили строковое имя переменной, вытащили правую часть в виде Value, создали переменную с Value). Но засада в том, что тут переменную надо НЕ создавать, а записывать в неё (т.е. если переменная уже создана, то новая переменная не нужна)
Yandex
Объявления
13.07.2009, 09:36     Пишем свой интерпретатор языка BASIC
Закрытая тема Создать тему
Опции темы

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