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

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

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

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

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

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

Репозиторий с проектом находится тут, там же есть возможность в браузере посмотреть историю ревизий (английский в логах весьма примитивен,комментарии и рекомендации можете писать в личку),а также скачать самый последний архив репозитория в формате .tar.gz
Если кто-то пользуется Subversion,скачать исходники можно так:
Код
svn co https://basin.svn.sourceforge.net/svnroot/basin basin
Эти темы возникли в результате моих вопросов по ходу написания:
Технический приём для формирования согласованных данных
Makefile: как с использованием gcc строить автоматические зависимости от .h файлов?
Вопрос по svn (Subversion)
Создание системы тестирования ПО.
Вопрос про разные реализации бэйсиков
[C/C++] Можно ли выразить порядковый номер элемента массива через индексы?
[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++
C++ пишем свой троян с нуля
C++ Интерпретатор небольшого языка программирования на С++
C++ Интерпретатор музыки стандарта BASIC PLAY на С++
Интерпретатор/компилятор ассемблер-подобного языка C++
Пишем свой чекер C++
После регистрации реклама в сообщениях будет скрыта и будут доступны все возможности форума.
#pragma
Временно недоступен
 Аватар для #pragma
952 / 223 / 6
Регистрация: 12.04.2009
Сообщений: 921
23.09.2009, 00:27  [ТС]     Пишем свой интерпретатор языка BASIC #141
Цитата Сообщение от Evg Посмотреть сообщение
Можешь тест добавить к себе в набор (не пропадать же ему)
Беру на вооружение.Вообще сейчас займусь папкой с тестами,наберу небольшую базу,ну и попробую допилить DIM.
Цитата Сообщение от Evg Посмотреть сообщение
Правда у тебя есть косячок. Если сделать переход на метку, но метку не определять, то у тебя падает в кору.
Это проверю.
Цитата Сообщение от Evg Посмотреть сообщение
И твоя и моя версия страдают недостатком выдачи диагностики - временами без поллитры не разберёшь, что оно хочет. Современные умные компиляторы позволяют более точно жиагностировать ошибку - они указывают не только номер файла и строки, но ещё и номер позиции в строке. Понятное дело, что сейчас это вообще не критично, но это ещё одна вещь, которая нужна для хорошего программного продукта - нормальный диалог с пользователем. Просто для порядка имей в виду
Ок,я понял,попытаюсь добавить.
Цитата Сообщение от Evg Посмотреть сообщение
Я так понимаю, что тестовый пример в репозитории отражает все поддерживаемые конструкции?
Да,в принципе,но возможны варианты этих конструкций,я просто не углублялся.Например,IF теперь,если не заканчивается на EOL,то должен заканчиваться на END IF именно двумя раздельными словами.Кстати,я уже задумываюсь о том,как делать правильный выход из программы,что нужно ведь,наверное,освободить сначала всю память,а потом уже делать выход?
Цитата Сообщение от Evg Посмотреть сообщение
Добавлено через 2 минуты
Смотрю ты концепцию условных переходов переделал. Ты просто содрал или всё-таки разобрался, чем плох старый вариант?
Да,я во всём разбирался,я именно делал сначала не подумав,а потом уже разбирался,почему мой вариант был плох,то есть,не просто содрал,а понял,почему так и так.Хотя сознаюсь,для меня изобретение велосипедов-очень тяжёлое занятие,и если я вижу,что есть уже что-то,что может подойти,я непременно это использую,и только в последнюю очередь буду напрягать мозги,прямо напасть какая-то.
Цитата Сообщение от Evg Посмотреть сообщение
Добавлено через 11 минут
В общем больше не получилось подобрать примеры, чтобы интерпретатор ломался или работал неправильно. В исходниках копаться уже совсем тяжело, так что пока прими поздравления: то, что уже есть, с виду работает так как надо
Можно сказать,что,по большому счёту,это твои исходники,я просто портировал их для компиляции на свой лад.. Просто так получилось методом проб и ошибок,и конечно,лени.. Конечно,там много мусора,я подумал,что,может стоит сделать Statement с помощью классов? А то у меня сокрытие данных превращается в кучу функций,ведь у меня сама структура только в .cpp файле...
После регистрации реклама в сообщениях будет скрыта и будут доступны все возможности форума.
Evg
Эксперт CАвтор FAQ
 Аватар для Evg
17192 / 5446 / 337
Регистрация: 30.03.2009
Сообщений: 14,777
Записей в блоге: 26
23.09.2009, 09:28     Пишем свой интерпретатор языка BASIC #142
Цитата Сообщение от #pragma Посмотреть сообщение
Кстати,я уже задумываюсь о том,как делать правильный выход из программы,что нужно ведь,наверное,освободить сначала всю память,а потом уже делать выход?
По большому счёту можно не заморачиваться и выходить. Все данные у тебя живут в течение всей программы, а потому на проблему освобождения динамической памяти можно забить. Я вижу только две причины, чтобы сделать освобождение памяти:
- это хороший тон программирования
- при помощи специальных программ можно будет отслеживать утечки памяти (т.е. в тот момент когда ты освободил всё, утилиты смогут найти фрагменты, который оказались неосвобождёнными)

Цитата Сообщение от #pragma Посмотреть сообщение
я подумал,что,может стоит сделать Statement с помощью классов? А то у меня сокрытие данных превращается в кучу функций,ведь у меня сама структура только в .cpp файле...
Тут я тебе не советчик. У меня нет С++'ного мышления. Когда я поначалу для тренировки решил попробовать написать на Си++, то понял, что при моём подходе нормальная концепция Си++ скорее мешает, чем помогает
#pragma
Временно недоступен
 Аватар для #pragma
952 / 223 / 6
Регистрация: 12.04.2009
Сообщений: 921
23.09.2009, 22:40  [ТС]     Пишем свой интерпретатор языка BASIC #143
Пришлось переделывать представление для LET в файле Statement.(это связано с тем,что в левой части присваивания может быть элемент массива,а его индекс,имя массива и значение элемента нужно где-то хранить).
Возник вопрос,скорее,относящийся к чистому С,поскольку данная конструкция пришла оттуда и в принципе это ты мне показал данный способ организации данных.Промежуточная схема теперь выглядит так:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
         struct { // Данные для LET
            union {                 // левая часть присваивания {
               struct {
                  Variable    *var;
               } var;
               struct {
                  Array *array;
                  size4 *index;
                  Value *value;
               } arr_el;
            } data;                 // левая часть присваивания }
            expr_Node_t *expr;      // правая часть присваивания
         } let;
Вопрос в том,как можно узнать на этапе калькуляции(когда мы уже интерпретируем и меняем значение переменных),что записано в let.data - arr-el или var?Можно ли полагаться на sizeof? Есть ли способ узнать,куда были записаны данные,в arr_el или в var,или нужно применять какие-то костыли,наподобие дополнительной переменной - флага?
Evg
Эксперт CАвтор FAQ
 Аватар для Evg
17192 / 5446 / 337
Регистрация: 30.03.2009
Сообщений: 14,777
Записей в блоге: 26
24.09.2009, 00:18     Пишем свой интерпретатор языка BASIC #144
Цитата Сообщение от #pragma Посмотреть сообщение
Вопрос в том,как можно узнать на этапе калькуляции(когда мы уже интерпретируем и меняем значение переменных),что записано в let.data - arr-el или var?Можно ли полагаться на sizeof? Есть ли способ узнать,куда были записаны данные,в arr_el или в var,или нужно применять какие-то костыли,наподобие дополнительной переменной - флага?
Только дополнительную переменную. И это не костыль. Ибо если хранить данные в union'е, то обязательно нужно дополнительное поле, по которому возможно понять, какое из полей union'а у нас инициализировано

Добавлено через 33 минуты
Цитата Сообщение от #pragma Посмотреть сообщение
(это связано с тем,что в левой части присваивания может быть элемент массива,а его индекс,имя массива и значение элемента нужно где-то хранить)
Я всё это храню в в виде выражения (которое может быть либо узлом переменной, либо декомпозицией массива) - см. interp_InterpStatementLET
#pragma
Временно недоступен
 Аватар для #pragma
952 / 223 / 6
Регистрация: 12.04.2009
Сообщений: 921
26.09.2009, 02:20  [ТС]     Пишем свой интерпретатор языка BASIC #145
Кое-как смог сделать массивы,хотя не уверен,полностью ли правильно,буду тестировать.Как-то у меня громоздко получилось,запутано всё так.У тебя по виду всё так просто сделано с массивами,мне же пришлось добавлять функциональности чуть ли не по всей программе,и ещё осталось (особенно связано с типизацией).
Но с виду работает.Рабочий пример (в ревизии 30):
PureBasic
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
   IF B1 THEN LET C=1: LET D=2 ELSE LET C=10: LET D=20
   PRINT C
   PRINT
   PRINT D
   PRINT
 
   ' Повторяем предыдущий код, но с таким условием, чтобы исполнилось ELSE
   IF B1-460 THEN LET C=1: LET D=2 ELSE LET C=10: LET D=20
   PRINT C
   PRINT
   PRINT D
   PRINT
 
   'Here we define and calculate array
    DIM array(3,10)
 
    LET array(0,9) = 2
 
    LET i = 0
    LET j = 0
 
    WHILE i < 3
      WHILE j < 10
           LET array(i,j) = i+j
           LET j = j+1
      WEND
      LET i = i+1
    WEND
 
    LET i = 0
    LET j = 0
 
    WHILE i < 3
      WHILE j < 10
           PRINT "array(",i,",",j,") is: ",array(i,j)
         PRINT
           LET j = j+1
      WEND
      LET i = i+1
    WEND
 
   ' Считаем сумму чисел от 1 до 100
   LET COUNT=0
   1:
   WHILE COUNT<100
      LET COUNT=COUNT+1
      PRINT COUNT," "
      IF COUNT<50 THEN GOTO 1 ELSE GOTO 2
   WEND
   2:
   PRINT
   PRINT "END OF PROGRAM"
Добавлено через 54 минуты
С
таким примером
PureBasic
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
' Работа с многомерным массивом
DIM AA(2,3,4)
LET COUNT = 100
LET i = 0
WHILE i < 2 
  LET j = 0
  WHILE j < 3 
    LET k = 0
    WHILE k < 4 
      LET COUNT = COUNT + 1
      LET AA(i,j,k) = COUNT
      LET k = k + 1
    WEND
    LET j = j + 1
  WEND
  LET i = i + 1
WEND
PRINT COUNT     
PRINT
PRINT AA(1,2,3) 
PRIN
прога вылетает - vector out of range - где-то напутал с индексами,буду чинить...

Добавлено через 19 минут
Всё,вроде поправил.Evg,а утебя работают цвета в консоли,если скриптом прогоняешь тесты? А то я сделал,но не знаю,будет ли везде работать. Я слышал,что это может не работать на некоторых терминалах/системах (в смысле другие управляющие символы для цветов).
Evg
Эксперт CАвтор FAQ
 Аватар для Evg
17192 / 5446 / 337
Регистрация: 30.03.2009
Сообщений: 14,777
Записей в блоге: 26
26.09.2009, 12:54     Пишем свой интерпретатор языка BASIC #146
Красным цветом по крайней мере выделяется. У тебя по ходу не влиты файлы с эталонными выдачами. А вообще цвета - это от терминала зависит. В своё время сталкивался с тем, что на солярисовых родных терминалах это не работает. Так что на цвета я уже давно забил. Обычно я пишу "PASS" и "****** FAIL ******", чтобы "FAIL" на фоне всего чётко выделялось. Но ты можешь пока оставить себе цвета, если это удобнее

По поводу индексации массивов - в бэйсике она начинается с 1, а не с нуля. Т.е. при формировании номера элемента в многомерной декомпозиции из каждого индекса надо вычесть 1. А так же есть DIM A (10 TO 20) - в этом случае надо вычитать 10. У меня пока это не сделано, но в комментариях обозначено. А 0 это частный случай (вычитаем 0, а потому ничего не меняется).

В принципе ты сам волен выбирать, как оно должно быть, но просто лучше делать так, как принято. Я по крайней мере буду приближать максимально к настоящему qbasic'у

А строковые массивы у тебя работают? А то основные конструкции вроде бы как уже реализованы, а потому уже пора заниматься наведением порядка. И в программе и в синтаксисе
Evg
Эксперт CАвтор FAQ
 Аватар для Evg
17192 / 5446 / 337
Регистрация: 30.03.2009
Сообщений: 14,777
Записей в блоге: 26
26.09.2009, 13:00     Пишем свой интерпретатор языка BASIC #147
**

Добавлено через 1 минуту
> С таким примером прога вылетает
Потому что у меня индексация с 1, а у тебя с нуля

Добавлено через 3 минуты
PureBasic
1
LET B=A
PureBasic
1
2
DIM A(2)
LET B=A(1)
Первый пример у тебя нормально ломается с сообщением, что A неинициализировано, а второй - нет
#pragma
Временно недоступен
 Аватар для #pragma
952 / 223 / 6
Регистрация: 12.04.2009
Сообщений: 921
26.09.2009, 17:19  [ТС]     Пишем свой интерпретатор языка BASIC #148
Цитата Сообщение от Evg Посмотреть сообщение
**
Первый пример у тебя нормально ломается с сообщением, что A неинициализировано, а второй - нет
Я где-то прочитал,что по умолчанию все элементы массива равны нулю.Они все обнуляются в interp_stmtDIM.
Про идексацию я тоже прочитал что с нуля начинается якобы,но,видимо,книга была не правильная Значит,надо переделать.
Насчёт эталонных выдач - не держу их в проекте.Задумка такая - там есть файл README в корневом каталоге,в нём написано,что если планируются изменения в исходниках,то сначала запускается скрипт tmpl_maker,он всё создаёт,а после изменений уже запускается test_script.
Evg
Эксперт CАвтор FAQ
 Аватар для Evg
17192 / 5446 / 337
Регистрация: 30.03.2009
Сообщений: 14,777
Записей в блоге: 26
26.09.2009, 17:48     Пишем свой интерпретатор языка BASIC #149
Цитата Сообщение от #pragma Посмотреть сообщение
Я где-то прочитал,что по умолчанию все элементы массива равны нулю.Они все обнуляются в interp_stmtDIM.
На самом деле в бэйсиках вообще все переменные по умолчанию обнуляются, а не только массивы. А строковые переменные и массивы пробиваются пустыми строками. Просто мы по началу сделали так, чтобы проще было жить (да и что-то типа отладочного средства получилось). В идеале мне видится так, что всё должно быть, как в обычном бэйсике, но по доп. опции включается поддержка контроля неинициализированных переменных. В любом случае это совйство для обычной переменной и для элемента массива должно быть одинаковым

Цитата Сообщение от #pragma Посмотреть сообщение
Насчёт эталонных выдач - не держу их в проекте.Задумка такая - там есть файл README в корневом каталоге,в нём написано,что если планируются изменения в исходниках,то сначала запускается скрипт tmpl_maker,он всё создаёт,а после изменений уже запускается test_script.
Это неправильно. Предположим у тебя работает. Далее я копирую к себе твои исходники и перекомпиливаю. Но на моей машине оно работает неправильно - например есть косяк, зависящий от использования неинициализированной переменной. У тебя он проявляется одним способом, у меня другим. Далее я получаю типа эталон, который на самом деле оказывается неправильным. Поэтому эталонная выдача должна обязательно рядом с тестом лежать. Если при изменении интерпретатора эталонная выдача меняется (изменился формат печати, справлена ошибка и т.п.), то эталонную выдачу надо править. И никуда от этого не денешься
#pragma
Временно недоступен
 Аватар для #pragma
952 / 223 / 6
Регистрация: 12.04.2009
Сообщений: 921
28.09.2009, 05:00  [ТС]     Пишем свой интерпретатор языка BASIC #150
Кое-что переделал,теперь неинициализированное значение в массиве вызывает ошибку.Поправил работу со строками и контроль типов для массивов.Попробовал переделать индексацию с нуля на 1,но что-то запутался с индексами и вернул всё назад.Надо просто сесть нарисовать как-то для себя,что-бы иметь представление,где точно менять индексы,а то у меня там кавардак с векторами.Ведь и общая формула доступа к элементу должна измениться? Сейчас она выглядит так(я её на всякий случай прописал в комментах(частный случай для 3-мерного массива),а то там цикл непонятный такой =) ) :
C++
1
array[a][b][c] = linear_array[a*y*z + b*z + c]
И получается вроде как нужно вычесть единицу из каждого индекса при умножении... Ну я как-нибудь соберусь с мыслями и сделаю :/

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

Ещё есть вопрос про FOR: по идее,эту инструкцию тоже можно отнести к Conditional Branch,но нужно тогда правильно сгенерировать условие,так? Я имею в виду,что условие придётся генерировать как-то вручную,ведь это не стандартное выражение,а ещё как-то вручную добавить в конце блока новый LET чтобы менял значение счётчика на нужную величину.Или всё таки лучше делать FOR как отдельную конструкцию?
Evg
Эксперт CАвтор FAQ
 Аватар для Evg
17192 / 5446 / 337
Регистрация: 30.03.2009
Сообщений: 14,777
Записей в блоге: 26
28.09.2009, 09:31     Пишем свой интерпретатор языка BASIC #151
Цитата Сообщение от #pragma Посмотреть сообщение
C++
1
array[a][b][c] = linear_array[a*y*z + b*z + c]
И получается вроде как нужно вычесть единицу из каждого индекса при умножении... Ну я как-нибудь соберусь с мыслями и сделаю :/
Да. В правой части должно быть "linear_array[(a-1)*y*z + (b-1)*z + (c-1)]". А в общем случае надо вычитать стартовый индекс (который по умолчанию равен 1, но по синтаксису можно задавать самому)

Цитата Сообщение от #pragma Посмотреть сообщение
Я вот незнаю,а как лучше сделать выключающуюся поддержку проверки неинициализированных переменных: через параметр командной строки,или через перекомпиляцию с помощью препроцессора? В принципе,и тот и другой вариант можно оправдать.Командная строка - удобство,гибкость.Препроцессор - всё-таки это не стандартная версия QBASIC,и вроде как надо перекомпилить,да и бинарник уменьшит,хоть и ненамного.Хотя стандартная версия всё равно не получится,так вроде командная строка перевешивает.Как считаешь?
Любая пользовательская настройка должна быть в виде опции. ТОлько поведение на мой взгляд надо инвертировать. По умолчанию работать так, как принято в бэйсиках, а по опции - ломаться на неинициализированных данных. Эта опция - по сути дела отладочное средство. Технически удобнее всего делать так, что в момент заведения новой переменной ей сразу прописывать нулевую инициализацию

Цитата Сообщение от #pragma Посмотреть сообщение
Ещё есть вопрос про FOR: по идее,эту инструкцию тоже можно отнести к Conditional Branch,но нужно тогда правильно сгенерировать условие,так? Я имею в виду,что условие придётся генерировать как-то вручную,ведь это не стандартное выражение,а ещё как-то вручную добавить в конце блока новый LET чтобы менял значение счётчика на нужную величину.Или всё таки лучше делать FOR как отдельную конструкцию?
Да, нужно сгенерировать правильный набор операций сравнений, условных переходов и т.п. Т.е. при поддержке FOR'а изменения вносятся только в синтаксический разборщик. В промежуточном представлении и интерпретаторе не надо делать ничего. Имено поэтому такой вид промежуточного представления даёт дополнительные бонусы: т.е. многие сложные высокоуровневые конструкции реализуются через один и тот же маленький набор низкоуровневых примитивов

Т.е. промежуточное представление для

PureBasic
1
2
3
for i=1 to 10 step 2
  <тело цикла>
next i
должно выглядеть точно так же, как для

PureBasic
1
2
3
4
5
let i=1
while i<=10
  <тело цикла>
  let i=i+2
wend
#pragma
Временно недоступен
 Аватар для #pragma
952 / 223 / 6
Регистрация: 12.04.2009
Сообщений: 921
29.09.2009, 06:42  [ТС]     Пишем свой интерпретатор языка BASIC #152
При попытке сделать индексацию с единицы нашёл какой-то странный баг.При печати в данном исходнике печать первого и второго цикла расходятся.
Исходник
PureBasic
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
   'Here we define and calculate array
   DIM array(3,10)
 
   LET i = 1
   LET j = 1
   LET n = 1
 
   WHILE i < 4
     WHILE j < 11
          LET array(i,j) = n
      PRINT "array(",i,",",j,") is: ",array(i,j)
          PRINT
      LET j = j+1
          LET n = n+1
     WEND
       LET i = i+1
       LET j = 1
   WEND
   
   PRINT
   LET i = 1
   LET j = 1
 
   WHILE i < 4
     WHILE j < 11
     PRINT "array(",i,",",j,") is: ",array(i,j)
         PRINT
     LET j = j+1
     WEND
       LET i = i+1
       LET j = 1
   WEND

Сначала грешил на формулу,но когда вставил печать в первый цикл,вообще попал в тупик,так как она верная.Вообще,если после первого цикла менять значение array(3,1) или array(2,1),то меняются также значения
array(2,10) и array(1,10) соответственно.Считал по формуле - получается,что у них в линейном массиве тот же индекс.
PureBasic
1
DIM array(3,10)
C++
1
2
linear_index(2,10) = (2-1)*(10-1) + (10-1) = 18
linear_index(3,1) = (3-1)*(10-1) +(1-1) = 18
Но верная печать в первом массиве ставит в тупик.В чём дело-пока загадка.На всякий случай скомиттил 33 ревизию.Там индексация с единицы,добавлена опция командной строки для проверки неинициализированных переменных,называется --noinit-errors.Теперь формат запуска программы такой:
Bash
1
 ./basin [option] [filename]
Также в случае ошибки синтаксиса или выполнения память освобождается.
Evg
Эксперт CАвтор FAQ
 Аватар для Evg
17192 / 5446 / 337
Регистрация: 30.03.2009
Сообщений: 14,777
Записей в блоге: 26
29.09.2009, 11:05     Пишем свой интерпретатор языка BASIC #153
Вычитать 1 нужно только из индекса. Размерность массива остаётся прежней
Вместо
C
1
2
linear_index(2,10) = (2-1)*(10-1) + (10-1) = 18
linear_index(3,1) = (3-1)*(10-1) +(1-1) = 18
должно быть
C
1
2
linear_index(2,10) = (2-1)*(10) + (10-1) = 19
linear_index(3,1) = (3-1)*(10) +(1-1) = 20
Добавлено через 7 минут
Пока не забыл, что на будущее. Чего нужно было бы сделать, но что нужно, а что нет - решай сам. В идеале надо сделать всё

1. Поддержать массивы с произвольного стартового индекса. Типа "DIM A(5 TO 10)
2. По возможности поддержать все синтаксические реализации для IF, WHILE, FOR (ну и какие там ещё есть циклы)
3. Видимо придётся наводить порядок в исходниках программы. Когда много чего работает - наверняка накопилось куча мест, которые неплохо бы переделать
4. Возможно сразу же попытаться реализовать вариант с генерацией исходника на Си++ с целью получения бинарника (о чём я уже рассказывал). Такой вариант уже имеет рассматривать с той точки зрения, что при дальнейшем развитии можно будет поддерживать обе ветки (интерпретация и генерация текста Си++), а потому многие неоптимальности видны будут сразу. Из разряда того, что при нынешней схеме реализация конструкции FOR НЕ затронула бы процесс интерпретации и генерации текста, т.к. в промежуточное представление мы не вносили новых конструкций

Ну и дальнейшим пунктом развития будет реализация встроенных функций (SIN, COS, VAL, STR$, ...)
Evg
Эксперт CАвтор FAQ
 Аватар для Evg
17192 / 5446 / 337
Регистрация: 30.03.2009
Сообщений: 14,777
Записей в блоге: 26
30.09.2009, 14:25     Пишем свой интерпретатор языка BASIC #154
Если смотрел мои исхожники, там есть конструкция var_DimDescr_t, описывающая размерность массива. Эту же конструкцию я использую и для описания элемента массива (которая по научному называется "декомпозиция"). Это неправильно (мне тогда просто лень было, а потом забыл). Просто должны быть две вещи:

- Описатель размерностей массива (на каждую размерность хранится стартовый индекс и размер). Этот описатель является свойством переменной
- Описатель декомпозиции элемента массива (на каждую размерность хранится текущее значение индекса). Этот описатель является свойством операции
Номер элемента вычисляется на основании двух этих конструкций.
Evg
Эксперт CАвтор FAQ
 Аватар для Evg
17192 / 5446 / 337
Регистрация: 30.03.2009
Сообщений: 14,777
Записей в блоге: 26
30.09.2009, 21:16     Пишем свой интерпретатор языка BASIC #155
Кстати, вот ещё идея. Сделать "OPTION TRACE_CMD <val>", "OPTION TRACE_WRITE <val>". Пользовательские фичи для включения трассировки исполнения операций и трассировки записей. Т.е. если есть блок кода, который предположительно работает неправильно, то командами "OPTION TRACE_WRITE 1" и "OPTION TRACE_WRITE 0" соотвественно включаем и выключаем трассировку
#pragma
Временно недоступен
 Аватар для #pragma
952 / 223 / 6
Регистрация: 12.04.2009
Сообщений: 921
30.09.2009, 21:33  [ТС]     Пишем свой интерпретатор языка BASIC #156
Цитата Сообщение от Evg Посмотреть сообщение
Кстати, вот ещё идея. Сделать "OPTION TRACE_CMD <val>", "OPTION TRACE_WRITE <val>". Пользовательские фичи для включения трассировки исполнения операций и трассировки записей. Т.е. если есть блок кода, который предположительно работает неправильно, то командами "OPTION TRACE_WRITE 1" и "OPTION TRACE_WRITE 0" соотвественно включаем и выключаем трассировку
В смысле навроде того,что у нас пишется в логи? Только там что будет писаться,что-то недопонимаю? UPD Всмысле будут писаться текущие значения какой-то переменной?
Evg
Эксперт CАвтор FAQ
 Аватар для Evg
17192 / 5446 / 337
Регистрация: 30.03.2009
Сообщений: 14,777
Записей в блоге: 26
30.09.2009, 21:41     Пишем свой интерпретатор языка BASIC #157
Ну навроде того, только это то, что для пользователя предназначено. При печати записей печатать что-то типа

Код
a.bas:10 WRITE A = 10
a.bas:11 WRITE B = 11
...
При печати трассировки кода печатать что-то типа

Код
exec a.bas:10
exec a.bas:11
exec a.bas:12
В отсутсвие отладчика комбинация этих двух печатей или одна из них способна сильно упростить процесс отладки. Ну это так. Воспомогательная функция, напрямую к интерпретатору не относящаяся

Добавлено через 56 секунд
Когда у тебя два заумных вложенных цикла с кучей ветвлений внутри, тяжело "теоретически" разбирать ошибки
Mozart
 Аватар для Mozart
39 / 17 / 1
Регистрация: 21.08.2009
Сообщений: 63
01.10.2009, 14:48     Пишем свой интерпретатор языка BASIC #158
Господа, увидел вашу тему, решил наваять что-то подобное, но у меня возникла проблема:
сначала сделал линейно и у меня выходило что 2+2*2=8, хотя 6;
потом немного видоизменил код добавив рекурсию в итоге предыдущее считает правильно а 3-1+2=0 хотя 4.
Как вы решали проблему приоритета умножения/деления над сложением/вычитанием?
ЗЫ: если брать все в скобки - считает правильно.
Evg
Эксперт CАвтор FAQ
 Аватар для Evg
17192 / 5446 / 337
Регистрация: 30.03.2009
Сообщений: 14,777
Записей в блоге: 26
01.10.2009, 16:32     Пишем свой интерпретатор языка BASIC #159
Создай, пожалуйста, отдельную тему и кинь мне ссылку на неё вличку. Я там всё напишу. Чтобы здесь вторую дискуссию не устраивать
MoreAnswers
Эксперт
37091 / 29110 / 5898
Регистрация: 17.06.2006
Сообщений: 43,301
01.10.2009, 23:28     Пишем свой интерпретатор языка BASIC
Еще ссылки по теме:

Задание: разработать "Интерпретатор языка". С чего начать? C++
По русскому названию языка программирования определить английское название этого языка C++
C++ Перепишите пожалуйста код программы с языка Visual Basic в C++
Не удается откомпилировать интерпретатор М-языка C++
Пишем свой класс, спецификатор доступа protected C++

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

Или воспользуйтесь поиском по форуму:
Evg
Эксперт CАвтор FAQ
 Аватар для Evg
17192 / 5446 / 337
Регистрация: 30.03.2009
Сообщений: 14,777
Записей в блоге: 26
01.10.2009, 23:28     Пишем свой интерпретатор языка BASIC #160
Я что-то смотрю, у тебя одни негативные тесты. А позитивные-то есть?
Yandex
Объявления
01.10.2009, 23:28     Пишем свой интерпретатор языка BASIC
Закрытая тема Создать тему
Опции темы

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