package de.bse.prgm.parse; import de.bse.prgm.cmd.Shebang; import de.bse.prgm.err.FileToReadIsEmptyError; import de.bse.prgm.err.IError; import de.bse.prgm.err.ShebangMissingError; import de.bse.prgm.err.SyntaxError; import de.bse.prgm.struct.Program; import de.bse.prgm.war.LineNumberWarning; import java.util.NoSuchElementException; import java.util.Scanner; /** * The Lexer verifies every line of code, and sends verified lines to the * parser. * * @author Elias Groll * @version 2.15 */ public class Lexer { /** * Lexer cannot be instantiated. */ private Lexer() { // it is not possible to create a Lexer-object } /** * All tokens the Lexer can understand/verify. */ public static final String[] KNOWN_COMMANDS = new String[] { "HIGH", "LOW", "INSTRUCT", "OUTPUT", "PAUSE", "GOTO", "IF", "END", "SOUND", "GOSUB", "RETURN", "FOR", "NEXT", "DEBUG", "BREAKPOINT", "SYMBOL", "LOOKUP", "LOOKDOWN", "SEROUT", "SERIN", "PWM", "POT", "PULSIN", "PULSOUT", "LET", "INPUT", "ASSERT", "ACTIVATEINFO", "DEACTIVATEINFO", "BRANCH", "NAP", "SLEEP", "EEPROM", "RANDOM", "READ", "WRITE", "TOGGLE", "REVERSE" }; private static Scanner prgmScanner; /** * Checks whether the given line is an accepted token. * * @param line * to be checked if it's a known command */ private static boolean isAKnownCommand(String line) { boolean retVal = false; for (String cmd : KNOWN_COMMANDS) { if ((line.startsWith(cmd) ^ line.endsWith(":")) ^ line.contains("=")) { // excluding or -> a line cannot be a label and a command or allocation retVal = true; } } return retVal; } /** * Checks whether the given line is a comment / blank line or not. * * @param line * to be checked * @return true/false if the line is a comment or blank line */ private static boolean isACommentOrABlankLine(String line) { boolean retVal = false; if (line.startsWith("'") || line.isEmpty()) { retVal = true; } return retVal; } /** * Checks whether the given line is a BS1-Shebang or not. * * @param line * to be checked if it is a shebang or not * @return true/false if the line is a BS1-Shebang */ private static boolean isAShebang(String line) { boolean retVal = false; if (line.matches("'\\s*\\{\\$STAMP\\s+BS1\\}")) { retVal = true; } return retVal; } /** * Creates an executable Program from a given String. * * @param prgm * (String) out of which the program will be created * @return Program createdProgam */ public static Program createProgramFromString(String prgm) { Program retVal = new Program(); prgmScanner = new Scanner(prgm); int lineNumber = 1; try { String actualLine = prgmScanner.nextLine(); actualLine = actualLine.trim(); if (!isAShebang(actualLine)) { retVal.addError(new ShebangMissingError()); } retVal.addCommand(new Shebang()); while (prgmScanner.hasNextLine()) { lineNumber++; actualLine = prgmScanner.nextLine(); actualLine = cutInlineComments(actualLine); actualLine = actualLine.trim(); if (isACommentOrABlankLine(actualLine)) { continue; } else if (isAKnownCommand(actualLine) && !(isACommentOrABlankLine(actualLine))) { retVal.addInstance(Parser.parseLine(actualLine, lineNumber)); } else { retVal.addError(new SyntaxError(lineNumber)); } } } catch (NoSuchElementException e) { retVal.addError(new FileToReadIsEmptyError()); } catch (Exception e) { retVal.addError(new IError() { public String errorMsg() { return "[Error, internal]Parser error"; } }); } if (lineNumber > 128) { retVal.addWarning(new LineNumberWarning()); } return retVal; } /** * Returns an array of strings containing all tokens. * * @return array of strings containing all commands known to the Lexer */ public static String[] getKnownCommands() { return KNOWN_COMMANDS; } /** * Cuts everything after the comment-tag ("'"). * * @param line * to be checked for inline-comments * @return line without inline-comments */ private static String cutInlineComments(String line) { if (line.contains("'")) { return line.substring(0, line.indexOf("'")); } else { return line; } } }