BasicInterpreter

This commit is contained in:
Hoang Nguyen 2021-05-21 16:30:19 +03:00
parent ea62c0d3b0
commit 143979c5b4
No known key found for this signature in database
GPG Key ID: 813CF484F4993419
8 changed files with 1106 additions and 0 deletions

View File

@ -0,0 +1,19 @@
PROGRAM thisProgram;
VAR
var1, a, b, c : INTEGER;
BEGIN {example}
a := 2;
var1 := 1;
b := 5;
c := 20;
LOOP b DO
var1 := var1 + a;
c := c - 1;
END;
LOOP a DO
b := b + 2
END
END. {example}

View File

@ -0,0 +1,6 @@
<a : INTEGER>; <b : INTEGER>; <c : INTEGER>; REAL; <var1 : INTEGER>; INTEGER;
a = 2
b = 9.0
c = 15.0
var1 = 11.0

View File

@ -0,0 +1,393 @@
package InterpreterAlpha;
import java.io.FileWriter;
import java.io.IOException;
import java.nio.file.*;
import java.util.HashMap;
import java.util.Map;
//////////////////////////////////////////////////////////////////
// //
// AST visitors //
// //
//////////////////////////////////////////////////////////////////
class NodeVisitor {
String visit(AST node) throws Exception {
if (node instanceof Block)
return visit_Block((Block) node);
else if (node instanceof Program)
return visit_Program((Program) node);
else if (node instanceof BinOp)
return visit_BinOp((BinOp) node);
else if (node instanceof Num)
return visit_Num((Num) node);
else if (node instanceof UnaryOp)
return visit_UnaryOp((UnaryOp) node);
else if (node instanceof Compound)
return visit_Compound((Compound) node);
else if (node instanceof NoOp)
return visit_NoOp((NoOp) node);
else if (node instanceof VarDecl)
return visit_VarDecl((VarDecl) node);
else if (node instanceof Assign)
return visit_Assign((Assign) node);
else if (node instanceof Var)
return visit_Var((Var) node);
else if (node instanceof Type)
return visit_Type((Type) node);
else if (node instanceof ForStatement)
return visit_for((ForStatement) node);
else
throw new Exception("No visit function for the type of node!");
}
String visit_Type(Type node) throws Exception {
return "";
}
String visit_for(ForStatement node) throws Exception {
return "";
}
String visit_Block(Block node) throws Exception {
return "";
}
String visit_Program(Program node) throws Exception {
return "";
}
String visit_BinOp(BinOp node) throws Exception {
return "";
}
String visit_Num(Num node) throws Exception {
return "";
}
String visit_UnaryOp(UnaryOp node) throws Exception {
return "";
}
String visit_Compound(Compound node) throws Exception {
return "";
}
String visit_NoOp(NoOp node) throws Exception {
return "";
}
String visit_VarDecl(VarDecl node) throws Exception {
return "";
}
String visit_Assign(Assign node) throws Exception {
return "";
}
String visit_Var(Var node) throws Exception {
return "";
}
}
///////////////////////////////////////////////////////////////////////////////
// //
// SYMBOL and SYMBOL TABLE //
// //
///////////////////////////////////////////////////////////////////////////////
class Symbol {
String name;
TokenType type; // INTEGER | REAL
static public FileWriter resultFile;
static {
try {
FileWriter deletedFile = new FileWriter("result.txt", false);
deletedFile.write("");
deletedFile.close();
resultFile = new FileWriter("result.txt", true);
} catch (IOException e) {
System.out.println("File doesn't exist!");
}
}
// built-in type
Symbol(String name) {
this.name = name;
if (name.equals("INTEGER"))
this.type = TokenType.INTEGER;
else
this.type = TokenType.REAL;
}
// variable
Symbol(String name, TokenType type) {
this.name = name;
this.type = type;
}
void printSym() throws Exception {
}
}
class VarSymbol extends Symbol {
VarSymbol(String name, TokenType type) {
super(name, type);
}
@Override
void printSym() throws Exception {
Symbol.resultFile.append("<" + this.name + " : " + this.type + ">");
}
}
class BuiltinTypeSymbol extends Symbol {
BuiltinTypeSymbol(String name) {
super(name);
}
@Override
void printSym() throws Exception {
Symbol.resultFile.append(this.name);
}
}
// map of (symbol_name, symbol)
class SymbolTable {
Map<String, Symbol> _symbols;
SymbolTable() {
this._symbols = new HashMap<>();
this.initBuiltins();
}
private void initBuiltins() {
this.define(new BuiltinTypeSymbol("INTEGER"));
this.define(new BuiltinTypeSymbol("REAL"));
}
void define(Symbol symbol) {
this._symbols.put(symbol.name, symbol);
}
// return an instance of Symbol class or a null objects
Symbol lookup(String name) {
return this._symbols.get(name);
}
}
class SymbolTableBuilder extends NodeVisitor {
SymbolTable symtab;
SymbolTableBuilder() {
this.symtab = new SymbolTable();
}
void printTable() throws Exception {
for (String name : symtab._symbols.keySet()) {
symtab._symbols.get(name).printSym();
Symbol.resultFile.append("; ");
}
Symbol.resultFile.append("\n\n");
}
@Override
String visit_Block(Block node) throws Exception {
for (VarDecl declaration : node.declaration)
this.visit(declaration);
this.visit(node.compound_statement);
return "";
}
@Override
String visit_Program(Program node) throws Exception {
this.visit(node.block);
return "";
}
@Override
String visit_BinOp(BinOp node) throws Exception {
this.visit(node.left);
this.visit(node.right);
return "";
}
@Override
String visit_UnaryOp(UnaryOp node) throws Exception {
this.visit(node.expr);
return "";
}
@Override
String visit_Compound(Compound node) throws Exception {
for (AST child : node.children)
this.visit(child);
return "";
}
@Override
String visit_VarDecl(VarDecl node) throws Exception {
String type_name = node.type_node.value;
Symbol type_symbol = this.symtab.lookup(type_name);
String var_name = node.var_node.value;
VarSymbol var_symbol = new VarSymbol(var_name, type_symbol.type);
this.symtab.define(var_symbol);
return "";
}
@Override
String visit_Assign(Assign node) throws Exception {
String var_name = node.left.value;
Symbol var_symbol = this.symtab.lookup(var_name);
if (var_symbol == null)
throw new Exception("No var_name declaration for " + var_name);
this.visit(node.right);
return "";
}
@Override
String visit_Var(Var node) throws Exception {
String var_name = node.value;
Symbol var_symbol = this.symtab.lookup(var_name);
if (var_symbol == null)
throw new Exception("No var_name declaration for " + var_name);
return "";
}
}
/////////////////////////////////////////////////////////////////////////////////////
// //
// INTERPRETER //
// //
/////////////////////////////////////////////////////////////////////////////////////
public class InterpreterAlpha extends NodeVisitor {
// store table of (var_name; var_value)
Map<String, String> GLOBAL_SCOPE;
AST tree;
InterpreterAlpha(AST tree) {
this.tree = tree;
GLOBAL_SCOPE = new HashMap<>();
}
void interpret() throws Exception {
AST tree = this.tree;
if (tree == null) {
throw new Exception("Have no tree to trace!");
}
this.visit(tree);
printTab(GLOBAL_SCOPE);
}
void printTab(Map<String, String> scope) throws Exception {
for (String key : scope.keySet())
Symbol.resultFile.append(key + " = " + scope.get(key) + "\n");
}
@Override
String visit_Program(Program node) throws Exception {
return this.visit(node.block);
}
@Override
String visit_for(ForStatement node) throws Exception {
String val = GLOBAL_SCOPE.getOrDefault(node.variable.value, null);
if (val == null)
throw new Exception("No variable found in for_loop!");
else {
int end_point = Integer.parseInt(val);
for (int tmp = 1; tmp <= end_point; tmp++) {
for (AST nod : node.statements)
this.visit(nod);
}
}
return "";
}
@Override
String visit_Block(Block node) throws Exception {
for (AST declaration : node.declaration)
this.visit(declaration);
this.visit(node.compound_statement);
return "";
}
@Override
String visit_BinOp(BinOp node) throws Exception {
switch (node.op.type) {
case PLUS:
return Double.toString(Double.parseDouble(this.visit(node.left)) + Double.parseDouble(this.visit(node.right)));
case MINUS:
return Double.toString(Double.parseDouble(this.visit(node.left)) - Double.parseDouble(this.visit(node.right)));
case MUL:
return Double.toString(Double.parseDouble(this.visit(node.left)) * Double.parseDouble(this.visit(node.right)));
case FLOAT_DIV:
return Double.toString(Double.parseDouble(this.visit(node.left)) / Double.parseDouble(this.visit(node.right)));
case INTEGER_DIV:
return Integer.toString((int) Double.parseDouble(this.visit(node.left)) / (int) Double.parseDouble(this.visit(node.right)));
default:
throw new Exception("Incorrect binary operator!");
}
}
@Override
String visit_Num(Num node) throws Exception {
return node.value;
}
@Override
String visit_UnaryOp(UnaryOp node) throws Exception {
switch (node.op.type) {
case PLUS:
return Double.toString(+Double.parseDouble(this.visit(((UnaryOp) node).expr)));
case MINUS:
return Double.toString(-Double.parseDouble(this.visit(((UnaryOp) node).expr)));
default:
throw new Exception("Incorrect unary operator!");
}
}
@Override
String visit_Compound(Compound node) throws Exception {
for (AST child : node.children)
this.visit(child);
return "";
}
@Override
String visit_Assign(Assign node) throws Exception {
String var_name = node.left.value;
GLOBAL_SCOPE.put(var_name, this.visit(node.right));
return "";
}
@Override
String visit_Var(Var node) throws Exception {
String var_name = node.value;
String val = this.GLOBAL_SCOPE.get(var_name);
if (val != null)
return val;
else
throw new Exception("Variable has no value!");
}
public static void main(String[] args) throws Exception {
String str = Files.readString(Paths.get("Program.txt"));
if (str.equals("")) {
throw new Exception("Please input text into Program.txt");
}
Lexer lexer = new Lexer(str);
Parser parser = new Parser(lexer);
AST tree = parser.parse();
SymbolTableBuilder symtab = new SymbolTableBuilder();
symtab.visit(tree);
symtab.printTable();
InterpreterAlpha interpreter = new InterpreterAlpha(tree);
interpreter.interpret();
Symbol.resultFile.close();
}
}

View File

@ -0,0 +1,216 @@
package InterpreterAlpha;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
enum TokenType {
INTEGER,
REAL,
INTEGER_CONST,
REAL_CONST,
PLUS, // +
MINUS, // -
MUL, // *
INTEGER_DIV, // DIV
FLOAT_DIV, // /
LPAREN, // (
RPAREN, // )
ID,
ASSIGN, // :=
BEGIN,
END,
SEMI, // ;
DOT, // .
PROGRAM,
VAR,
COLON, // :
COMMA, // ,
LOOP,
DO,
EOF,
// EOF token is used to indicate that
// there is no more input left for lexical analysis
}
class Token {
TokenType type;
String value;
Token(TokenType type, String value) {
this.type = type;
this.value = value;
}
}
class Lexer {
String text;
int pos;
char current_char;
private static final Map<String, Token> RESERVED_KEYWORDS = initMap();
// map of constant keywords
private static Map<String, Token> initMap() {
Map<String, Token> map = new HashMap<>();
map.put("PROGRAM", new Token(TokenType.PROGRAM, "PROGRAM"));
map.put("VAR", new Token(TokenType.VAR, "VAR"));
map.put("DIV", new Token(TokenType.INTEGER_DIV, "DIV"));
map.put("INTEGER", new Token(TokenType.INTEGER, "INTEGER"));
map.put("REAL", new Token(TokenType.REAL, "REAL"));
map.put("BEGIN", new Token(TokenType.BEGIN, "BEGIN"));
map.put("END", new Token(TokenType.END, "END"));
map.put("LOOP", new Token(TokenType.LOOP, "LOOP"));
map.put("DO", new Token(TokenType.DO, "DO"));
return Collections.unmodifiableMap(map);
}
Lexer(String text) {
this.text = text;
this.pos = 0;
this.current_char = this.text.charAt(this.pos);
}
void error() throws Exception {
throw new Exception("Invalid character!");
}
// advance the position and set the current_char variable
void advance() {
this.pos++;
if (this.pos > this.text.length() - 1) {
this.current_char = '\0'; // End of input
} else {
this.current_char = this.text.charAt(this.pos);
}
}
// look at the next character
char peek() {
int peek_pos = this.pos + 1;
if (peek_pos > this.text.length() - 1) {
return '\0';
} else {
return this.text.charAt(peek_pos);
}
}
// Return a multi digit integer or real number token (as a String)
Token number() {
StringBuilder result = new StringBuilder();
while (this.current_char != '\0' && Character.isDigit(this.current_char)) {
result.append(this.current_char);
this.advance();
}
if (this.current_char == '.') {
result.append(this.current_char);
this.advance();
while (this.current_char != '\0' && Character.isDigit(this.current_char)) {
result.append(this.current_char);
this.advance();
}
return new Token(TokenType.REAL_CONST, result.toString());
} else
return new Token(TokenType.INTEGER_CONST, result.toString());
}
Token _id() {
StringBuilder result = new StringBuilder();
while (this.current_char != '\0' && Character.isLetterOrDigit(this.current_char)) {
result.append(this.current_char);
this.advance();
}
return RESERVED_KEYWORDS.getOrDefault(result.toString(), new Token(TokenType.ID, result.toString()));
}
// Lexical analyzer
// that breaks a sentence apart into tokens (1 at a time)
Token get_next_token() throws Exception {
while (this.current_char != '\0') {
// skip whitespaces, End of Line, Tab
if (Character.isSpaceChar(this.current_char) || this.current_char == '\n' || this.current_char == '\t') {
this.advance();
continue;
}
// skip comments
if (this.current_char == '{') {
this.advance(); // starting bracket
while (this.current_char != '}') {
this.advance();
}
this.advance(); // closing bracket
continue;
}
if (Character.isLetter(this.current_char)) {
return this._id();
}
if (Character.isDigit(this.current_char)) {
return this.number();
}
if (this.current_char == ':' && this.peek() == '=') {
this.advance();
this.advance();
return new Token(TokenType.ASSIGN, ":=");
}
if (this.current_char == ';') {
this.advance();
return new Token(TokenType.SEMI, ";");
}
if (this.current_char == ':') {
this.advance();
return new Token(TokenType.COLON, ":");
}
if (this.current_char == ',') {
this.advance();
return new Token(TokenType.COMMA, ",");
}
if (this.current_char == '+') {
this.advance();
return new Token(TokenType.PLUS, "+");
}
if (this.current_char == '-') {
this.advance();
return new Token(TokenType.MINUS, "-");
}
if (this.current_char == '*') {
this.advance();
return new Token(TokenType.MUL, "*");
}
if (this.current_char == '/') {
this.advance();
return new Token(TokenType.FLOAT_DIV, "/");
}
if (this.current_char == '(') {
this.advance();
return new Token(TokenType.LPAREN, "(");
}
if (this.current_char == ')') {
this.advance();
return new Token(TokenType.RPAREN, ")");
}
if (this.current_char == '.') {
this.advance();
return new Token(TokenType.DOT, ".");
}
this.error();
}
return new Token(TokenType.EOF, "");
}
}

View File

@ -0,0 +1,359 @@
package InterpreterAlpha;
import java.util.ArrayList;
// a node on the Abstract Syntax Tree
class AST {
}
class BinOp extends AST {
AST left;
AST right;
Token op;
BinOp(AST left, Token op, AST right) {
this.left = left;
this.op = op;
this.right = right;
}
}
class UnaryOp extends AST {
Token op;
AST expr;
UnaryOp(Token op, AST expr) {
this.op = op;
this.expr = expr;
}
}
class Num extends AST {
Token token;
String value;
Num(Token token) {
this.token = token;
this.value = token.value;
}
}
class Compound extends AST {
ArrayList<AST> children;
Compound() {
this.children = new ArrayList<AST>();
}
}
class Assign extends AST {
Var left;
Token op;
AST right;
Assign(Var left, Token op, AST right) {
this.left = left;
this.op = op;
this.right = right;
}
}
class Var extends AST {
Token token;
String value; // variable's name
Var(Token token) {
this.token = token;
this.value = token.value;
}
}
class ForStatement extends AST {
ArrayList<AST> statements;
Var variable;
ForStatement(ArrayList<AST> statements, Var variable) {
this.statements = statements;
this.variable = variable;
}
}
// empty operator
class NoOp extends AST {
}
class Program extends AST {
String name;
Block block;
Program(String name, Block block) {
this.name = name;
this.block = block;
}
}
class Block extends AST {
ArrayList<VarDecl> declaration;
Compound compound_statement;
Block(ArrayList<VarDecl> declaration, Compound compound_statement) {
this.declaration = declaration;
this.compound_statement = compound_statement;
}
}
class VarDecl extends AST {
Var var_node;
Type type_node;
VarDecl(Var var_node, Type type_node) {
this.var_node = var_node;
this.type_node = type_node;
}
}
// an integer or a real number
class Type extends AST {
Token token;
String value;
Type(Token token) {
this.token = token;
this.value = token.value;
}
}
class Parser {
Lexer lexer;
Token current_token;
Parser(Lexer lexer) throws Exception {
this.lexer = lexer;
this.current_token = this.lexer.get_next_token();
}
void error() throws Exception {
throw new Exception("Invalid syntax!");
}
void eat(TokenType token_type) throws Exception {
if (this.current_token.type == token_type) {
this.current_token = this.lexer.get_next_token();
} else {
this.error();
}
}
// program: PROGRAM variable SEMI block DOT
Program program() throws Exception {
this.eat(TokenType.PROGRAM);
Var var_node = this.variable();
String program_name = var_node.value;
this.eat(TokenType.SEMI);
Block block_node = this.block();
this.eat(TokenType.DOT);
return new Program(program_name, block_node);
}
// block: declarations compound_statement
Block block() throws Exception {
ArrayList<VarDecl> declaration_nodes = this.declarations();
Compound compound_statement_node = this.compound_statement();
return new Block(declaration_nodes, compound_statement_node);
}
// declarations: VAR (variable_declaration SEMI)*
// | empty
ArrayList<VarDecl> declarations() throws Exception {
ArrayList<VarDecl> declarations = new ArrayList<VarDecl>();
if (this.current_token.type == TokenType.VAR) {
this.eat(TokenType.VAR);
while (this.current_token.type == TokenType.ID) {
ArrayList<VarDecl> var_decl = this.variable_declaration();
declarations.addAll(var_decl);
this.eat(TokenType.SEMI);
}
}
return declarations;
}
// variable_declaration: ID (COMMA ID)* COLON type_spec
ArrayList<VarDecl> variable_declaration() throws Exception {
ArrayList<Var> var_nodes = new ArrayList<Var>();
var_nodes.add(new Var(this.current_token));
this.eat(TokenType.ID);
while (this.current_token.type == TokenType.COMMA) {
this.eat(TokenType.COMMA);
var_nodes.add(new Var(this.current_token));
this.eat(TokenType.ID);
}
this.eat(TokenType.COLON);
Type type_node = this.type_spec();
ArrayList<VarDecl> var_declarations = new ArrayList<VarDecl>();
for (Var var_node : var_nodes)
var_declarations.add(new VarDecl(var_node, type_node));
return var_declarations;
}
// type_spec: INTEGER | REAL
Type type_spec() throws Exception {
Token token = this.current_token;
if (this.current_token.type == TokenType.INTEGER)
this.eat(TokenType.INTEGER);
else
this.eat(TokenType.REAL);
return new Type(token);
}
// compound_statement: BEGIN statement_list END
Compound compound_statement() throws Exception {
this.eat(TokenType.BEGIN);
ArrayList<AST> nodes = this.statement_list();
this.eat(TokenType.END);
Compound root = new Compound();
root.children.addAll(nodes);
return root;
}
// statement_list: statement
// | statement SEMI statement_list
ArrayList<AST> statement_list() throws Exception {
AST node = this.statement();
ArrayList<AST> results = new ArrayList<AST>();
results.add(node);
while (this.current_token.type == TokenType.SEMI) {
this.eat(TokenType.SEMI);
results.add(this.statement());
}
if (this.current_token.type == TokenType.ID)
this.error();
return results;
}
// statement: compound_statement
// | for_statement
// | assign_statement
// | empty
AST statement() throws Exception {
AST node;
if (this.current_token.type == TokenType.BEGIN)
node = this.compound_statement();
else if (this.current_token.type == TokenType.ID)
node = this.assign_statement();
else if (this.current_token.type == TokenType.LOOP)
node = this.for_statement();
else
node = this.empty();
return node;
}
// for_statement: LOOP ID DO statement_list END
ForStatement for_statement() throws Exception {
this.eat(TokenType.LOOP);
Var variable = this.variable();
this.eat(TokenType.DO);
ArrayList<AST> statements = this.statement_list();
this.eat(TokenType.END);
return new ForStatement(statements, variable);
}
// assign_statement: variable ASSIGN expr
Assign assign_statement() throws Exception {
Var left = this.variable();
Token token = this.current_token;
this.eat(TokenType.ASSIGN);
AST right = this.expr();
return new Assign(left, token, right);
}
// variable: ID
Var variable() throws Exception {
Var node = new Var(this.current_token);
this.eat(TokenType.ID);
return node;
}
NoOp empty() {
return new NoOp();
}
// factor: PLUS factor
// | MINUS factor
// | INTEGER_CONST
// | REAL_CONST
// | LPAREN expr RPAREN
// | variable
AST factor() throws Exception {
Token token = this.current_token;
switch (token.type) {
case PLUS:
this.eat(TokenType.PLUS);
return new UnaryOp(token, this.factor());
case MINUS:
this.eat(TokenType.MINUS);
return new UnaryOp(token, this.factor());
case INTEGER_CONST:
this.eat(TokenType.INTEGER_CONST);
return new Num(token);
case REAL_CONST:
this.eat(TokenType.REAL_CONST);
return new Num(token);
case LPAREN:
this.eat(TokenType.LPAREN);
AST node = this.expr();
this.eat(TokenType.RPAREN);
return node;
default:
return this.variable();
}
}
// term: factor ((MUL | DIV) factor)*
AST term() throws Exception {
AST node = this.factor();
while (this.current_token.type == TokenType.FLOAT_DIV
|| this.current_token.type == TokenType.INTEGER_DIV
|| this.current_token.type == TokenType.MUL) {
Token token = this.current_token;
if (token.type == TokenType.MUL) {
this.eat(TokenType.MUL);
} else if (token.type == TokenType.INTEGER_DIV)
this.eat(TokenType.INTEGER_DIV);
else
this.eat(TokenType.FLOAT_DIV);
node = new BinOp(node, token, this.factor());
}
return node;
}
// expr: term ((PLUS | MINUS) term)*
AST expr() throws Exception {
AST node = this.term();
while (this.current_token.type == TokenType.MINUS || this.current_token.type == TokenType.PLUS) {
Token token = this.current_token;
if (token.type == TokenType.MINUS) {
this.eat(TokenType.MINUS);
} else
this.eat(TokenType.PLUS);
node = new BinOp(node, token, this.term());
}
return node;
}
AST parse() throws Exception {
AST node = this.program();
if (this.current_token.type != TokenType.EOF)
this.error();
return node;
}
}

View File

@ -0,0 +1,47 @@
package InterpreterAlpha;
import org.junit.Test;
import java.nio.file.Files;
import java.nio.file.Paths;
import static org.junit.Assert.*;
public class InterpreterAlphaTest {
@Test
public void interpret() throws Exception{
String str = "PROGRAM thisProgram;\n" +
"VAR\n" +
" var1, a, b, c : INTEGER;\n" +
"\n" +
"BEGIN {example}\n" +
" a := 2;\n" +
" var1 := 1;\n" +
" b := 5;\n" +
" c := 20;\n" +
"\n" +
" LOOP b DO\n" +
" var1 := var1 + a;\n" +
" c := c - 1;\n" +
" END;\n" +
"\n" +
" LOOP a DO\n" +
" b := b + 2\n" +
" END\n" +
"END. {example}";
Lexer lexer = new Lexer(str);
Parser parser = new Parser(lexer);
AST tree = parser.parse();
SymbolTableBuilder symtab = new SymbolTableBuilder();
symtab.visit(tree);
InterpreterAlpha interpreter = new InterpreterAlpha(tree);
interpreter.interpret();
Symbol.resultFile.close();
String result = Files.readString(Paths.get("result.txt"));
assertEquals("a = 2\n" +
"b = 9.0\n" +
"c = 15.0\n" +
"var1 = 11.0\n", result);
}
}

View File

@ -0,0 +1,36 @@
package InterpreterAlpha;
import org.junit.Test;
import static org.junit.Assert.*;
public class LexerTest {
@Test
public void get_next_token() throws Exception{
Lexer x = new Lexer(":=");
Token y = x.get_next_token();
assertEquals(TokenType.ASSIGN, y.type);
assertEquals(":=", y.value);
x = new Lexer("{this is a comment}\t\n ;");
y = x.get_next_token();
assertEquals(";", y.value);
assertEquals(TokenType.SEMI, y.type);
x = new Lexer("a12xb");
y = x.get_next_token();
assertEquals(TokenType.ID, y.type);
assertEquals("a12xb", y.value);
x = new Lexer("1483");
y = x.get_next_token();
assertEquals(TokenType.INTEGER_CONST, y.type);
assertEquals("1483", y.value);
x = new Lexer("12.5");
y = x.get_next_token();
assertEquals(TokenType.REAL_CONST, y.type);
assertEquals("12.5", y.value);
}
}

View File

@ -0,0 +1,30 @@
package InterpreterAlpha;
import org.junit.Test;
import java.nio.file.Files;
import java.nio.file.Paths;
import static org.junit.Assert.*;
public class SymbolTableBuilderTest {
@Test
public void printTable() throws Exception{
String str = "PROGRAM thisProgram;\n" +
"VAR\n" +
" var1, a, b, c : INTEGER;\n" +
" d : REAL;\n" +
"BEGIN {example}\n" +
"END. {example}";
Lexer lexer = new Lexer(str);
Parser parser = new Parser(lexer);
AST tree = parser.parse();
SymbolTableBuilder sym_table = new SymbolTableBuilder();
sym_table.visit(tree);
sym_table.printTable();
Symbol.resultFile.close();
String result = Files.readString(Paths.get("result.txt"));
assertEquals("<a : INTEGER>; <b : INTEGER>; <c : INTEGER>; <d : REAL>; REAL; <var1 : INTEGER>; INTEGER; \n\n", result);
}
}