From 11588d270f89be5e3bdab9fb0faf92a0c2df9fba Mon Sep 17 00:00:00 2001 From: hneemann Date: Mon, 12 Mar 2018 19:36:03 +0100 Subject: [PATCH] refactoring and added a static context --- .../de/neemann/digital/hdl/hgs/Context.java | 2 +- .../neemann/digital/hdl/hgs/Expression.java | 2 +- .../de/neemann/digital/hdl/hgs/Parser.java | 44 ++++++- .../de/neemann/digital/hdl/hgs/Template.java | 77 ----------- .../de/neemann/digital/hdl/hgs/Tokenizer.java | 5 +- .../digital/hdl/hgs/function/FuncAdapter.java | 37 ++++++ .../hdl/hgs/refs/ReferenceToArray.java | 30 +++-- .../hdl/hgs/refs/ReferenceToStruct.java | 10 +- .../neemann/digital/hdl/hgs/ParserTest.java | 120 +++++++++++++----- 9 files changed, 193 insertions(+), 134 deletions(-) delete mode 100644 src/main/java/de/neemann/digital/hdl/hgs/Template.java create mode 100644 src/main/java/de/neemann/digital/hdl/hgs/function/FuncAdapter.java diff --git a/src/main/java/de/neemann/digital/hdl/hgs/Context.java b/src/main/java/de/neemann/digital/hdl/hgs/Context.java index 36608eba8..645931ca7 100644 --- a/src/main/java/de/neemann/digital/hdl/hgs/Context.java +++ b/src/main/java/de/neemann/digital/hdl/hgs/Context.java @@ -42,7 +42,7 @@ public class Context { * * @param name the name * @return the value - * @throws EvalException {@link EvalException + * @throws EvalException EvalException */ public Object getVar(String name) throws EvalException { Object v = map.get(name); diff --git a/src/main/java/de/neemann/digital/hdl/hgs/Expression.java b/src/main/java/de/neemann/digital/hdl/hgs/Expression.java index 43e5d9d20..246484fdd 100644 --- a/src/main/java/de/neemann/digital/hdl/hgs/Expression.java +++ b/src/main/java/de/neemann/digital/hdl/hgs/Expression.java @@ -84,7 +84,7 @@ public interface Expression { static Object add(Object a, Object b) throws EvalException { if (a instanceof Number && b instanceof Number) return ((Number) a).longValue() + ((Number) b).longValue(); - if (a instanceof String && b instanceof String) + if (a instanceof String || b instanceof String) return a.toString() + b.toString(); throw new EvalException("arguments must be int or string, not " + a.getClass().getSimpleName() + "+" + b.getClass().getSimpleName()); } diff --git a/src/main/java/de/neemann/digital/hdl/hgs/Parser.java b/src/main/java/de/neemann/digital/hdl/hgs/Parser.java index 8a83e7b57..a3bc574b5 100644 --- a/src/main/java/de/neemann/digital/hdl/hgs/Parser.java +++ b/src/main/java/de/neemann/digital/hdl/hgs/Parser.java @@ -29,6 +29,7 @@ public class Parser { private final Tokenizer tok; private HashMap functions; + private Context staticContext; /** * Create a new instance @@ -47,23 +48,37 @@ public class Parser { public Parser(Reader reader) { tok = new Tokenizer(reader); functions = new HashMap<>(); - functions.put("format", new FunctionFormat()); + addFunction("format", new FunctionFormat()); - functions.put("isset", new FunctionIsSet()); + addFunction("isset", new FunctionIsSet()); - functions.put("newList", new Function(0) { + addFunction("newList", new Function(0) { @Override public Object calcValue(Context c, ArrayList args) { return new ArrayList<>(); } }); - functions.put("newMap", new Function(0) { + addFunction("newMap", new Function(0) { @Override public Object calcValue(Context c, ArrayList args) { return new HashMap<>(); } }); + + staticContext = new Context(); + } + + /** + * Adds a new function to the parser + * + * @param name the name + * @param function the function + * @return this for chained calls + */ + public Parser addFunction(String name, Function function) { + functions.put(name, function); + return this; } /** @@ -78,12 +93,27 @@ public class Parser { String text = tok.readText(); if (text.length() > 0) s.add(c -> c.print(text)); - while (!isToken(EOF)) - s.add(parseStatement()); - + while (!isToken(EOF)) { + if (isToken(STATIC)) { + Statement stat = parseStatements(); + try { + stat.execute(staticContext); + } catch (EvalException e) { + throw newParserException("error evaluating static code: " + e.getMessage()); + } + } else + s.add(parseStatement()); + } return s.optimize(); } + /** + * @return the static context of this template + */ + public Context getStaticContext() { + return staticContext; + } + private Statement parseStatements() throws IOException, ParserException { if (isToken(OPENBRACE)) { Statements s = new Statements(); diff --git a/src/main/java/de/neemann/digital/hdl/hgs/Template.java b/src/main/java/de/neemann/digital/hdl/hgs/Template.java deleted file mode 100644 index af2a0be65..000000000 --- a/src/main/java/de/neemann/digital/hdl/hgs/Template.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright (c) 2018 Helmut Neemann. - * Use of this source code is governed by the GPL v3 license - * that can be found in the LICENSE file. - */ -package de.neemann.digital.hdl.hgs; - -import java.io.*; - -/** - * Represents a template - */ -public class Template { - private final Statement s; - - /** - * Creates a new template. - * - * @param text the text to parse - * @throws IOException IOException - * @throws ParserException ParserException - */ - public Template(String text) throws IOException, ParserException { - this(new StringReader(text)); - } - - /** - * Creates a new template. - * - * @param in the text to parse - * @throws IOException IOException - * @throws ParserException ParserException - */ - public Template(InputStream in) throws IOException, ParserException { - this(new InputStreamReader(in)); - } - - /** - * Creates a new template. - * - * @param reader the text to parse - * @throws IOException IOException - * @throws ParserException ParserException - */ - public Template(Reader reader) throws IOException, ParserException { - try { - this.s = new Parser(reader).parse(); - } finally { - reader.close(); - } - } - - - /** - * Evaluates the template with an empty context - * - * @return the context - * @throws EvalException EvalException - */ - public Context execute() throws EvalException { - return execute(new Context()); - } - - /** - * Evaluates the template with the given context. - * Create a new context every time this method is called! - * - * @param c the context - * @return the context - * @throws EvalException EvalException - */ - public Context execute(Context c) throws EvalException { - s.execute(c); - return c; - } - -} diff --git a/src/main/java/de/neemann/digital/hdl/hgs/Tokenizer.java b/src/main/java/de/neemann/digital/hdl/hgs/Tokenizer.java index b448d47b1..b8ba91ad8 100644 --- a/src/main/java/de/neemann/digital/hdl/hgs/Tokenizer.java +++ b/src/main/java/de/neemann/digital/hdl/hgs/Tokenizer.java @@ -17,7 +17,7 @@ public class Tokenizer { enum Token { UNKNOWN, IDENT, AND, OR, XOR, NOT, OPEN, CLOSE, NUMBER, EOL, EOF, SHIFTLEFT, SHIFTRIGHT, COMMA, EQUAL, ADD, SUB, MUL, GREATER, SMALER, DIV, MOD, END, IF, ELSE, FOR, WHILE, SEMICOLON, NOTEQUAL, STRING, - OPENBRACE, CLOSEDBRACE, CODEEND, OPENSQUARE, CLOSEDSQUARE, DOT, PRINT, PRINTF + OPENBRACE, CLOSEDBRACE, CODEEND, OPENSQUARE, CLOSEDSQUARE, DOT, PRINT, STATIC, PRINTF } private static HashMap statementMap = new HashMap<>(); @@ -112,6 +112,9 @@ public class Tokenizer { case '.': token = Token.DOT; break; + case '@': + token = Token.STATIC; + break; case ';': token = Token.SEMICOLON; break; diff --git a/src/main/java/de/neemann/digital/hdl/hgs/function/FuncAdapter.java b/src/main/java/de/neemann/digital/hdl/hgs/function/FuncAdapter.java new file mode 100644 index 000000000..0d67662b7 --- /dev/null +++ b/src/main/java/de/neemann/digital/hdl/hgs/function/FuncAdapter.java @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2018 Helmut Neemann. + * Use of this source code is governed by the GPL v3 license + * that can be found in the LICENSE file. + */ +package de.neemann.digital.hdl.hgs.function; + +import de.neemann.digital.hdl.hgs.Context; +import de.neemann.digital.hdl.hgs.EvalException; +import de.neemann.digital.hdl.hgs.Expression; + +import java.util.ArrayList; + +/** + * Simple function adapter to implement a function with one argument of type long + */ +public abstract class FuncAdapter extends Function { + /** + * Creates a new function + */ + public FuncAdapter() { + super(1); + } + + @Override + public Object calcValue(Context c, ArrayList args) throws EvalException { + return f(Expression.toLong(args.get(0).value(c))); + } + + /** + * The function + * + * @param n the argument + * @return the result + */ + protected abstract Object f(long n); +} diff --git a/src/main/java/de/neemann/digital/hdl/hgs/refs/ReferenceToArray.java b/src/main/java/de/neemann/digital/hdl/hgs/refs/ReferenceToArray.java index cb0d5eda7..8abe5a1d0 100644 --- a/src/main/java/de/neemann/digital/hdl/hgs/refs/ReferenceToArray.java +++ b/src/main/java/de/neemann/digital/hdl/hgs/refs/ReferenceToArray.java @@ -32,23 +32,29 @@ public class ReferenceToArray implements Reference { @Override public void set(Context context, Object value) throws EvalException { - Object ar = parent.get(context); - if (ar instanceof List) { - final List list = (List) ar; - final int index = Expression.toInt(this.index.value(context)); - while (list.size() <= index) + Object listObj = parent.get(context); + if (listObj instanceof List) { + final List list = (List) listObj; + final int i = Expression.toInt(index.value(context)); + if (i < 0) + throw new EvalException("index out of bounds: " + i); + while (list.size() <= i) list.add(null); - list.set(index, value); + list.set(i, value); } else - throw new EvalException("not an array: " + ar); + throw new EvalException("not an array: " + listObj); } @Override public Object get(Context context) throws EvalException { - Object ar = parent.get(context); - if (ar instanceof List) - return ((List) ar).get(Expression.toInt(index.value(context))); - else - throw new EvalException("not an array: " + ar); + Object listObj = parent.get(context); + if (listObj instanceof List) { + final List list = (List) listObj; + final int i = Expression.toInt(index.value(context)); + if (i >= list.size() || i < 0) + throw new EvalException("index out of bounds: " + i); + return list.get(i); + } else + throw new EvalException("not an array: " + listObj); } } diff --git a/src/main/java/de/neemann/digital/hdl/hgs/refs/ReferenceToStruct.java b/src/main/java/de/neemann/digital/hdl/hgs/refs/ReferenceToStruct.java index ba9d24ce7..e0d8c625b 100644 --- a/src/main/java/de/neemann/digital/hdl/hgs/refs/ReferenceToStruct.java +++ b/src/main/java/de/neemann/digital/hdl/hgs/refs/ReferenceToStruct.java @@ -31,7 +31,7 @@ public class ReferenceToStruct implements Reference { Key key = (Key) k.get(null); KEY_MAP.put(key.getKey(), key); } catch (IllegalAccessException e) { - throw new RuntimeException("error ecessing the Keys"); + throw new RuntimeException("error accessing the Keys"); } } } @@ -54,7 +54,7 @@ public class ReferenceToStruct implements Reference { if (m instanceof Map) ((Map) m).put(name, value); else - throw new EvalException("not a map"); + throw new EvalException("not a map: " + m); } @Override @@ -62,13 +62,13 @@ public class ReferenceToStruct implements Reference { Object m = parent.get(context); if (m instanceof Map) return ((Map) m).get(name); - if (m instanceof ElementAttributes) { + else if (m instanceof ElementAttributes) { Key key = KEY_MAP.get(name); if (key == null) - throw new EvalException("invallid key: " + name); + throw new EvalException("invalid key: " + name); return ((ElementAttributes) m).get(key); } else - throw new EvalException("not a map"); + throw new EvalException("not a map: " + m); } diff --git a/src/test/java/de/neemann/digital/hdl/hgs/ParserTest.java b/src/test/java/de/neemann/digital/hdl/hgs/ParserTest.java index 689909b86..7685968b7 100644 --- a/src/test/java/de/neemann/digital/hdl/hgs/ParserTest.java +++ b/src/test/java/de/neemann/digital/hdl/hgs/ParserTest.java @@ -7,9 +7,10 @@ package de.neemann.digital.hdl.hgs; import de.neemann.digital.core.element.ElementAttributes; import de.neemann.digital.core.element.Keys; +import de.neemann.digital.hdl.hgs.function.FuncAdapter; import junit.framework.TestCase; -import java.io.*; +import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -36,13 +37,8 @@ public class ParserTest extends TestCase { assertTrue(true); } - try { - new Parser("1+\"Hallo\"").parseExpression().value(new Context()); - fail(); - } catch (EvalException e) { - assertTrue(true); - } - + assertEquals("Hallo4", new Parser("\"Hallo\" + (2*2)").parseExpression().value(new Context())); + assertEquals("Hallo_true", new Parser("\"Hallo_\" + (1<2)").parseExpression().value(new Context())); } public void testParseExpressionCompare() throws IOException, ParserException, EvalException { @@ -82,73 +78,89 @@ public class ParserTest extends TestCase { .value(new Context() .setVar("a", true))); - assertEquals("true", new Template("").execute().toString()); - assertEquals("", new Template("").execute().toString()); + assertEquals("true", exec("").toString()); + assertEquals("", exec("").toString()); + } + + private Context exec(String code) throws IOException, ParserException, EvalException { + return exec(code, new Context()); + } + + private Context exec(String code, Context c) throws IOException, ParserException, EvalException { + new Parser(code).parse().execute(c); + return c; } public void testParseTemplateSimple() throws IOException, ParserException, EvalException { - Context c = new Template("Hello World!").execute(); - assertEquals("Hello World!", c.toString()); + assertEquals("Hello World!", exec("Hello World!", new Context()).toString()); + } + + private Context exec(Statement s) throws EvalException { + return exec(s, new Context()); + } + + private Context exec(Statement s, Context c) throws EvalException { + s.execute(c); + return c; } public void testParseTemplateVariable() throws IOException, ParserException, EvalException { - Context c = new Template("Hello World!").execute(new Context().setVar("a", "My")); + Context c = exec("Hello World!", new Context().setVar("a", "My")); assertEquals("Hello My World!", c.toString()); } public void testParseTemplateCodeOnly() throws IOException, ParserException, EvalException { - Context c = new Template("").execute(new Context().setVar("a", "My")); + Context c = exec("", new Context().setVar("a", "My")); assertEquals("My", c.toString()); } public void testParseTemplatePrint() throws IOException, ParserException, EvalException { - Context c = new Template("Hello World!").execute(); + Context c = exec("Hello World!"); assertEquals("Hello My World!", c.toString()); } public void testParseTemplateFor() throws IOException, ParserException, EvalException { - Context c = new Template("Hello World!").execute(); + Context c = exec("Hello World!"); assertEquals("Hello 0123456789 World!", c.toString()); } public void testParseTemplateForStatements() throws IOException, ParserException, EvalException { - Context c = new Template("").execute(); + Context c = exec(""); assertEquals("09182736455463728190", c.toString()); } public void testParseTemplateForNested() throws IOException, ParserException, EvalException { - Context c = new Template("Hello n World!").execute(); + Context c = exec("Hello n World!"); assertEquals("Hello nnnnnnnnnn World!", c.toString()); } public void testParseTemplateForNested2() throws IOException, ParserException, EvalException { - Context c = new Template("Hello (:) World!").execute(); + Context c = exec("Hello (:) World!"); assertEquals("Hello (::)(::)(::) World!", c.toString()); } public void testParseTemplateForNested3() throws IOException, ParserException, EvalException { - Context c = new Template("Hello () World!").execute(); + Context c = exec("Hello () World!"); assertEquals("Hello (12)(24)(36) World!", c.toString()); } public void testParseTemplateElementAttibutes() throws IOException, ParserException, EvalException { ElementAttributes attr = new ElementAttributes().set(Keys.BITS, 5); - Context c = new Context().setVar("elem", attr); - new Template("bits=;").execute(c); + Context c = exec("bits=;", new Context().setVar("elem", attr)); assertEquals("bits=5;", c.toString()); } public void testParseTemplateFormat() throws IOException, ParserException, EvalException { ElementAttributes attr = new ElementAttributes().set(Keys.BITS, 17); Context c = new Context().setVar("elem", attr); - new Template("").execute(c); + exec("", c); assertEquals("hex=11;", c.toString()); } public void testParseTemplateArray() throws IOException, ParserException, EvalException { Context c = new Context().setVar("a", new ArrayList()); - new Template(";").execute(c); + exec(";", c); assertEquals("7;", c.toString()); Object lo = c.getVar("a"); assertTrue(lo instanceof List); @@ -159,7 +171,7 @@ public class ParserTest extends TestCase { } public void testParseTemplateMap() throws IOException, ParserException, EvalException { - Context c = new Template(";").execute(); + Context c = exec(";"); assertEquals("7;", c.toString()); Object mo = c.getVar("m"); assertTrue(mo instanceof Map); @@ -168,15 +180,63 @@ public class ParserTest extends TestCase { assertEquals(7L, ((Map) mo).get("val")); } + public void testParseTemplateMapError() throws IOException, ParserException { + try { + Context c = exec(";"); + fail(); + } catch (EvalException e) { + assertTrue(true); + } + } + + public void testParseTemplateArrayError() throws IOException, ParserException { + try { + Context c = exec(";"); + fail(); + } catch (EvalException e) { + assertTrue(true); + } + } + public void testParseTemplateIsSet() throws IOException, ParserException, EvalException { - Template t = new Template(";"); - assertEquals("false;", t.execute().toString()); - assertEquals("4;", t.execute(new Context().setVar("m", 4)).toString()); + Statement t = new Parser(";").parse(); + assertEquals("false;", exec(t).toString()); + assertEquals("4;", exec(t, new Context().setVar("m", 4)).toString()); } public void testComment() throws IOException, ParserException, EvalException { - Template t = new Template(";"); - assertEquals("false;", t.execute().toString()); + Context c = exec(";"); + assertEquals("false;", c.toString()); + } + + public void testAddFunction() throws IOException, ParserException, EvalException { + Statement s = new Parser("a : in ;") + .addFunction("type", new FuncAdapter() { + @Override + protected Object f(long n) { + if (n == 1) + return "std_logic"; + else + return "std_logic_vector(" + (n - 1) + " downto 0)"; + } + }) + .parse(); + assertEquals("a : in std_logic;", + exec(s, new Context() + .setVar("elem", new ElementAttributes())).toString()); + assertEquals("a : in std_logic_vector(5 downto 0);", + exec(s, new Context() + .setVar("elem", new ElementAttributes().setBits(6))).toString()); + } + + public void testStatic() throws IOException, ParserException { + Parser p = new Parser("generic a; "); + final ArrayList generics = new ArrayList<>(); + p.getStaticContext().setVar("gen", generics); + p.parse(); + + assertEquals(1, generics.size()); + assertEquals("a", generics.get(0)); }