//-------------------------------------------------------------------------------------------------- // // @ CopyRight Roberti & Parau Enterprises, Inc. 2021-2023 // // This work is licensed under the Creative Commons Attribution-NoDerivatives 4.0 International License. // To view a copy of this license, visit http://creativecommons.org/licenses/by-nd/4.0/ // or send a letter to Creative Commons, PO Box 1866, Mountain View, CA 94042, USA. // //-------------------------------------------------------------------------------------------------- //------------------------------------------------------------------------------ // // DVASM InputStream class class - read main input and mange MACRO insertions // //------------------------------------------------------------------------------ package framework; import java.nio.file.*; import java.util.LinkedList; import java.io.InputStream; import java.io.IOException; import java.util.Arrays; import java.util.ArrayList; public class DVParseInput { // // Create class // public DVParseInput(String inputFileName, ArrayList macroDirectories, String archDirectory, String archExtDirectory, DVMacros macros) throws IOException { this.inputFileName= inputFileName; this.macroDirectories= macroDirectories; this.archDirectory= archDirectory; this.archExtDirectory= archExtDirectory; this.macros= macros; byte[] input= Files.readAllBytes(Paths.get(inputFileName)); int startLine= 0; int n; for (n= 0; n < input.length; n++) { if (input[n] == '\n') { if (n > 0 && input[n-1] == '\r') this.inputLines.add(new Line(input, startLine, n-1)); else this.inputLines.add(new Line(input, startLine, n)); startLine= n+1; continue; } } if (startLine < input.length) this.inputLines.add(new Line(input, startLine, input.length)); } // // Start parsing main loop // // We get one line at a time and check to and of statement // // For the following cases we skip the JavaCC parsors and create a statement directly: // // Empy line // Comment line // In line macro // protected DVStatements parse() { DVStatements statements= new DVStatements(this.inputFileName, this.macroDirectories, this.archDirectory, this.archExtDirectory, this, this.macros); DVParseInput.InputData inputData= new InputData(); DVStatements.Statement statement; ArrayList stmtLines= new ArrayList(4); byte[] lnb; while(! this.inputLines.isEmpty()) { Line line= this.inputLines.remove(0); stmtLines.add(line); lnb= line.getLine(); // // Check for in-line macro // if (lnb.length >= 6 && lnb[0] == '#' & (new String(lnb)).stripTrailing().equalsIgnoreCase("#MACRO")) { if (stmtLines.size() > 1) { statement= statements.add(stmtLines.toArray(new DVParseInput.Line[stmtLines.size()])); statement.putError("Line % is a Start Inline Macro line but follows a line with continuation comment", line.lineNo); } else { String inlineCode= ""; while(! this.inputLines.isEmpty()) { line= this.inputLines.remove(0); stmtLines.add(line); lnb= line.getLine(); if (lnb.length >= 4 && lnb[0] == '#' & (new String(lnb)).stripTrailing().equalsIgnoreCase("#END")) break; inlineCode+= new String(line.line) + "\n"; line= null; } if (line == null) { statement= statements.add(stmtLines.toArray(new DVParseInput.Line[stmtLines.size()])); statement.putError("End of input reached while reading an In_Line macro"); stmtLines.clear(); break; } statement= statements.add(stmtLines.toArray(new DVParseInput.Line[stmtLines.size()])); stmtLines.clear(); this.macros.addInlineMacro(statement, statements.jSApi, String.format("Inline macro from line %s to line %s included", statements.currStatement.lines[0].lineNo, statements.currStatement.lines[statements.currStatement.lines.length-1].lineNo), inlineCode); continue; } } // // Scan until strings, comment, continuation comments and end-of-line // int i; for (i= 0; i < lnb.length; i++) { // // Check for strings // if (lnb[i] == '"' || lnb[i] == '\'') { byte q= lnb[i]; int s= i; for (i++; i < lnb.length; i++) if (lnb[i] == q) break; if (i >= lnb.length) { statement= statements.add(stmtLines.toArray(new DVParseInput.Line[stmtLines.size()])); statement.putError("String on line %s starting at position %d is missing ending quote", line.lineNo, i); stmtLines.clear(); i= lnb.length; // Force end of line scan } continue; } // // Check for comments // if (lnb[i] == '/' && i < lnb.length-1 && (lnb[i+1] == '/' || lnb[i+1] == '>')) break; } if (i < lnb.length-1) { line.dataLength= i; if (lnb[i+1] == '>') continue; } else line.dataLength= line.line.length; // // Parse statement // statement= statements.add(stmtLines.toArray(new DVParseInput.Line[stmtLines.size()])); stmtLines.clear(); if (statement.error) continue; this.currStatement= statement; inputData.reStart(statement.lines); DVInputParser parser= new DVInputParser(inputData); try { parser.start(statements); } catch(ParseException e) { String msg= new String(e.getMessage()+"\n"); String out= new String(""); while(msg.length() > 0) { int x= msg.indexOf('\n'); out= new String(out+" "+msg.substring(0, x+1)).replace("", ""); msg= new String(msg.substring(x+1)); } statement.putError("Input Parsing Exception\n Last line Processed [%s]\n Error: %s\n", statement.lines[statement.lines.length-1].lineNo, out); } catch (TokenMgrError e) { statement.putError("Input Token Manager Error\n Last line Processed [%s]" + "\n Lexical state: %s\n Error: %s", statement.lines[statement.lines.length-1].lineNo, parser.getState(), e.toString().replace("", "")); } // // Clear stmtLines for next statement // } return statements; } // // Return input file name // protected String getFileName() { return this.inputFileName; } protected void startMacro(String name, DVStatements.Statement macroParent) { this.macroExtendedName= this.currStatement.lines[this.currStatement.lines.length-1].macroExtendedName + ":" + name; if (this.macroExtendedName.startsWith("|:")) this.macroExtendedName= "|" + this.macroExtendedName.substring(2); this.macroLineNoPrefix= this.currStatement.lines[this.currStatement.lines.length-1].lineNo + "."; this.macroLineNo= 0; this.commIndent= this.indent; this.macroParent= macroParent; } protected void endMacro() { this.macroExtendedName= "|"; this.macroParent= null; this.macroLineNoPrefix= ""; } protected void insertLine(byte[] line) { this.inputLines.add(this.macroLineNo, new Line(line)); } // // Set indentation // protected void setIndentIn() { this.indent+= 4; } protected void setIndentOut() { this.indent-= 4; if (this.indent < 12) this.indent= 12; } // // Insert line formatted - shift indent left // protected void formatLeft(String lbl, String opCode, String parm, String comm) { formatLine(lbl, opCode, parm, comm, -4); } // // Insert line formatted - shift indent left // protected void formatLine(String lbl, String opCode, String parm, String comm) { formatLine(lbl, opCode, parm, comm, 0); } // // Insert line formatted line // private void formatLine(String lbl, String opCode, String parm, String comm, int indAdjust) { this.inputLines.add(this.macroLineNo, new Line(lbl == null ? "" : lbl, opCode == null ? "" : opCode, parm == null ? "" : parm, comm == null ? "" : comm, indAdjust)); } // // Insert a comment only line // protected void formatComm(String comm, int indent) { this.inputLines.add(this.macroLineNo, new Line(((" ".repeat(indent+DVParseInput.this.commIndent-12)) + comm).getBytes())); } // // Line class // protected class Line { private Line(byte[] input, int from, int to) { this.macroExtendedName= "|"; this.lineNo= Integer.toString(DVParseInput.this.lineNo); DVParseInput.this.lineNo++; this.line= Arrays.copyOfRange(input, from, to); for (int i= 0; i < line.length-1; i++) if (line[i] <= 0x1F || line[i] >= 0x7F) line[i]= ' '; } private Line(byte[] ln) { this.macroExtendedName= DVParseInput.this.macroExtendedName; this.macroParent= DVParseInput.this.macroParent; this.lineNo= DVParseInput.this.macroLineNoPrefix + Integer.toString(DVParseInput.this.macroLineNo); DVParseInput.this.macroLineNo++; this.line= ln; for (int i= 0; i < ln.length-1; i++) if (ln[i] <= 0x1F || ln[i] >= 0x7F) ln[i]= ' '; } private Line(String lbl, String opCode, String parm, String comm, int indAdjust) { this.macroExtendedName= DVParseInput.this.macroExtendedName; this.macroParent= DVParseInput.this.macroParent; DVParseInput.this.macroLineNo++; this.lineNo= DVParseInput.this.macroLineNoPrefix + Integer.toString(DVParseInput.this.macroLineNo); this.line= null; this.comp= new String[] { lbl, opCode, parm, comm }; this.indAdjust= indAdjust; } // // Get byte array // private byte[] getLine() { if (this.line == null || this.indAdjust > 0) this.expandComp(); return this.line; } // // Expand formatted line and insert it in inout stream // private void expandComp() { if (this.line != null) return; int l; String punchLine= comp[0] + " "; if ((l= Math.max(DVParseInput.this.indent+this.indAdjust-1, 11)-this.comp[0].length()) > 0) { punchLine+= " ".repeat(l); l= 0; } punchLine+= this.comp[1] + " "; if ((l= 11+l-this.comp[1].length()) > 0) { punchLine+= " ".repeat(l); l= 0; } punchLine+= this.comp[2] + " "; if ((l= 35+l-this.comp[2].length()) > 0) punchLine+= " ".repeat(l); punchLine+= this.comp[3]; this.line= (punchLine).getBytes(); for (int i= 0; i < line.length-1; i++) if (line[i] <= 0x1F || line[i] >= 0x7F) line[i]= ' '; } // // Class fields // protected String macroExtendedName; protected DVStatements.Statement macroParent; protected String lineNo; protected byte[] line; protected int dataLength= 0; protected String[] comp= null; protected String comm= null; protected int indAdjust= 0; } // // Parser input stream // public class InputData extends InputStream { // // Reset for a new statement // public void reStart(DVParseInput.Line[] lines) { this.lines= lines; this.line= 0; this.column= 0; this.markLine= 0; this.markColumn= 0; } // // Input stream methods // public int read() { if (this.line >= this.lines.length) return -1; if (this.column < this.lines[this.line].dataLength) { this.column++; return this.lines[this.line].line[this.column-1]; } this.line++; this.column= 0; return (line < this.lines.length ? '\n' : -1); } public int read(byte[] buffer, int offset, int length) { if (this.line >= this.lines.length) return -1; int copyLength; for (copyLength= offset; copyLength < offset+length; copyLength++) { if (this.column < this.lines[this.line].dataLength) { buffer[copyLength]= this.lines[this.line].line[this.column]; this.column++; continue; } this.line++; if (this.line >= this.lines.length) break; this.column= 0; buffer[copyLength]= '\n'; } return (copyLength-offset > 0 ? copyLength-offset : -1); } public boolean markSupported() { return true; } public void mark(int inputLimit) { this.markLine= this.line; this.markColumn= this.column; } public void reset() { this.line= this.markLine; this.column= this.markColumn; } // // Class fields // private Line[] lines; private int line= 0; private int column= 0; private int markLine= 0; private int markColumn= 0; } // // Class fields // private String inputFileName; private ArrayList macroDirectories; private String archDirectory; private String archExtDirectory; private DVMacros macros; private ArrayList inputLines= new ArrayList(); private int lineNo= 1; private int totalByteUsed= 0; private int totalLineUsed= 0; private DVStatements.Statement currStatement; private String macroName; private int macroLineNo; private String macroExtendedName= "|"; private String macroLineNoPrefix; private int indent= 12; private int commIndent= 12; private DVStatements.Statement macroParent; }