-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathJavaTokenizer.py
More file actions
250 lines (217 loc) · 8.1 KB
/
Copy pathJavaTokenizer.py
File metadata and controls
250 lines (217 loc) · 8.1 KB
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
#######
#@Jonathan Ke
#@9/23/2019
#########
#Parses and converts Java code into list of tokens following description given by
#https://www.cs.cmu.edu/~pattis/15-1XX/15-200/lectures/tokens/lecture.html
#The following algorithm is implemented:
'''
The first phase, a Java compiler tokenizes a program by scanning its characters left to right,
top to bottom (there is a hidden end-of-line character at the end of each line; recall that it
is equivalent to white space), and combining selected characters into tokens. It works by repeating
the following algorithm (an algorithm is a precise set of instructions):
Skip any white space...
...if the next character is an underscore, dollar, or alphabetic character, it builds an identifier token.
Except for recognizing keywords and certain literals (true, false, null) which all share the form of
identifiers, but are not themselves identifiers
...if the next character is a numeric character, ' or ", it builds a literal token.
...if the next character is a period, that is a seperator unless the character after it is a numeric character (in
which case it builds a double literal).
...if the next two characters are a // or /* starting a comment, it builds a comment token.
...if the next character is anything else, it builds a separator or operator token (trying
to build the longest token, given that white space separates tokens, except in a char or String literal).
Recall that white space (except when inside a textual literal or comment) separates tokens.
Also, the Java compiler uses the "longest token rule": it includes characters in a token until it reaches a
character that cannot be included.
Finally, after building and recognizing each token, the Java compiler passes all tokens (except for comments,
which are ignored after being tokenized) on to the next phase of the compiler.
'''
from Token import *
from JavaLang import JavaLang
def tokenize(code):
'''str -> [Token]
Creates a list of java tokens from input code
'''
out = []
i = 0
while i < len(code):
if code[i].isspace():
i += 1
elif code[i] in '_$' or code[i].isalpha():
#identifiers, keywords, true, false, null
token = getNextWord(code[i:])
if token in {'true','false','null'}:
out.append(Literal(token))
elif token in JavaLang.keywords:
out.append(Keyword(token))
else:
out.append(Identifier(token))
i += len(token)
elif code[i].isnumeric() or code[i] == '"' or code[i] == "'": #literals
token = getLiteral(code[i:])
out.append(Literal(token))
i += len(token)
elif code[i:].startswith('//'): #comments
if '\n' in code[i:]:
endIndex = code.index('\n',i)
else:
endIndex = len(code)
i = endIndex
elif code[i:].startswith('/*'): #comments
term = '*/'
endIndex = code.find(term,i)
if endIndex == -1:
raise CreateException('Comment not tokenizable', 'Check your comment ended!')
i = endIndex
elif code[i] in JavaLang.separators: #separators
out.append(Separator(code[i]))
i += 1
else: #operators
token = getOperator(code,i)
out.append(Operator(token))
i += len(token)
return out
def getOperator(s, i):
#parses out an operator
if s[i:i+3] in JavaLang.operators:
return s[i:i+3]
elif s[i:i+2] in JavaLang.operators:
return s[i:i+2]
elif s[i] in JavaLang.operators:
return s[i]
raise CreateException("OperatorNotFoundException", s[i:i+3] +" not recognized")
def peek(s, i):
#light peeking function, handles index exceptions
if i >= len(s):
return None
else:
return s[i]
def getLiteral(s):
#parses literals
if s[0] == '"': #string literals
return getString(s, '"')
elif s[0] == "'": #char literals
return getString(s, "'")
else: #double, int, and long literals
return getNum(s)
def getNextWord(s):
#parses identifiers
'''str -> str
Gets the nearest word'''
for i in range(len(s)):
if not (s[i].isalnum() or s[i] == '_'):
return s[:i]
raise CreateException("WordNotFoundException", "Could not parse word")
def getString(s, char):
'''str -> str
Parses first Java string from input'''
out = char
i = 1
while i < len(s):
if s[i] == '\\':
out += s[i:i+2]
i+=2
elif s[i] == char:
return out + char
else:
out += s[i]
i+= 1
raise CreateException("STRINGNOTFOUNDEXCEPTION",
"Could not parse string") #for debugging purposes
def getNum(s):
#parses numerical literals
for i in range(len(s)):
if not s[i].isnumeric() and s[i] != '.':
return s[:i]
raise CreateException("NumberNotFoundException", "Could not parse number")
##Object Classes necessary for errors and using code
#######################################################
class CreateException(Exception):
def __init__(self, exp, message):
self.expression = exp
self.message = message
#Test functions
#######################################################
def testTokenize():
inputStr0 = '''
import java.util.Scanner;
import java.io.File;
public class PrintMyself{
public static void main(String[] args) throws Exception{
Scanner con = new Scanner(new File("PrintMyself.java"));
while (con.hasNextLine()){
System.out.println(con.nextLine());
byte b = 'a';
}
}
/**
*
* dsjkaldjksjlajdk
*/
//Obsdjfbwodfnjsnvlknffenvljnwfjlvnefjlbnefjlfvnerwkfidsodjfnakdnkasnvnfovoae
}
'''
#The following test is pulled from the website below:
#https://www.cs.cmu.edu/~pattis/15-1XX/15-200/lectures/tokens/lecture.html
inputStr1 = '''
import java.util.Scanner;
import java.io.File;
public class PrintMyself{
public static void main(String[] args) throws Exception{
Scanner con = new Scanner(new File("PrintMyself.java"));
while (con.hasNextLine()){
System.out.println(con.nextLine());
byte b = 'a';
a = -1;
}
}
/**
*
* dsjkaldjksjlajdk
*/
//Obsdjfbwodfnjsnvlknffenvljnwfjlvnefjlbnefjlfvnerwkfidsodjfnakdnkasnvnfovoae
}
'''
inputStr2 = '''
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
//
// Description:
//
// This program computes the time it take to drop an object (in a vacuum)
// form an arbitrary height in an arbitrary gravitational field (so it can
// be used to calculate drop times on other planets). It models a straight
// input/calculate/output program: the user enters the gravitation field
// and then the height; it calculates thd drop time and then prints in on
// the console.
//
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
import edu.cmu.cs.pattis.cs151xx.Prompt;
public class Application {
public static void main(String[] args)
{
try {
double gravity; //meter/sec/sec
double height; //meters
double time; //sec
//Input
gravity = Prompt.forDouble("Enter gravitational acceleration (in meters/sec/sec)");
height = Prompt.forDouble("Enter height of drop (in meters)");
//Calculate
time = Math.sqrt(2.*height/gravity);
//Output
System.out.println("\nDrop time = " + time + " secs");
}catch (Exception e) {
e.printStackTrace();
System.out.println("main method in Application class terminating");
System.exit(0);
}
}
'''
print("#############START###########")
out = tokenize(inputStr1)
for L in out:
print(L)
print("############END#############")
#testTokenize()