er. We will also need a HashMap that will hold our global variables. We will also have a member HashMap - this will be our source when someone calls a function. The LineManager should take a List as a parameter to the constructor and store it in a member. This will be our read-in input file. It will need a method : SplitAndAssign. This method will get the next line and split it by looking at the global variables to find "FS" - the field separator; you can use String.split for this. It will assign $0, $1, etc. It will set NF. It should return false if there is no line to split. It should update the NR and FNR variables. Now we can create the constructor for the Interpreter. It should require a ProgramNode and a file path. If a path is provided, it should use Files.ReadAllLines() and create a LineManager member, otherwise make a LineManager with an empty list of string. It shoul
Java Code:
Build the two variable classes that we need: InterpreterDataType and a subclass InterpreterArrayDataType. I will abbreviate these as IDT and IADT. IDT holds a string and has two constructors - one with and one without an initial value supplied. IADT holds a HashMap<String,IDT>. Its constructor should create that hash map. We will use these as our variable storage classes as the interpreter runs.
Let's start the interpreter itself. Create a new class (Interpreter). Inside of it, we will have an inner class - LineManager. We will also need a HashMap<String,IDT> that will hold our global variables. We will also have a member HashMap<String, FunctionDefinitionNode> - this will be our source when someone calls a function.
The LineManager should take a List<String> as a parameter to the constructor and store it in a member. This will be our read-in input file. It will need a method : SplitAndAssign. This method will get the next line and split it by looking at the global variables to find "FS" - the field separator; you can use String.split for this. It will assign $0, $1, etc. It will set NF. It should return false if there is no line to split. It should update the NR and FNR variables.
Now we can create the constructor for the Interpreter. It should require a ProgramNode and a file path. If a path is provided, it should use Files.ReadAllLines() and create a LineManager member, otherwise make a LineManager with an empty list of string. It should set the FILENAME global variable. Set the default value of the FS global variable to " " (a single space), OFMT to "%.6g", OFS to " " and ORS to "\n". We won't be using the "RS" variable. Populate the function hash map from the functions in the ProgramNode and each of the BuiltIn's (described below).
Next, we will create a new class: BuiltInFunctionDefinitionNode. This will derive from FunctionDefinitionNode (so that It can be used in any place where a FunctionDefinitionNode is used). The big difference is that we will be adding a lambda expression to hold the functionality.
The lambda function should accept a HashMap of String, InterpreterDataType and return string. The hash map will be the parameters to the function and the string will be the return value. I named this lambda function "Execute". When we are interpreting our AWK code and come across a BuiltInFunctionNode, we will call Execute which will then do the work.
Built-ins can do one other thing that functions defined in AWK cannot do - accept "any number" of parameters. This is called variadic. Add a boolean to the BuiltInFunctionDefinitionNode to indicate if the function is variadic.
We will then create multiple instances of this BuiltInFunctionDefinitionNode, one for each built-in function. We won't be doing all of them (although you can!), but here are the ones we must do:
print, printf, getline, next, gsub, index, length, match, split, sprintf, sub, substr, tolower, toupper
Most of these are very short because we can use the Java built-in functionality.
print, printf: both are variadic. When you create these, they will have a single parameter, but that parameter will be a IADT. Use System.out.print or System.out.printf. The one "tricky part" is that order matters. You can assume that the IADT will have HashMap entries of "0", "1", "2", etc. You can call printf in Java by passing an array:
System.out.printf ("%d %d %d", myIntArray);
An interesting issue appears here. We are storing data in our IDTs as String. But printf could be looking for numbers (%d or %f) or strings (%s). The only way to really resolve this is to parse the format string. For now, send everything to printf as a string.
getline and next : these will call SplitAndAssign - we won't do the other forms.
gsub, match, sub: for these we can use Java's built in regular expression (Regex).
index, length, split, substr, tolower, toupper: for these we can use Java's string class.
Testing for this assignment should include testing the parser changes, test case for the interpreter, the line manager and each of the Built-in functions.
Parser Changes: All functions completed, FunctionCallNodes are created
IDT/ADT: Exist and are correct
LineManager: Members and constructor are correct
SplitandAssign: Splits string, populated $0..$n & NR, uses NF
Interpreter: Members are correct
Interpreter Constructor: Creates LineManager, pre-populates global variables and functions
BuiltInFuctionDefinitionNode: Members and constructor are correct
print, printf: Exist, marked as variadic, call Java functions
getline and next: Exist and call SplitAndAssign
gsub, match, sub, index, length, split, substr, tolower, toupper: Exist, call Java functions, correct return
Trending now
This is a popular solution!
Step by step
Solved in 3 steps
Where is LineManager.java? Write a java class for LineManager.java where the members and constructors are correct
Where are the test cases for interpreter.java? Write a test case for interpreter.java. For interpreter.java and builtinfunctiondefinitionnode.java, there are missing implementations that needs to be added. Attached are images of the 2 java files.
Interpreter.java
public class Interpreter {
private HashMap<String, InterpreterDataType> globalVariables;
private HashMap<String, FunctionDefinitionNode> functions;
// Inner class LineManager
private class LineManager {
private List<String> lines;
public LineManager(List<String> inputLines) {
this.lines = inputLines;
}
public boolean splitAndAssign() {
// Implementation here...
return false;
}
}
public Interpreter(ProgramNode node, String path) {
globalVariables = new HashMap<>();
functions = new HashMap<>();
// Other initializations...
}
}
BuiltInFunctionDefinitionNode.java
public class BuiltInFunctionDefinitionNode extends FunctionDefinitionNode {
private Function<HashMap<String, InterpreterDataType>, String> execute;
private boolean isVariadic;
public BuiltInFunctionDefinitionNode() {
// Constructor logic...
}
// Lambda function Execute
public String execute(HashMap<String, InterpreterDataType> parameters) {
return this.execute.apply(parameters);
}
}