aboutsummaryrefslogtreecommitdiff
path: root/parser.cpp
blob: cb8695f3f7fc44f3ad1c3ec2b0e859627d7d9487 (plain) (blame)
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
#include <cstdio>
#include <cctype>
#include <sstream>
#include "parser.h"
#include "exc.h"
#include "consts.h"
#include "builtin.h"

using std::stringstream;

static char buff[TOKEN_BUFF_SIZE];
static EvalObj *parse_stack[PARSE_STACK_SIZE];
extern Cons *empty_list;

Tokenizor::Tokenizor() : stream(stdin), buff_ptr(buff), escaping(false) {}
void Tokenizor::set_stream(FILE *_stream) {
    stream = _stream;
}

#define IS_NEWLINE(ch) \
    ((ch) == '\n')
#define IS_BRACKET(ch) \
    ((ch) == '(' || (ch) == ')') 
#define IS_SPACE(ch) \
    ((ch) == ' ' || (ch) == '\t' || IS_NEWLINE(ch))
#define IS_DELIMITER(ch) \
    (IS_BRACKET(ch) || IS_SPACE(ch))
#define IS_COMMENT(ch) \
    ((ch) == ';')
#define IS_QUOTE(ch) \
    ((ch) == '\"')
#define IS_SLASH(ch) \
    ((ch) == '\\')

#define POP \
    do { \
        *buff_ptr = 0; \
        ret = string(buff); \
        buff_ptr = buff; \
    } while (0)

bool Tokenizor::get_token(string &ret) {
    char ch;
    bool flag = false;
    while (fread(&ch, 1, 1, stream))
    {
        if (escaping)
        {
            escaping = false;
            switch (ch)
            {
                case '\\': *buff_ptr++ = '\\'; break;
                case '\"': *buff_ptr++ = '\"'; break;
                case 'n': *buff_ptr++ = '\n'; break;
                case 't': *buff_ptr++ = '\t'; break;
                default: {
                             buff_ptr = buff;
                             throw TokenError(string("") + ch, 
                                     PAR_ERR_ILLEGAL_CHAR_IN_ESC);
                         }
            }
        }
        else
        {
            bool in_quote = buff_ptr != buff && IS_QUOTE(*buff);
            if (buff_ptr != buff && 
                    (IS_BRACKET(*buff) || 
                     IS_DELIMITER(ch) ||
                     IS_COMMENT(ch) ||
                     IS_QUOTE(ch)))
            {
                if (IS_COMMENT(*buff))
                {
                    if (IS_NEWLINE(ch)) buff_ptr = buff;
                    else buff_ptr = buff + 1;
                }
                else if (!in_quote)
                {
                    POP;
                    flag = true;
                }
                else if (IS_QUOTE(ch))
                {
                    *buff_ptr++ = '\"';
                    POP;
                    return true;    // discard current slash
                }
            }
            if (in_quote || !IS_SPACE(ch)) 
            {
                if (in_quote && IS_SLASH(ch))
                    escaping = true;
                else 
                    *buff_ptr++ = ch;
            }
            if (flag) return true;
        }
    }
    if (buff_ptr != buff) POP;
    return false; // can't read more 
}

ASTGenerator::ASTGenerator() {}


EvalObj *ASTGenerator::to_obj(const string &str) {
    EvalObj *res = NULL;
    if ((res = IntNumObj::from_string(str)))
        return res;
    if ((res = RatNumObj::from_string(str)))
        return res;
    if ((res = RealNumObj::from_string(str)))
        return res;
    if ((res = CompNumObj::from_string(str)))
        return res;
    return new SymObj(str);
}
Cons *ASTGenerator::absorb(Tokenizor *tk) {
    EvalObj **top_ptr = parse_stack;
    for (;;)
    {
        if (top_ptr > parse_stack && *parse_stack)
            return new Cons(*(top_ptr - 1), empty_list);
        string token;
        if (!tk->get_token(token)) return NULL;
        if (token == "(")
            *top_ptr++ = NULL;  // Make the beginning of a new level
        else if (token == ")")
        {
            Cons *lst = empty_list;
            while (top_ptr >= parse_stack && *(--top_ptr))
            {
                Cons *_lst = new Cons(*top_ptr, lst); // Collect the list
                _lst->next = lst == empty_list ? NULL : lst;
                lst = _lst;
            }
            if (top_ptr < parse_stack)
                throw NormalError(READ_ERR_UNEXPECTED_RIGHT_BRACKET);
            *top_ptr++ = lst;
        }
        else *top_ptr++ = ASTGenerator::to_obj(token);
    }
}