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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

464
Evg
Эксперт CАвтор FAQ
17941 / 6172 / 412
Регистрация: 30.03.2009
Сообщений: 16,942
Записей в блоге: 27
25.08.2009, 15:29 #106
Итого, примерно надумал следующее. Ввожу понятие типа, который может быть целочисленный (NUMERIC), строковой (STRING) и логический (LOGICAL). Переменные могут быть только первых двух типов, промежуточное значение выражения - всех трёх. Тип изначально является свойством значения (Value), но при этом засовывается в Variable и Expression, поскольку они так же оперируют значениями. Целочисленный тип разбиваю далее на два подтипа: целый и плавающий. Любые двухаргументные операции разрешены только над одинаковыми типами (в этом отношении int и float имеют одинаковый тип NUMERIC). Присваивание разрешено только для одинаковых типов

На текущий момент есть вроде бы всё основное: переменные, арифметика, операции управления, строки, массивы (в том числе и многомерные). Для полного сачтья надо поддержать встроенные функции (обычно их называют термином "builtin functions" или сокращённо просто "builtin"). Ну и с технической стороны надо поддержать пользовательские метки, FOR'ы, нормально поддержать двухсимвольные операции сравнения (">=", "<=", "<>"), но это всё уже мелкая работа, т.к. структуру уже затрагивать не должно - просто будет допиливаться код.

Что мне сейчас не нравится принципиально - это реализация строк. Она сделана в виде массива, а потому структура Value получается большой. Нормально строку надо хранить не по значению, а по косвенности, но в Си это означает мегагеморрой с динамической памятью, слежение за удалением и копированием и т.п. А вот на Си++ это делается нормально за счёт деструкторов и реализации конструктора копирования (либо ваще тупо использовать стандартный String)

Собственно программу выдаю для изучения того, как всё это примерно выглядит. Понятно, что тебе не нужно пытаться всё это повторить. Делай так, как понимаешь. Но с моей прогой возможно удетнекое общее понятие того, что в итоге должно быть. Словами это фиг объяснишь

Выкладываю опять в двух вариантах: basic.rar в кодировке KOI-8 и basic_w.rar в кодировке WIN-1251
1
Вложения
Тип файла: rar basic.rar (27.0 Кб, 23 просмотров)
Тип файла: rar basic_w.rar (27.4 Кб, 34 просмотров)
Evg
Эксперт CАвтор FAQ
17941 / 6172 / 412
Регистрация: 30.03.2009
Сообщений: 16,942
Записей в блоге: 27
25.08.2009, 16:12 #107
Для такого кода

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
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
' Простая арифметика
LET A1 = 10
LET A2 = 100
LET B1 = A1 + 5 * A2 - 50
PRINT B1 ' expected 460
 
' Неявные преобразования типов
LET A1 = 1 + 2.7
PRINT A1 ' expected 3
LET A1 = 2.7 + 1
PRINT A1 ' expected 3.7
 
' Ситуация, когда по тексту переменная сначала появляется в правой части
' присваивания, а потом влевой (т.е. типа якобы сначала использование,
' а потом запись). Однако по управлению (в run-time) всё нормально:
' сначала идёт запись, и только потом чтение
' Заодно и проверка работы IF'а
LET C = 0
WHILE C < 2 DO
  IF C = 1 THEN
    LET B = SHAMAN + 1 ' сюда попадаем только на второй итерации цикла
  ELSE
    LET SHAMAN = 999 ' а сюда - на первой
  END_IF
  LET C = C + 1
END_WHILE
PRINT SHAMAN ' expected 999
PRINT B      ' expected 1000
 
' Считаем сумму чисел от 1 до 100
LET COUNT=0
LET SUM=0
WHILE COUNT < 100 DO
  LET COUNT=COUNT+1
  LET SUM=SUM+COUNT
END_WHILE
PRINT COUNT ' expected 100
PRINT SUM   ' expected 5050
 
' Работа с одномерным массивом
DIM A(4)
LET C=0
WHILE C < 4 DO
  LET A(C+1) = (C+1) * (C+1)
  LET C = C + 1
END_WHILE
PRINT A(1) ' expected 1
PRINT A(2) ' expected 4
PRINT A(3) ' expected 9
PRINT A(4) ' expected 16
 
' Работа с многомерным массивом
DIM AA(2,3,4)
LET COUNT = 100
LET I1 = 0
WHILE I1 < 2 DO
  LET I2 = 0
  WHILE I2 < 3 DO
    LET I3 = 0
    WHILE I3 < 4 DO
      LET COUNT = COUNT + 1
      LET AA(I1+1,I2+1,I3+1) = COUNT
      LET I3 = I3 + 1
    END_WHILE
    LET I2 = I2 + 1
  END_WHILE
  LET I1 = I1 + 1
END_WHILE
PRINT COUNT     ' expected 124
PRINT AA(2,1,2) ' expected 114
 
' Операция сложения над строками
DIM R$(3)
LET R$(1) = "ERT"
LET R$(2) = "zxcvbnm"
LET R$(3) = "1234567"
LET A$ = ""
LET C = 0
WHILE C < 3 DO
  LET A$ = A$ + R$(C+1)
  LET C = C + 1
END_WHILE
PRINT A$
выдаём вот такой результат

Код
-> 460
-> 3
-> 3.700000
-> 999
-> 1000
-> 100
-> 5050
-> 1
-> 4
-> 9
-> 16
-> 124
-> 114
-> ERTzxcvbnm1234567
Добавлено через 8 минут
Вот ещё что забыл сказать. При работе в качестве интерпретирующей части можно повесить модуль, который вместо интерпретации будет генерить код на Си. Например, для исходника

PureBasic
1
2
LET A=B+1
PRINT A
На выходе будем получать код (условно говоря, реально есть свои ньюансы)

C
1
2
3
4
5
6
7
8
9
10
11
12
13
/* Это всё файлы, являющиеся исходниками интерпретатора */
#include "value.c"
#include "variable.c"
#include "interp.c"
 
var_Variable_t A;
var_Variable_t B;
 
main()
{
  A.vals[0] = val_ValueBinOp (VAL_OP_ADD, B.vals[0], val_CreateNumericInt (1));
  val_UserPrintValue (A.vals[0]);
}
Т.е. получается очень неоптимально написанный код, построенный на тех же структурах, на которых работает интерпретатор (чтобы два раза не писать). Но тем не менее код будет рабочим, дальше его моджо будет скомпилить и получить настоящий бинарник, который будет работать.

По такой технологии сейчас поддерживаются некоторые устаревшие языки программирования. Особенно у военных, которые ни за что не пойдут на то, чтобы переписать какие-то военные программы, написанные много лет назад, потому как писали их гении того времени, а потому в коде разобраться невозможно. И гораздо более надёжным вариантом оказывается написать интерпретатор или конвертер в Си
1
#pragma
Временно недоступен
952 / 223 / 6
Регистрация: 12.04.2009
Сообщений: 921
25.08.2009, 19:01  [ТС] #108
Вот про конвертер в Си немного не понял идею,как именно это будет работать.Мне теперь остаётся только наблюдать за твоим ходом написания,и потихоньку ковырять свой вариант.
0
Evg
Эксперт CАвтор FAQ
17941 / 6172 / 412
Регистрация: 30.03.2009
Сообщений: 16,942
Записей в блоге: 27
25.08.2009, 22:07 #109
Цитата Сообщение от #pragma Посмотреть сообщение
Вот про конвертер в Си немного не понял идею,как именно это будет работать.
Ну... для начала надо понять, как это на уровне промежуточного представлнеия выглядит. Далее понимаешь, как это представление интерпретируется, а дпльше попросту сгенерить код, который по большому счёту работает в терминах Value и Variable. Суть в том, что из исходника на одном языке получается исходник на другом. В идеальном варианте исходник на Си для того примера будет вот таким:

C
1
2
3
4
5
6
#include <stdio.h>
int A, B;
int main (void)
{
  A = B + 1;
}
Но для генерации "хорошего" исходника в сложных случаях придётся долго ковыряться. А "плохой" исходник генерится чуть ли не с полпинка. Но ты пока этим не заморачивайся. Просто имей в виду, что такое возможно. Со временем сам начнёшь понимать. Чем-то похожее состояние у тебя было в самом начале, когда тебе совсем непонятно было, как вообще можно сделать хоть что-то работающее, содержашие LET'ы, PRINT'ы, IF'ы. Но в какой-то момент ты это просёк и с ходу сделал сразу кучу всего. ВОт и здесь будет тоже самое: поначалу всё дико непонятно и сложно, а потом вдруг поймёшь и дальше всё попрёт. Просто здесь начальный период понимания будет побольше, ибо сама постановка задачи стала сложнее.

Это при том, что я себе совершенно чётко представлял, что должно быть, у меня ушло на это неделя времени. Так что твои два месяца - это вполне нормально. Как говорится "тяжело в учении..."

Цитата Сообщение от #pragma Посмотреть сообщение
Мне теперь остаётся только наблюдать за твоим ходом написания,и потихоньку ковырять свой вариант.
Ну... особо наблюдать больше не придётся Просто перед отпуском на работе резких телодвижений лучше не делать, а потому вот занялся интерпретатором. Да и если дальше меня не заломает, то следить там уже особо будет нечего - вся необходимая база уже вроде бы как отписана. Тебе нужно только понять принцип, по которому построено промежуточное представление и его интерпретация. Возможно не так просто будет разобрать тот низкоуровневый код на Си, когда привык работать с высокоуровневыми конструкциями типа списка и т.п.

Так что если есть вопросы - задавай, до вечера пятницы я ещё тут.

Добавлено через 1 час 18 минут
По поводу генерации кода на Си (Си++). Относительно совтояния твоих исходников из поста #86

Допустим, имеем исходник на бэйсике:

PureBasic
1
2
3
4
5
6
7
8
9
DIM INT A
DIM INT B
DIM INT C
LET A=10
LET B=11
LET C=A*A + B*B
PRINT "A=",A
PRINT "B=",B
PRINT "C=",C
А теперь в каталоге с твоими исходниками создадис вот такой вот файлик Си++

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
// Эта шапка необходима, чтобы исходник нормально компилился и слинковался
#include <iostream>
#include <fstream>
using std::ifstream;
using std::cout;
using std::endl;
using std::cerr;
ifstream source;
#include "parser.cpp"
#include "error_handler.cpp"
 
// А с этого места "полезный" код
#include "value_class.cpp"
#include "variable_class.cpp"
 
int main (void)
{
  // DIM INT A
  Variable A ("A", INT);
 
  // DIM INT B
  Variable B ("B", INT);
 
  // DIM INT C
  Variable C ("C", INT);
 
  // LET A=10
  A = Value("10",INT);
 
  // LET B=11
  B = Value("11",INT);
 
  // LET C=A*A + B*B
  C = (A.GetValue() * A.GetValue()) + (B.GetValue() * B.GetValue());
 
  // PRINT "A=",A
  cout << "A=" << A.GetValue() << endl;
 
  // PRINT "B=",B
  cout << "B=" << B.GetValue() << endl;
 
  // PRINT "C=",C
  cout << "C=" << C.GetValue() << endl;
 
  return 0;
}
Этот исходник отлично компиляется и получается бинарник. Который при исполнении печатает то же самое, что и интерпретатор

Код
$ g++ a.cc
$ ./a.out 
A=10
B=11
C=221
При этом, работая через промежуточное представлени (да и можно для такого простого исходника напрямую), твой интерпретатор сам может сгенерить этот самый код на Си++. В итоге получаешь некую систему, когда на входе имеешь текст на бэйсике, а на выходе имеешь бинарный файл. Код по скороксти получается неэффективный, но зато техническая реализация в таком виде в разы проще, чем писать собственный компилятор с бэйсика

Надеюсь, так понятнее. Если непонятно - забей. Если понятно - имей в виду. Такую хрень можно будет потом привинтить к интерпретатору. Можно конечно и сразу такое ваять, но сначала написать интерпретатор будет проще.
1
#pragma
Временно недоступен
952 / 223 / 6
Регистрация: 12.04.2009
Сообщений: 921
25.08.2009, 22:47  [ТС] #110
Да,это круто! Как я понял просто работаем с выходным файлом и записываем туда строки в зависимости от интерпретации файла на псевдо-бейсике.Затем можно дать команду типа system("g++ a.cc") (если в линукс,ну в другой среде можно команду поменять) и он скомпилируется на месте.
0
Evg
Эксперт CАвтор FAQ
17941 / 6172 / 412
Регистрация: 30.03.2009
Сообщений: 16,942
Записей в блоге: 27
25.08.2009, 23:33 #111
Ну как и что потом запустить - уже не важно. В общем суть главное понял. Это большой бонус удобства работы с промежуточным представлением. Хочешь - интерпретируй, хочешь - генери с него текст на Си\Си++, хочешь - можешь напрямую делать компиляцию (фактически формирование ассемблерного файла). Представление в процессе построения заведомо очищается от пользовательских ошибок и, в отличие от файла, по нему можно ходить взад и вперёд, в том числе и заниматься оптимизациями (типа "A*4" заменять на "A<<2" или "A+5+6" заменять на "A+11"). Так что после того, как ты поймёшь, что же это за зверь такой, у тебя опять появится оптимизм
1
#pragma
Временно недоступен
952 / 223 / 6
Регистрация: 12.04.2009
Сообщений: 921
29.08.2009, 08:35  [ТС] #112
Не знаю,зачем,но сделал проект на sourceforge.net - https://sourceforge.net/projects/basin/ (basin - Basic Interpreter),теперь у кого установлена Subversion,смогут просто скачать исходники командой
Код
svn co https://basin.svn.sourceforge.net/svnroot/basin basin
Там пока только та часть ветки,не затронутая изменениями с промежуточным представлением,то есть до того момента,как я начал добавлять файлы expression,statement и т.д.
Всё это дело вывешено под лицензией "BSD license",надеюсь возражений не будет? Просто хотел добавить опцию для скачивания,да и чтобы не лазить по теме в поисках исходников.Заодно снять лишний трафик с форума.Предлагаю держать исходники там.Можно попросить модераторов просто отредактировать первый пост,чтобы там были все ссылки относящиеся к данной теме.
Если,например,нужно просто посмотреть историю изменений,то сделать это можно отсюда http://basin.svn.sourceforge.net/viewvc/basin/
0
Evg
Эксперт CАвтор FAQ
17941 / 6172 / 412
Регистрация: 30.03.2009
Сообщений: 16,942
Записей в блоге: 27
16.09.2009, 09:22 #113
Цитата Сообщение от #pragma Посмотреть сообщение
Не знаю,зачем,но сделал проект на sourceforge.net
Оп-па. А это сообщение у меня почему-то не было отмечено, как непрочитанное

Добавлено через 16 минут
Уж коли пошло на то, что начал делать как серьёзную программу, то тебе надо бы удалить признак "exec" из исходников (т.е. часть файлов имеет права на исполнение). Видимо, ты пользуешься каким-то аналогом винlузового проводника, где это не видно

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

Пока особо не смотрел, но в бэйсиках вроде бы сделано так, что переменная без суффикса по умолчанию имеет плавающий тип, а у тебя целый. Но это мелочь

Добавлено через 6 минут
В value у тебя присуствуют поля типа int и long. Но для 32-битных машин как правило их размеры совпадают. Более аккуратно было бы в файле с настройками host'ового компилятора завести typedef'ы с именами тиа int32_t, int64_t и везде использовать их. И в начале исполнения программы поставить run-time контроль, что "assert (sizieof(int32_t) == 4); assert (sizieof(int64_t) == 8);". Аналогично для плавающих. Вроде бы как это мелочь, но такие вещи лучше делать сразу, чтобы потом в миллион местах не исправлять

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

Добавлено через 15 минут
И если у тебя есть какой-то набор тестовых ситуаций - для порядка его тоже можно под cvs положить (создать отдельный каталог и свалить туда). ТОже полезно, а то когда работаешь с одним файлом, добавляешь туда и удаляешь, то интересные тесты пропадают
1
#pragma
Временно недоступен
952 / 223 / 6
Регистрация: 12.04.2009
Сообщений: 921
16.09.2009, 12:58  [ТС] #114
Цитата Сообщение от Evg Посмотреть сообщение
Уж коли пошло на то, что начал делать как серьёзную программу, то тебе надо бы удалить признак "exec" из исходников (т.е. часть файлов имеет права на исполнение). Видимо, ты пользуешься каким-то аналогом винlузового проводника, где это не видно
Хм у меня права по умолчанию стояли и были drwxr-xr-x,ну я поменял их на drwxr--r-- а последний x не даёт убрать - тогда файлы в папке не видно,не знаю,с чем это связано :\
Цитата Сообщение от Evg Посмотреть сообщение
Ещё где-то для порядку надо описать, какие конструкции понимает твой интерпретатор. Чтобы хоть можно было поэкспериментировать и самому что-то написать. Я в репозитории храню исходник, в котором присутсвуют все образцы поддерживаемых конструкций
Да вроде кинул туда исходник,буду его обновлять
Цитата Сообщение от Evg Посмотреть сообщение
Пока особо не смотрел, но в бэйсиках вроде бы сделано так, что переменная без суффикса по умолчанию имеет плавающий тип, а у тебя целый. Но это мелочь
В той теме,что ты сделал про бейсики я прочитал что как раз int,но сменить не проблема,если что
Цитата Сообщение от Evg Посмотреть сообщение
Добавлено через 6 минут
В value у тебя присуствуют поля типа int и long. Но для 32-битных машин как правило их размеры совпадают. Более аккуратно было бы в файле с настройками host'ового компилятора завести typedef'ы с именами тиа int32_t, int64_t и везде использовать их. И в начале исполнения программы поставить run-time контроль, что "assert (sizieof(int32_t) == 4); assert (sizieof(int64_t) == 8);". Аналогично для плавающих. Вроде бы как это мелочь, но такие вещи лучше делать сразу, чтобы потом в миллион местах не исправлять
Я написал себе заметку сделать систему типов,но то что ты говоришь-я что-то слабо представляю,о чём это и где точно прописывается..У меня в-общем при печатании слова int в среде выпадает менюшка со всякими int32_t и прочими,но я не в курсе,что это.
Цитата Сообщение от Evg Посмотреть сообщение
Добавлено через 1 минуту
И сделай при интерпретации print'а чтобы энтеры рисовались, а то лепит всё в одну кучу - неудобно. Или как-то можно самому энтер напечатать?
Я прочитал про print-ы,там по-моему они не могут печатать EOL,но можно напечатать пустой print,и будет перевод строки.А если нужно пробел между значениями,то у меня можно напечатать строку с пробелом в качестве выражения .Формат записи: PRINT <выражение> [,] или [;] [<выражение>] а пустой print переводит строку.
Цитата Сообщение от Evg Посмотреть сообщение
Добавлено через 15 минут
И если у тебя есть какой-то набор тестовых ситуаций - для порядка его тоже можно под cvs положить (создать отдельный каталог и свалить туда). ТОже полезно, а то когда работаешь с одним файлом, добавляешь туда и удаляешь, то интересные тесты пропадают
Сделаю.

Добавлено через 26 минут
Напиши,какая маска прав в восьмеричном формате должна быть у папок и файлов,я поставлю...

Добавлено через 27 минут
Как мне это сделать вообще с правами,сначала все файлы удалить из репов,а потом опять закачать с новыми правами?В репозиторий руками же не залезешь.
0
Evg
Эксперт CАвтор FAQ
17941 / 6172 / 412
Регистрация: 30.03.2009
Сообщений: 16,942
Записей в блоге: 27
16.09.2009, 13:44 #115
Цитата Сообщение от #pragma Посмотреть сообщение
Я написал себе заметку сделать систему типов,но то что ты говоришь-я что-то слабо представляю,о чём это и где точно прописывается..У меня в-общем при печатании слова int в среде выпадает менюшка со всякими int32_t и прочими,но я не в курсе,что это.
Ну, грубо говоря проблема такая, что в языке Си не определён размер базовых типов. На каждой архитектуре он определяется по своему. Но в общей своей массе эти настройки совпадают. И тем не менее, где нужен размер типа какого-то фиксированного размера, заводят typedef. Например "typedef int int32_t; typedef long long int64_t" и везде в программе используют int32_t и int64_t. Если ты отнесёшь свою программу на архитектуру с другиминастройками базового типа, то тебе нужно просто в одном месте изменить typedef, всё остальное срастётся автоматически. Обычно все эти настроки делаются через систему конфигурации, но тебе пока можно этим не заморачиваться, просто принять к сведению и работать через ручкаминаписанный typedef

Цитата Сообщение от #pragma Посмотреть сообщение
Я прочитал про print-ы,там по-моему они не могут печатать EOL,но можно напечатать пустой print,и будет перевод строки.А если нужно пробел между значениями,то у меня можно напечатать строку с пробелом в качестве выражения .Формат записи: PRINT <выражение> [,] или [;] [<выражение>] а пустой print переводит строку.
Я уже плохо помню, но что-то в глубине памяти маячит, что "PRINT A" печатает A с переводом строки, а "PRINT A;" - без перевода. Но опять-таки не суть как делать, я у себя всегда принудительно влепил перевод строки, чтобы в процессе отладки глаза не ломать

Цитата Сообщение от #pragma Посмотреть сообщение
Напиши,какая маска прав в восьмеричном формате должна быть у папок и файлов,я поставлю...
Каталоги 755, файлы 644

Цитата Сообщение от #pragma Посмотреть сообщение
Как мне это сделать вообще с правами,сначала все файлы удалить из репов,а потом опять закачать с новыми правами?В репозиторий руками же не залезешь.
Интересный вопрос. Хз как это делать. Видимо надо читать мануал, с ходу и не скажу
0
#pragma
Временно недоступен
952 / 223 / 6
Регистрация: 12.04.2009
Сообщений: 921
16.09.2009, 14:06  [ТС] #116
Цитата Сообщение от Evg Посмотреть сообщение
Ну, грубо говоря проблема такая, что в языке Си не определён размер базовых типов. На каждой архитектуре он определяется по своему. Но в общей своей массе эти настройки совпадают. И тем не менее, где нужен размер типа какого-то фиксированного размера, заводят typedef. Например "typedef int int32_t; typedef long long int64_t" и везде в программе используют int32_t и int64_t. Если ты отнесёшь свою программу на архитектуру с другиминастройками базового типа, то тебе нужно просто в одном месте изменить typedef, всё остальное срастётся автоматически. Обычно все эти настроки делаются через систему конфигурации, но тебе пока можно этим не заморачиваться, просто принять к сведению и работать через ручкаминаписанный typedef
Я просто не пойму,чем мешает такой вариант
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
   // Type will be used for loops
   typedef int  count;
   // Type for counting symbols in program
   typedef long s_count;
 
   typedef char size1;// TODO Types system(basic,changeable etc.)
 
   typedef int  size2;
 
   typedef long size3;
 
   typedef unsigned long size4;
 
   typedef size2  stmtId;
 
   typedef vector <string>       str_Arr_t;
 
   typedef vector <size4>        sz4_Arr_t;
 
   typedef vector <sz4_Arr_t *>  sz4_MDim_t;
И почему важны номера 32 и 64 я ведь нигде на размеры не полагаюсь в программе?
P.S. А вообще у меня уже навроде паранойи насчёт типов везде где можно было поставил size3 для счётчиков привязанных к размерам векторов,потому что поглядел,какой у них max_size() на моей машине.
Насчёт репов-боюсь попортить репозиторий,поэтому можно попробовать сделать это постепенно,по одному,просто удаляя старые и добавляя новые переименованные файлы.Потом можно попробовать переименовать их обратно.
0
Evg
Эксперт CАвтор FAQ
17941 / 6172 / 412
Регистрация: 30.03.2009
Сообщений: 16,942
Записей в блоге: 27
16.09.2009, 14:28 #117
Просто я смотрел revision 16 (ибо 20 у меня не компилится), а там в классе Value используются int и long

32 и 64 необязательно (просто удобно), можно назвать venik и prjanik, лишь бы не использовать напрямую базовые типы (чтобы править нужно было в одном месте)
1
#pragma
Временно недоступен
952 / 223 / 6
Регистрация: 12.04.2009
Сообщений: 921
16.09.2009, 14:32  [ТС] #118
На что ругается компилятор?У меня всё ок,работает.Видимо,тут уже сказывается разность версий компиляторов?А-а,может надо makefile подправить,ты наверное только через командную строку работаешь?так я makefile только иногда обновляю,проверь
0
Evg
Эксперт CАвтор FAQ
17941 / 6172 / 412
Регистрация: 30.03.2009
Сообщений: 16,942
Записей в блоге: 27
16.09.2009, 14:39 #119
Цитата Сообщение от #pragma Посмотреть сообщение
На что ругается компилятор?У меня всё ок,работает.Видимо,тут уже сказывается разность версий компиляторов?
Не помню. На линковке вроде бы падало.

Вот такой код на исполнении должен зацикливаться, но он не зацикливается

Код
   LET B1 = 5
2:
   IF B1 THEN GOTO 2
   PRINT B1
а если меняем местами первые две строки

Код
2:
   LET B1 = 5
   IF B1 THEN GOTO 2
   PRINT B1
то зацикливается. Однако если внесём PRINT вовнутрь

Код
2:
   LET B1 = 5
   PRINT B1
   IF B1 THEN GOTO 2
То на печать ничего не выводится

Я глубоко не копался, просто мысль для такого теста появилась после того, как я увидел реализацию того, как ты IF делаешь. Я ожидал увидеть переполнение стека в процессе интерпретации (из-за возникающей рекурсии в твоей схеме), но тут похоже какой-то другой косяк
1
#pragma
Временно недоступен
952 / 223 / 6
Регистрация: 12.04.2009
Сообщений: 921
16.09.2009, 16:00  [ТС] #120
Да,косяк вроде в том,что при создании IF сначала проверяются (и создаются,конечно) его под-программы,а уже потом только сама инструкция IF,а это меняет глобальный порядок инструкций в программе.Выход-сначала создавать инструкции ,у которых есть свои подпрограммы "всухую",а уже потом создавать лист и проверять его на синтаксис,причем не меняя порядка следования,то есть сначала создаются все инструкции для THEN,а потом для ELSE,когда IF уже создан(ну в принципе это логично).А затем уже эти подпрограммы записывать в IF.Я ещё в начале заметил,что у меня в логах творится беспорядок: инструкции печатаются не по порядку,просто сначала не придал такого значения,что это баг.
Такой вариант должен исправить проблему,я полагаю.
Цитата Сообщение от Evg Посмотреть сообщение
Я глубоко не копался, просто мысль для такого теста появилась после того, как я увидел реализацию того, как ты IF делаешь. Я ожидал увидеть переполнение стека в процессе интерпретации (из-за возникающей рекурсии в твоей схеме), но тут похоже какой-то другой косяк
Ты видишь что неправильно реализован IF? Если я исправлю проблему, как описал выше,ты всё равно считаешь,что реализация неверная? Т.е. перегрузка стека неизбежна? Я считал,что будет верным делать подпрограммы для IF и WHILE,специально сделал функции интерпретации для закрытых блоков,надеясь что в будущем это поможет при составлении функций и т.п.

P.S.Я кажется понял,почему ты так подумал.Нет-нет,у меня GOTO работает только в runtime,то есть создание IF будет только 1 раз.
0
MoreAnswers
Эксперт
37091 / 29110 / 5898
Регистрация: 17.06.2006
Сообщений: 43,301
16.09.2009, 16:00
Привет! Вот еще темы с ответами:

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

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

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

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


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

Или воспользуйтесь поиском по форуму:
120
Yandex
Объявления
16.09.2009, 16:00
Закрытая тема Создать тему
Опции темы

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