From 7c73559bb107023ab45d56328bda824ecc6e681f Mon Sep 17 00:00:00 2001 From: GideonLeGrange Date: Mon, 14 Apr 2014 14:42:51 +0200 Subject: [PATCH] Re-wrote scanning and parsing to handle problems from issues #3 and #4 --- .../legrange/mikrotik/impl/parser/Command.g4 | 23 ---- .../me/legrange/mikrotik/impl/Parser.java | 118 ++++++++---------- .../legrange/mikrotik/impl/ScanException.java | 21 ++++ 3 files changed, 74 insertions(+), 88 deletions(-) delete mode 100644 src/main/antlr4/me/legrange/mikrotik/impl/parser/Command.g4 create mode 100644 src/main/java/me/legrange/mikrotik/impl/ScanException.java diff --git a/src/main/antlr4/me/legrange/mikrotik/impl/parser/Command.g4 b/src/main/antlr4/me/legrange/mikrotik/impl/parser/Command.g4 deleted file mode 100644 index b485e7d..0000000 --- a/src/main/antlr4/me/legrange/mikrotik/impl/parser/Command.g4 +++ /dev/null @@ -1,23 +0,0 @@ -/** A Mikrotik API Command grammar */ - -grammar Command; -line: command param* where? retrn? ; -command: CMD ; -param: NAME ('=' text )? ; -text: (QUOTED|VALUE) ; -where: 'where' expr ; -expr: ( eqExpr | hasExpr | moreExpr | lessExpr ) binExpr? ; -binExpr: andExpr | orExpr ; -eqExpr: NAME '=' text ; -hasExpr: NAME ; -moreExpr: NAME '>' text; -lessExpr: NAME '<' text; -andExpr: expr 'and' expr ; -orExpr: expr 'or' expr ; -retrn: 'returns' NAME (',' NAME )* ; - -NAME : ('a'..'z'|'A'..'Z'|'-')+ ; -CMD : (('/')(NAME))+ ; -QUOTED : ('"')(.*?)('"') ; -VALUE : (~[ \t\r\n="])+ ; -WS: [ \n\t\r]+ -> skip; \ No newline at end of file diff --git a/src/main/java/me/legrange/mikrotik/impl/Parser.java b/src/main/java/me/legrange/mikrotik/impl/Parser.java index dc6caf8..ea2185a 100644 --- a/src/main/java/me/legrange/mikrotik/impl/Parser.java +++ b/src/main/java/me/legrange/mikrotik/impl/Parser.java @@ -1,12 +1,9 @@ package me.legrange.mikrotik.impl; import java.util.Arrays; -import java.util.HashMap; import java.util.LinkedList; import java.util.List; -import java.util.Map; -import java.util.Scanner; -import java.util.StringTokenizer; +import me.legrange.mikrotik.impl.Scanner.Token; /** * Parse the pseudo-command line into command objects. @@ -14,6 +11,15 @@ import java.util.StringTokenizer; */ class Parser { + public static void main(String...args) throws Exception { + /* System.out.println(parse("/system/reboot")); + System.out.println(parse("/interface/print where type=ether")); + System.out.println(parse("/interface/wireless/monitor .id=wlan1")); + System.out.println(parse("/ip/firewall/filter/add action=drop chain=forward in-interface=gre1 protocol=udp dst-port=78,80")); + System.out.println(parse("/file/print detail where name=conf.rsc")); + System.out.println(parse("/ip/firewall/filter/add action=drop chain=forward in-interface=aps_hs protocol=udp dst-port=78,80,32")); */ + System.out.println(parse("/interface/gre/set .id=gre1 comment=\"test coment\"")); + } /** parse the given bit of text into a Command object */ static Command parse(String text) throws ParseException { Parser parser = new Parser(text); @@ -22,10 +28,7 @@ class Parser { /** run parse on the internal data and return the command object */ private Command parse() throws ParseException { - next(); - expect(Token.COMMAND); - cmd = new Command(text); - next(); + command(); while (!((token == Token.WHERE) || (token == Token.RETURN) || (token == Token.EOL))) { param(); } @@ -38,20 +41,36 @@ class Parser { expect(Token.EOL); return cmd; } + + private void command() throws ParseException { + StringBuilder path = new StringBuilder(); + do { + expect(Token.SLASH); + path.append("/"); + next(); + expect(Token.TEXT); + path.append(text); + next(); + } while (token == Token.SLASH); + cmd = new Command(path.toString()); + } private void param() throws ParseException { String name = text; next(); if (token == Token.EQUALS) { next(); - expect(Token.NAME); - String val = text; + expect(Token.TEXT); + StringBuilder val = new StringBuilder(text); next(); while (token == Token.COMMA) { + val.append(","); + next(); + expect(Token.TEXT); + val.append(text); next(); - val = val + "," + text; } - cmd.addParameter(new Parameter(name, val)); + cmd.addParameter(new Parameter(name, val.toString())); } else { cmd.addParameter(new Parameter(name)); @@ -64,12 +83,12 @@ class Parser { } private void expr() throws ParseException { - expect(Token.NOT, Token.NAME); + expect(Token.NOT, Token.TEXT); switch (token) { case NOT: notExpr(); break; - case NAME: { + case TEXT: { String name = text; next(); expect(Token.EQUALS, Token.LESS, Token.MORE); @@ -116,19 +135,20 @@ class Parser { cmd.addQuery("?#!"); } - private void eqExpr(String name) { + private void eqExpr(String name) throws ParseException { next(); // eat = + expect(Token.TEXT); cmd.addQuery(String.format("?%s=%s", name, text)); next(); } - private void lessExpr(String name) { + private void lessExpr(String name) throws ScanException { next(); // eat < cmd.addQuery(String.format("?<%s=%s", name, text)); next(); } - private void moreExpr(String name) { + private void moreExpr(String name) throws ScanException { next(); // eat > cmd.addQuery(String.format("?>%s=%s", name, text)); next(); @@ -140,8 +160,8 @@ class Parser { private void returns() throws ParseException { next(); - expect(Token.NAME); - List props = new LinkedList(); + expect(Token.TEXT); + List props = new LinkedList<>(); while (!(token == Token.EOL)) { if (token != Token.COMMA) { props.add(text); @@ -158,59 +178,27 @@ class Parser { throw new ParseException(String.format("Expected %s but found %s", Arrays.asList(tokens), this.token)); } - private void next() { - if (!words.isEmpty()) { - text = words.remove(0); - if (text.startsWith("/")) { - token = Token.COMMAND; - } else { - Token t = lookup.get(text); - if (t != null) { - token = t; - text = ""; - } else { - token = Token.NAME; - } - } - } else { - token = Token.EOL; - text = ""; + private void next() throws ScanException { +// Token was = token; + token = scanner.next(); + // System.out.printf("'%s' => '%s'\n", was, token); + while (token == Token.WS) { + token = scanner.next(); } - // System.out.printf("%s: %s\n", token, text); + text = scanner.text(); } - private Parser(String line) { + private Parser(String line) throws ScanException { line = line.trim(); - StringTokenizer st = new StringTokenizer(line, " \t,=", true); - boolean quote = false; - while (st.hasMoreElements()) { - String t = st.nextToken().trim(); - if (!t.equals("")) { - words.add(t); - } - } + scanner = new Scanner(line); + next(); } - private final List words = new LinkedList(); - private String text; + + + private final Scanner scanner; private Token token; + private String text; private Command cmd; - private static final Map lookup = new HashMap(); - private enum Token { - - COMMAND, WHERE, RETURN, EOL, NOT, AND, OR, NAME, EQUALS, MORE, LESS, COMMA; - } - - static { - lookup.put("where", Token.WHERE); - lookup.put("return", Token.RETURN); - lookup.put("not", Token.NOT); - lookup.put("and", Token.AND); - lookup.put("or", Token.OR); - lookup.put("=", Token.EQUALS); - lookup.put(">", Token.MORE); - lookup.put("<", Token.LESS); - lookup.put(",", Token.COMMA); - } } diff --git a/src/main/java/me/legrange/mikrotik/impl/ScanException.java b/src/main/java/me/legrange/mikrotik/impl/ScanException.java new file mode 100644 index 0000000..1463f2d --- /dev/null +++ b/src/main/java/me/legrange/mikrotik/impl/ScanException.java @@ -0,0 +1,21 @@ +package me.legrange.mikrotik.impl; + +import me.legrange.mikrotik.MikrotikApiException; + +/** + * Exception thrown if the scanner encounters an error while scanning a command line. + * @author GideonLeGrange + */ +public class ScanException extends ParseException { + + ScanException(String msg) { + super(msg); + } + + ScanException(String msg, Throwable err) { + super(msg, err); + } + + + +}