summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorMike Buland <mike@xagasoft.com>2013-04-22 13:05:22 -0600
committerMike Buland <mike@xagasoft.com>2013-04-22 13:05:22 -0600
commit2909f50d008920568f0e50da760b266388ccc124 (patch)
tree6789c162a2b950c2006c944e9d21e6ed9bda7069 /src
parentd7ccd9c4d8e5a5bb4f12b36b3e4ad3105c5a9317 (diff)
downloadclic-2909f50d008920568f0e50da760b266388ccc124.tar.gz
clic-2909f50d008920568f0e50da760b266388ccc124.tar.bz2
clic-2909f50d008920568f0e50da760b266388ccc124.tar.xz
clic-2909f50d008920568f0e50da760b266388ccc124.zip
There is now a parser & calculator interface.
Diffstat (limited to 'src')
-rw-r--r--src/lexer.cpp163
-rw-r--r--src/lexer.h32
-rw-r--r--src/main.cpp16
-rw-r--r--src/parser.cpp266
-rw-r--r--src/parser.h35
-rw-r--r--src/token.cpp70
-rw-r--r--src/token.h47
7 files changed, 625 insertions, 4 deletions
diff --git a/src/lexer.cpp b/src/lexer.cpp
new file mode 100644
index 0000000..834d3bc
--- /dev/null
+++ b/src/lexer.cpp
@@ -0,0 +1,163 @@
1#include "lexer.h"
2#include "token.h"
3#include "number.h"
4
5#include <bu/sio.h>
6
7Lexer::Lexer( Bu::Stream &rIn ) :
8 rIn( rIn ),
9 iBufPos( 0 ),
10 iScale( 0 ),
11 iRadix( 10 ),
12 numRangeTop('9'),
13 ascRangeTop(0)
14{
15}
16
17Lexer::~Lexer()
18{
19}
20
21Token Lexer::nextToken()
22{
23 for(;;)
24 {
25 if( iBufPos >= sBuf.getSize() )
26 {
27 iBufPos = -1;
28 return Token( Token::tEndOfLine );
29 }
30
31 if( iBufPos < 0 )
32 {
33 if( rIn.isEos() )
34 return Token( Token::tEndOfInput );
35
36 sBuf = rIn.readLine();
37 if( sBuf.getSize() == 0 )
38 {
39 iBufPos = -1;
40 continue;
41 }
42 iBufPos = 0;
43 }
44
45 //Bu::println("Testing char '%1' at %2").arg( sBuf[iBufPos] ).arg( iBufPos );
46 switch( sBuf[iBufPos] )
47 {
48 case ' ':
49 case '\t':
50 iBufPos++;
51 break;
52
53 case '\\':
54 {
55 Bu::String *sTmp = new Bu::String();
56 for( iBufPos++; iBufPos < sBuf.getSize() &&
57 sBuf[iBufPos] != ' ' && sBuf[iBufPos] != '\t';
58 iBufPos++ )
59 {
60 sTmp->append( sBuf[iBufPos] );
61 }
62 return Token( Token::tCommand, sTmp );
63 }
64 break;
65
66 case '+':
67 iBufPos++;
68 return Token( Token::tPlus );
69
70 case '-':
71 iBufPos++;
72 return Token( Token::tMinus );
73
74 case '*':
75 iBufPos++;
76 return Token( Token::tMultiply );
77
78 case '/':
79 iBufPos++;
80 return Token( Token::tDivide );
81
82 case '(':
83 iBufPos++;
84 return Token( Token::tOpenParen );
85
86 case ')':
87 iBufPos++;
88 return Token( Token::tCloseParen );
89
90 default:
91 {
92 Bu::String *sTmp = new Bu::String();
93 if( (sBuf[iBufPos] >= '0' &&
94 sBuf[iBufPos] <= numRangeTop) ||
95 (sBuf[iBufPos] >= 'a' &&
96 sBuf[iBufPos] <= ascRangeTop) ||
97 sBuf[iBufPos] == '.' )
98 {
99 for( ; iBufPos < sBuf.getSize() ; iBufPos++ )
100 {
101 if( (sBuf[iBufPos] >= '0' &&
102 sBuf[iBufPos] <= numRangeTop) ||
103 (sBuf[iBufPos] >= 'a' &&
104 sBuf[iBufPos] <= ascRangeTop) ||
105 sBuf[iBufPos] == '.' )
106 {
107 sTmp->append( sBuf[iBufPos] );
108 }
109 else
110 {
111 break;
112 }
113 }
114 Number *n = new Number( *sTmp, iScale, iRadix );
115 delete sTmp;
116 return Token( Token::tNumber, n );
117 }
118 else if( (sBuf[iBufPos]>=(ascRangeTop+1) && sBuf[iBufPos]<='z') ||
119 (sBuf[iBufPos]>='A' && sBuf[iBufPos]<='Z') ||
120 sBuf[iBufPos] == '_' )
121 {
122 for( ; iBufPos < sBuf.getSize(); iBufPos++ )
123 {
124 if( (sBuf[iBufPos]>='a' && sBuf[iBufPos]<='z') ||
125 (sBuf[iBufPos]>='A' && sBuf[iBufPos]<='Z') ||
126 (sBuf[iBufPos]>='0' && sBuf[iBufPos]<='9') ||
127 sBuf[iBufPos] == '_' )
128 {
129 sTmp->append( sBuf[iBufPos] );
130 }
131 else
132 {
133 break;
134 }
135 }
136 return Token( Token::tString, sTmp );
137 }
138 else
139 {
140 sBuf.clear();
141 Bu::println("Invalid character discovered!");
142 }
143 }
144 break;
145 }
146 }
147}
148
149void Lexer::setRadix( int i )
150{
151 iRadix = i;
152 if( iRadix <= 10 )
153 {
154 numRangeTop = '0'+iRadix-1;
155 ascRangeTop = 0;
156 }
157 else
158 {
159 numRangeTop = '9';
160 ascRangeTop = 'a'+iRadix-11;
161 }
162}
163
diff --git a/src/lexer.h b/src/lexer.h
new file mode 100644
index 0000000..4e6d73d
--- /dev/null
+++ b/src/lexer.h
@@ -0,0 +1,32 @@
1#ifndef LEXER_H
2#define LEXER_H
3
4#include <bu/stream.h>
5
6class Token;
7
8class Lexer
9{
10public:
11 Lexer( Bu::Stream &rIn );
12 virtual ~Lexer();
13
14 Token nextToken();
15
16 int getScale() const { return iScale; }
17 void setScale( int i ) { iScale = i; }
18
19 int getRadix() const { return iRadix; }
20 void setRadix( int i );
21
22private:
23 Bu::Stream &rIn;
24 Bu::String sBuf;
25 int iBufPos;
26 int iScale;
27 int iRadix;
28 char numRangeTop;
29 char ascRangeTop;
30};
31
32#endif
diff --git a/src/main.cpp b/src/main.cpp
index d1b9d1c..dec7867 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -1,9 +1,13 @@
1#include "number.h" 1#include "number.h"
2#include "packedintarray.h" 2#include "packedintarray.h"
3#include "lexer.h"
4#include "token.h"
5#include "parser.h"
3#include <math.h> 6#include <math.h>
4#include <sys/time.h> 7#include <sys/time.h>
5 8
6#include <bu/sio.h> 9#include <bu/sio.h>
10#include <bu/streamstack.h>
7using namespace Bu; 11using namespace Bu;
8 12
9void packedtest1() 13void packedtest1()
@@ -361,12 +365,16 @@ int main( int , char *[] )
361 println("CliC"); 365 println("CliC");
362 366
363// packedtest1(); 367// packedtest1();
364 numbertest1(); 368// numbertest1();
365 numbertestcomp(); 369// numbertestcomp();
366 radixtest(); 370// radixtest();
367 fractest(); 371// fractest();
368// ordertest(); 372// ordertest();
369 373
374 Lexer lex( sioRaw );
375 Parser parser( lex, sioRaw );
376 parser.parse();
377
370 return 0; 378 return 0;
371} 379}
372 380
diff --git a/src/parser.cpp b/src/parser.cpp
new file mode 100644
index 0000000..21ccc11
--- /dev/null
+++ b/src/parser.cpp
@@ -0,0 +1,266 @@
1#include "parser.h"
2#include "lexer.h"
3#include "number.h"
4
5#include <bu/sio.h>
6
7Parser::Parser( Lexer &lex, Bu::Stream &rOut ) :
8 lex( lex ),
9 rOut( rOut )
10{
11}
12
13Parser::~Parser()
14{
15}
16
17void Parser::parse()
18{
19 for(;;)
20 {
21 Token t = lex.nextToken();
22 switch( t.eType )
23 {
24 case Token::tEndOfInput:
25 return;
26
27 case Token::tEndOfLine:
28 unwind();
29 if( !tsTerminal.isEmpty() )
30 {
31 Bu::println( rOut, "%1").arg( *tsTerminal.peek().nVal );
32 }
33 tsTerminal.clear();
34 break;
35
36 case Token::tCommand:
37 if( *t.sVal == "exit" || *t.sVal == "quit" )
38 return;
39 else if( *t.sVal == "scale" )
40 {
41 Token t2 = lex.nextToken();
42 if( t2.eType == Token::tEndOfLine )
43 {
44 Bu::println( rOut, "Current scale: %1").
45 arg( lex.getScale() );
46 }
47 else if( t2.eType == Token::tNumber )
48 {
49 int32_t i = t2.nVal->toInt32();
50 lex.setScale( i );
51 if( i < 0 )
52 {
53 Bu::println( rOut, "ERROR: You must provide a "
54 "positive integer or zero as the parameter "
55 "to scale.");
56 }
57 else
58 {
59 Bu::println( rOut, "Scale changed to: %1").
60 arg( lex.getScale() );
61 }
62 }
63 else
64 {
65 Bu::println( rOut, "ERROR: You must provide a number "
66 "as the parameter to scale.");
67 }
68 }
69 else if( *t.sVal == "radix" )
70 {
71 Token t2 = lex.nextToken();
72 if( t2.eType == Token::tEndOfLine )
73 {
74 Bu::println( rOut, "Current radix: %1").
75 arg( lex.getRadix() );
76 }
77 else if( t2.eType == Token::tNumber )
78 {
79 int32_t i = t2.nVal->toInt32();
80 if( i < 2 || i > 36 )
81 Bu::println( rOut, "ERROR: Radix must be between "
82 "2 and 36 inclusive");
83 else
84 {
85 lex.setRadix( i );
86 Bu::println( rOut, "Radix changed to: %1").
87 arg( lex.getRadix() );
88 }
89 }
90 else
91 {
92 Bu::println( rOut, "You must provide a number as "
93 "the parameter to radix.");
94 }
95 }
96 else
97 {
98 Bu::println( rOut, "ERROR: Unknown command '%1'").
99 arg( *t.sVal );
100 }
101 break;
102
103 case Token::tNumber:
104 tsTerminal.push( t );
105 break;
106
107 default:
108 if( tsNonTerminal.getSize() == 0 ||
109 getPriority( tsNonTerminal.peek().eType ) <=
110 getPriority( t.eType ) )
111 {
112// Bu::println("Pushing non-terminal: %1").arg( t.eType );
113 tsNonTerminal.push( t );
114
115// for( TokenStack::iterator i = tsTerminal.begin(); i; i++ ) Bu::print(" [%1]").arg( *(*i).nVal ); Bu::println("");
116// for( TokenStack::iterator i = tsNonTerminal.begin(); i; i++ ) Bu::print(" <%1>").arg( (*i).eType ); Bu::println("");
117 }
118 else
119 {
120// Bu::println("Unwinding stack before pushing: %1").arg( t.eType );
121 unwind();
122 tsNonTerminal.push( t );
123// for( TokenStack::iterator i = tsTerminal.begin(); i; i++ ) Bu::print(" [%1]").arg( *(*i).nVal ); Bu::println("");
124// for( TokenStack::iterator i = tsNonTerminal.begin(); i; i++ ) Bu::print(" <%1>").arg( (*i).eType ); Bu::println("");
125 }
126 break;
127 }
128 }
129}
130
131void Parser::unwind()
132{
133 for(;;)
134 {
135// for( TokenStack::iterator i = tsTerminal.begin(); i; i++ ) Bu::print(" [%1]").arg( *(*i).nVal ); Bu::println("");
136// for( TokenStack::iterator i = tsNonTerminal.begin(); i; i++ ) Bu::print(" <%1>").arg( (*i).eType ); Bu::println("");
137 if( tsNonTerminal.isEmpty() )
138 return;
139
140 if( tsTerminal.getSize() < reqTokens( tsNonTerminal.peek().eType ) )
141 {
142 return;
143 }
144
145 Token t = tsNonTerminal.peekPop();
146 switch( t.eType )
147 {
148 case Token::tPlus:
149 {
150 Token b = tsTerminal.peekPop();
151 Token a = tsTerminal.peekPop();
152 tsTerminal.push(
153 Token( Token::tNumber, new Number( *a.nVal + *b.nVal ) )
154 );
155 }
156 break;
157
158 case Token::tMinus:
159 {
160 Token b = tsTerminal.peekPop();
161 Token a = tsTerminal.peekPop();
162 tsTerminal.push(
163 Token( Token::tNumber, new Number( *a.nVal - *b.nVal ) )
164 );
165 }
166 break;
167
168 case Token::tMultiply:
169 {
170 Token b = tsTerminal.peekPop();
171 Token a = tsTerminal.peekPop();
172 tsTerminal.push(
173 Token( Token::tNumber, new Number( *a.nVal * *b.nVal ) )
174 );
175 }
176 break;
177
178 case Token::tDivide:
179 {
180 Token b = tsTerminal.peekPop();
181 Token a = tsTerminal.peekPop();
182 tsTerminal.push(
183 Token( Token::tNumber, new Number( *a.nVal / *b.nVal ) )
184 );
185 }
186 break;
187
188 case Token::tOpenParen:
189 tsNonTerminal.push( t );
190 return;
191
192 case Token::tCloseParen:
193 unwind();
194 if( tsNonTerminal.peek().eType == Token::tOpenParen )
195 {
196
197 tsNonTerminal.pop();
198 }
199 else
200 {
201 throw Bu::ExceptionBase("Close paren found without open paren.");
202 }
203 break;
204
205 case Token::tNumber:
206 case Token::tString:
207 case Token::tCommand:
208 case Token::tEndOfLine:
209 case Token::tEndOfInput:
210 // These should never show up at all
211 break;
212 }
213 }
214}
215
216int Parser::reqTokens( Token::Type eType )
217{
218 switch( eType )
219 {
220 case Token::tPlus:
221 case Token::tMinus:
222 case Token::tDivide:
223 case Token::tMultiply:
224 return 2;
225
226 case Token::tOpenParen:
227 return 0;
228
229 case Token::tCloseParen:
230 return 1;
231
232 default:
233 return 0;
234 }
235}
236
237int Parser::getPriority( Token::Type eType )
238{
239 switch( eType )
240 {
241 case Token::tNumber:
242 case Token::tString:
243 case Token::tCommand:
244 return 0;
245
246 case Token::tPlus:
247 case Token::tMinus:
248 return 1;
249
250 case Token::tDivide:
251 case Token::tMultiply:
252 return 2;
253
254 case Token::tOpenParen:
255 case Token::tCloseParen:
256 return 3;
257
258 case Token::tEndOfLine:
259 case Token::tEndOfInput:
260 return -1;
261
262 default:
263 throw Bu::ExceptionBase("Invalid type in getPriority");
264 }
265}
266
diff --git a/src/parser.h b/src/parser.h
new file mode 100644
index 0000000..5563613
--- /dev/null
+++ b/src/parser.h
@@ -0,0 +1,35 @@
1#ifndef PARSER_H
2#define PARSER_H
3
4#include <bu/list.h>
5#include "token.h"
6
7namespace Bu
8{
9 class Stream;
10};
11
12class Lexer;
13
14class Parser
15{
16public:
17 Parser( Lexer &lex, Bu::Stream &rOut );
18 virtual ~Parser();
19
20 void parse();
21
22private:
23 void unwind();
24 int reqTokens( Token::Type eType );
25 int getPriority( Token::Type eType );
26
27private:
28 Lexer &lex;
29 Bu::Stream &rOut;
30 typedef Bu::List<Token> TokenStack;
31 TokenStack tsTerminal;
32 TokenStack tsNonTerminal;
33};
34
35#endif
diff --git a/src/token.cpp b/src/token.cpp
new file mode 100644
index 0000000..d7fbe88
--- /dev/null
+++ b/src/token.cpp
@@ -0,0 +1,70 @@
1#include "token.h"
2
3#include "number.h"
4#include <bu/formatter.h>
5#include <bu/string.h>
6
7Token::Token( Type eType ) :
8 eType( eType ),
9 sVal( 0 )
10{
11}
12
13Token::Token( Type eType, Bu::String *s ) :
14 eType( eType ),
15 sVal( s )
16{
17}
18
19Token::Token( Type eType, Number *n ) :
20 eType( eType ),
21 nVal( n )
22{
23}
24
25Token::Token( const Token &rSrc ) :
26 eType( rSrc.eType ),
27 sVal( rSrc.sVal )
28{
29 Token &rMod = const_cast<Token &>(rSrc);
30 rMod.sVal = 0;
31}
32
33Token::~Token()
34{
35 switch( eType )
36 {
37 case tNumber:
38 delete nVal;
39 break;
40
41 case tString:
42 case tCommand:
43 delete sVal;
44 break;
45
46 default:
47 break;
48 }
49}
50
51Bu::Formatter &operator<<( Bu::Formatter &f, Token::Type eType )
52{
53 switch( eType )
54 {
55 case Token::tNumber: return f << "num";
56 case Token::tString: return f << "str";
57 case Token::tCommand: return f << "cmd";
58 case Token::tPlus: return f << "+";
59 case Token::tMinus: return f << "-";
60 case Token::tDivide: return f << "/";
61 case Token::tMultiply: return f << "*";
62 case Token::tOpenParen: return f << "(";
63 case Token::tCloseParen: return f << ")";
64 case Token::tEndOfLine: return f << "eol";
65 case Token::tEndOfInput: return f << "eoi";
66
67 default: return f << "???";
68 }
69}
70
diff --git a/src/token.h b/src/token.h
new file mode 100644
index 0000000..3b5caff
--- /dev/null
+++ b/src/token.h
@@ -0,0 +1,47 @@
1#ifndef TOKEN_H
2#define TOKEN_H
3
4class Number;
5namespace Bu
6{
7 class String;
8 class Formatter;
9};
10
11class Token
12{
13public:
14 enum Type
15 {
16 tNumber,
17 tString,
18 tCommand,
19 tPlus,
20 tMinus,
21 tDivide,
22 tMultiply,
23 tOpenParen,
24 tCloseParen,
25
26 tEndOfLine,
27
28 tEndOfInput
29 };
30
31 Token( Type eType );
32 Token( Type eType, Bu::String *s );
33 Token( Type eType, Number *n );
34 Token( const Token &rSrc );
35 ~Token();
36
37 Type eType;
38 union
39 {
40 Bu::String *sVal;
41 class Number *nVal;
42 };
43};
44
45Bu::Formatter &operator<<( Bu::Formatter &f, Token::Type eType );
46
47#endif