/*
PseudoCode Interpreted Language (PCIL): Part of the algoviz@vt collection of algorithm visualizations.
Copyright (C) 2008 Brandon Malone, Frank Hadlock
This file is part of the PseudoCode Interpreted Language.
PseudoCode Interpreted Language is free software: you can redistribute
it and/or modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of the License,
or (at your option) any later version.
PseudoCode Interpreted Language is distributed in the hope that it will
be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
Public License for more details.
You should have received a copy of the GNU General Public License along with
PseudoCode Interpreted Language. If not, see
.
*/
/*
* Analyzer.java
*
* Created on February 25, 2008, 11:05 PM
*
* To change this template, choose Tools | Template Manager
* and open the template in the editor.
*/
package lex;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashMap;
/**
*
* @author Brandon
*/
public class Analyzer {
public enum State {
Start,
StringConstant,
IntegerConstant,
RealConstant,
VariableName
}
public State currentState;
// maps from keyword in source to lexicalValue
public HashMap keywords;
// maps from operator in source to lexicalValue
public HashMap operators;
// maps from constant in source to sourceValue; the lexicalValue is "constant"
public HashMap constants;
// contains all white space characters
public ArrayList whiteSpace;
// the processed part of the source
private ArrayList sourceTokens;
// the source code
private String source;
// current position in the source
private int currentIndex;
// built-up string
private StringBuilder buffer;
/** Creates a new instance of Analyzer */
public Analyzer() {
currentState = State.Start;
this.initializeKeywords();
this.initializeOperators();
this.initializeConstants();
this.initializeWhiteSpace();
}
private void initializeKeywords() {
keywords = new HashMap();
keywords.put("=", "=");
keywords.put(",", ",");
keywords.put("input", "input");
keywords.put("initialize", "initialize");
keywords.put("as", "as");
keywords.put("if", "if");
keywords.put("then", "then");
keywords.put("else", "else");
keywords.put("endIf", "endIf");
keywords.put("while", "while");
keywords.put("endWhile", "endWhile");
keywords.put("forEach", "forEach");
keywords.put("in", "in");
keywords.put("next", "next");
keywords.put("return", "return");
keywords.put("function","function");
keywords.put("endFunction","endFunction");
keywords.put("call", "call");
keywords.put("say", "say");
keywords.put("true", "constant");
keywords.put("false", "constant");
keywords.put("finish", "finish");
keywords.put("and", "and");
keywords.put("or", "or");
keywords.put("ask", "ask");
keywords.put("*","*/");
keywords.put("/","*/");
keywords.put("%", "*/");
keywords.put("+","+-");
keywords.put("-","+-");
keywords.put("(","(");
keywords.put(")",")");
keywords.put("!=", "relationalOperator");
keywords.put("==", "relationalOperator");
keywords.put(">","relationalOperator");
keywords.put("gt","relationalOperator");
keywords.put("gte","relationalOperator");
keywords.put("<","relationalOperator");
keywords.put("lt","relationalOperator");
keywords.put("lte","relationalOperator");
keywords.put(".",".");
keywords.put(":", ":");
// newLine is most like an operator
keywords.put("\n", "newLine");
}
private void initializeOperators() {
operators = new HashMap();
operators.put("*","*/");
operators.put("/","*/");
operators.put("%", "*/");
operators.put("+","+-");
operators.put("-","+-");
operators.put("(","(");
operators.put(")",")");
operators.put("!=", "relationalOperator");
operators.put("==", "relationalOperator");
operators.put(">","relationalOperator");
operators.put("gt","relationalOperator");
operators.put("gte","relationalOperator");
operators.put("<","relationalOperator");
operators.put("lt","relationalOperator");
operators.put("lte","relationalOperator");
operators.put(".",".");
operators.put(":", ":");
operators.put("call", "call");
// newLine is most like an operator
operators.put("\n", "newLine");
operators.put("=", "=");
operators.put(",", ",");
operators.put("input", "input");
operators.put("initialize", "initialize");
operators.put("as", "as");
operators.put("if", "if");
operators.put("then", "then");
operators.put("else", "else");
operators.put("endIf", "endIf");
operators.put("while", "while");
operators.put("endWhile", "endWhile");
operators.put("forEach", "forEach");
operators.put("in", "in");
operators.put("next", "next");
operators.put("return", "return");
operators.put("function","function");
operators.put("endFunction","endFunction");
operators.put("say", "say");
operators.put("true", "constant");
operators.put("false", "constant");
operators.put("finish", "finish");
operators.put("or", "or");
operators.put("and", "and");
operators.put("ask", "ask");
}
private void initializeConstants() {
constants = new HashMap();
constants.put("PI", "3.1415");
constants.put("EXP", "2.71828");
}
private void initializeWhiteSpace() {
whiteSpace = new ArrayList();
whiteSpace.add(" ");
whiteSpace.add("\t");
}
public ArrayList analyzeSourceFile(String sourceFilePath) throws IOException {
// open the source file
BufferedReader br= new BufferedReader(new FileReader(sourceFilePath));
// read the source
String source = readSourceFile(br);
// close the source file
br.close();
// analyze the source text
ArrayList tokens = analyze(source);
return tokens;
}
public ArrayList analyze(String sourceFilePath, String tokenFilePath) throws IOException {
// open the source file
BufferedReader br= new BufferedReader(new FileReader(sourceFilePath));
// read the source
String source = readSourceFile(br);
// close the source file
br.close();
// analyze the source text
ArrayList tokens = analyze(source);
// open token file
PrintWriter pw = new PrintWriter(new BufferedWriter(new FileWriter(tokenFilePath)));
// write to the token file
writeTokenFile(pw, tokens);
// close the token file
pw.close();
return tokens;
}
private String readSourceFile(BufferedReader br) throws IOException {
StringBuilder str = new StringBuilder();
String line = br.readLine();
while(line != null) {
if (line.length() > 0) {
str.append(line);
str.append("\n");
}
line = br.readLine();
}
str.append("\n");
return str.toString();
}
private void writeTokenFile(PrintWriter pw, ArrayList tokens) throws IOException {
// write each of the tokens to the file
for(SourceToken token : tokens) {
pw.println(token.toString());
}
}
public ArrayList analyze(String source) {
// analyze the provided source
this.source = source;
// start at the beginning of the source
this.currentIndex = 0;
// reset the state
this.currentState = State.Start;
// clear memory buffer
this.buffer = new StringBuilder();
// reset the source tokens
sourceTokens = new ArrayList();
// and analyze
while (this.currentIndex < source.length()) {
analyze();
}
// once finished analyzing, return the source tokens
return sourceTokens;
}
private void analyze() {
// find the current character
String currentChar = String.valueOf(source.charAt(currentIndex));
// based on the current state, determine what to do
switch(this.currentState) {
case Start:
start(currentChar);
break;
case StringConstant:
stringConstant(currentChar);
break;
case IntegerConstant:
integerConstant(currentChar);
break;
case RealConstant:
realConstant(currentChar);
break;
case VariableName:
variableName(currentChar);
break;
}
}
private void start(String c) {
// if this is a control character, generate a source token
if (operators.containsKey(c)) {
generateSourceToken(operators.get(c), c);
currentIndex++;
}
// if this is white space, then just skip this character
else if (whiteSpace.contains(c)) {
currentIndex++;
}
// if this is a double quote, then begin a string constant
else if (c.equals("\"")) {
// begin a string constant
this.currentState = State.StringConstant;
// advance to the next character
currentIndex++;
}
// if this is a number, then begin a numeric constant
else if (Character.isDigit(c.charAt(0))) {
// add this to the buffer
buffer.append(c);
// begin an integer constant
this.currentState = State.IntegerConstant;
// advance to the next character
currentIndex++;
}
// if this is a character, then begin a "variable" name
// "variable" -
else { // if (Character.isLetter(c.charAt(0))) {
// add this to the buffer
buffer.append(c);
// begin a variableName constant
this.currentState = State.VariableName;
// advance to the next character
currentIndex++;
}
/*
// otherwise, just ignore this character
else {
// advance to the next character
currentIndex++;
}
**/
}
public void stringConstant(String c) {
// end of the string constant?
if (c.equals("\"")) {
// create a source token for this string
generateSourceToken("constant", this.buffer.toString());
// move to the next character
currentIndex++;
}
// otherwise, add this character to the current string
else {
// append to the buffer
this.buffer.append(c);
// move to the next character
currentIndex++;
}
}
public void integerConstant(String c) {
// is this a digit?
if (Character.isDigit(c.charAt(0))) {
// append to the buffer
this.buffer.append(c);
// move to the next character
currentIndex++;
}
// is it a decimal?
else if (c.equals(".")) {
// append to the buffer
this.buffer.append(c);
// move to the Real Number state, since this has a decimal
this.currentState = State.RealConstant;
// move to the next character
currentIndex++;
}
// otherwise, create a source token for the number in the buffer
else {
generateSourceToken("constant", this.buffer.toString());
// no need to advance; this could be a '*','+', etc.
// the state is also set correctly
}
}
public void realConstant(String c) {
// is this a digit?
if (Character.isDigit(c.charAt(0))) {
// append to the buffer
this.buffer.append(c);
// move to the next character
currentIndex++;
}
// otherwise, create a source token for the number in the buffer
else {
generateSourceToken("constant", this.buffer.toString());
// no need to advance; this could be a '*','+', etc.
// the state is also set correctly
}
}
public void variableName(String c) {
// is this a character?
if (Character.isLetter(c.charAt(0))) {
// append to the buffer
this.buffer.append(c);
// move to the next character
currentIndex++;
}
// is this a digit?
else if (Character.isDigit(c.charAt(0))) {
// append to the buffer
this.buffer.append(c);
// move to the next character
currentIndex++;
}
// otherwise, create a source token
else {
String word = this.buffer.toString();
// special case - '!='
if (word.equals("!") && c.equals("=")) {
generateSourceToken("relationalOperator", "!=");
// also need to advance
currentIndex++;
}
else if (keywords.containsKey(word)) { // check to see if this is a keyword
generateSourceToken(keywords.get(word), word);
} else {
generateSourceToken("variableName", word);
}
// no need to advance
// the state is also set correctly
}
}
/*Start,
StringConstant,
IntegerConstant,
RealConstant,
Word;*/
private void generateSourceToken(String lexicalValue, String sourceValue) {
// make sure not to actually set the source value to a new line
// because it inserts newlines into the file
if (sourceValue.equals("\n")) {
lexicalValue = "nl";
sourceValue = SourceToken.NewLine;
}
SourceToken t = new SourceToken(lexicalValue, sourceValue);
sourceTokens.add(t);
// also, clear the buffer
this.buffer = new StringBuilder();
// move to the start state
this.currentState = State.Start;
// stay at the same index, so don't advance the counter
// no need to do anything else
}
}