refactoring and added a static context

This commit is contained in:
hneemann 2018-03-12 19:36:03 +01:00
parent a5d104076a
commit 11588d270f
9 changed files with 193 additions and 134 deletions

View File

@ -42,7 +42,7 @@ public class Context {
* *
* @param name the name * @param name the name
* @return the value * @return the value
* @throws EvalException {@link EvalException * @throws EvalException EvalException
*/ */
public Object getVar(String name) throws EvalException { public Object getVar(String name) throws EvalException {
Object v = map.get(name); Object v = map.get(name);

View File

@ -84,7 +84,7 @@ public interface Expression {
static Object add(Object a, Object b) throws EvalException { static Object add(Object a, Object b) throws EvalException {
if (a instanceof Number && b instanceof Number) if (a instanceof Number && b instanceof Number)
return ((Number) a).longValue() + ((Number) b).longValue(); 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(); return a.toString() + b.toString();
throw new EvalException("arguments must be int or string, not " + a.getClass().getSimpleName() + "+" + b.getClass().getSimpleName()); throw new EvalException("arguments must be int or string, not " + a.getClass().getSimpleName() + "+" + b.getClass().getSimpleName());
} }

View File

@ -29,6 +29,7 @@ public class Parser {
private final Tokenizer tok; private final Tokenizer tok;
private HashMap<String, Function> functions; private HashMap<String, Function> functions;
private Context staticContext;
/** /**
* Create a new instance * Create a new instance
@ -47,23 +48,37 @@ public class Parser {
public Parser(Reader reader) { public Parser(Reader reader) {
tok = new Tokenizer(reader); tok = new Tokenizer(reader);
functions = new HashMap<>(); 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 @Override
public Object calcValue(Context c, ArrayList<Expression> args) { public Object calcValue(Context c, ArrayList<Expression> args) {
return new ArrayList<>(); return new ArrayList<>();
} }
}); });
functions.put("newMap", new Function(0) { addFunction("newMap", new Function(0) {
@Override @Override
public Object calcValue(Context c, ArrayList<Expression> args) { public Object calcValue(Context c, ArrayList<Expression> args) {
return new HashMap<>(); 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(); String text = tok.readText();
if (text.length() > 0) if (text.length() > 0)
s.add(c -> c.print(text)); s.add(c -> c.print(text));
while (!isToken(EOF)) while (!isToken(EOF)) {
s.add(parseStatement()); 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 s.optimize();
} }
/**
* @return the static context of this template
*/
public Context getStaticContext() {
return staticContext;
}
private Statement parseStatements() throws IOException, ParserException { private Statement parseStatements() throws IOException, ParserException {
if (isToken(OPENBRACE)) { if (isToken(OPENBRACE)) {
Statements s = new Statements(); Statements s = new Statements();

View File

@ -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;
}
}

View File

@ -17,7 +17,7 @@ public class Tokenizer {
enum Token { enum Token {
UNKNOWN, IDENT, AND, OR, XOR, NOT, OPEN, CLOSE, NUMBER, EOL, EOF, SHIFTLEFT, SHIFTRIGHT, COMMA, EQUAL, 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, 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<String, Token> statementMap = new HashMap<>(); private static HashMap<String, Token> statementMap = new HashMap<>();
@ -112,6 +112,9 @@ public class Tokenizer {
case '.': case '.':
token = Token.DOT; token = Token.DOT;
break; break;
case '@':
token = Token.STATIC;
break;
case ';': case ';':
token = Token.SEMICOLON; token = Token.SEMICOLON;
break; break;

View File

@ -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<Expression> 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);
}

View File

@ -32,23 +32,29 @@ public class ReferenceToArray implements Reference {
@Override @Override
public void set(Context context, Object value) throws EvalException { public void set(Context context, Object value) throws EvalException {
Object ar = parent.get(context); Object listObj = parent.get(context);
if (ar instanceof List) { if (listObj instanceof List) {
final List list = (List) ar; final List list = (List) listObj;
final int index = Expression.toInt(this.index.value(context)); final int i = Expression.toInt(index.value(context));
while (list.size() <= index) if (i < 0)
throw new EvalException("index out of bounds: " + i);
while (list.size() <= i)
list.add(null); list.add(null);
list.set(index, value); list.set(i, value);
} else } else
throw new EvalException("not an array: " + ar); throw new EvalException("not an array: " + listObj);
} }
@Override @Override
public Object get(Context context) throws EvalException { public Object get(Context context) throws EvalException {
Object ar = parent.get(context); Object listObj = parent.get(context);
if (ar instanceof List) if (listObj instanceof List) {
return ((List) ar).get(Expression.toInt(index.value(context))); final List list = (List) listObj;
else final int i = Expression.toInt(index.value(context));
throw new EvalException("not an array: " + ar); 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);
} }
} }

View File

@ -31,7 +31,7 @@ public class ReferenceToStruct implements Reference {
Key key = (Key) k.get(null); Key key = (Key) k.get(null);
KEY_MAP.put(key.getKey(), key); KEY_MAP.put(key.getKey(), key);
} catch (IllegalAccessException e) { } 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) if (m instanceof Map)
((Map) m).put(name, value); ((Map) m).put(name, value);
else else
throw new EvalException("not a map"); throw new EvalException("not a map: " + m);
} }
@Override @Override
@ -62,13 +62,13 @@ public class ReferenceToStruct implements Reference {
Object m = parent.get(context); Object m = parent.get(context);
if (m instanceof Map) if (m instanceof Map)
return ((Map) m).get(name); return ((Map) m).get(name);
if (m instanceof ElementAttributes) { else if (m instanceof ElementAttributes) {
Key key = KEY_MAP.get(name); Key key = KEY_MAP.get(name);
if (key == null) if (key == null)
throw new EvalException("invallid key: " + name); throw new EvalException("invalid key: " + name);
return ((ElementAttributes) m).get(key); return ((ElementAttributes) m).get(key);
} else } else
throw new EvalException("not a map"); throw new EvalException("not a map: " + m);
} }

View File

@ -7,9 +7,10 @@ package de.neemann.digital.hdl.hgs;
import de.neemann.digital.core.element.ElementAttributes; import de.neemann.digital.core.element.ElementAttributes;
import de.neemann.digital.core.element.Keys; import de.neemann.digital.core.element.Keys;
import de.neemann.digital.hdl.hgs.function.FuncAdapter;
import junit.framework.TestCase; import junit.framework.TestCase;
import java.io.*; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -36,13 +37,8 @@ public class ParserTest extends TestCase {
assertTrue(true); assertTrue(true);
} }
try { assertEquals("Hallo4", new Parser("\"Hallo\" + (2*2)").parseExpression().value(new Context()));
new Parser("1+\"Hallo\"").parseExpression().value(new Context()); assertEquals("Hallo_true", new Parser("\"Hallo_\" + (1<2)").parseExpression().value(new Context()));
fail();
} catch (EvalException e) {
assertTrue(true);
}
} }
public void testParseExpressionCompare() throws IOException, ParserException, EvalException { public void testParseExpressionCompare() throws IOException, ParserException, EvalException {
@ -82,73 +78,89 @@ public class ParserTest extends TestCase {
.value(new Context() .value(new Context()
.setVar("a", true))); .setVar("a", true)));
assertEquals("true", new Template("<? if (1) print(\"true\"); ?>").execute().toString()); assertEquals("true", exec("<? if (1) print(\"true\"); ?>").toString());
assertEquals("", new Template("<? if (0) print(\"true\"); ?>").execute().toString()); assertEquals("", exec("<? if (0) print(\"true\"); ?>").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 { public void testParseTemplateSimple() throws IOException, ParserException, EvalException {
Context c = new Template("Hello World!").execute(); assertEquals("Hello World!", exec("Hello World!", new Context()).toString());
assertEquals("Hello World!", c.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 { public void testParseTemplateVariable() throws IOException, ParserException, EvalException {
Context c = new Template("Hello <? =a ?> World!").execute(new Context().setVar("a", "My")); Context c = exec("Hello <? =a ?> World!", new Context().setVar("a", "My"));
assertEquals("Hello My World!", c.toString()); assertEquals("Hello My World!", c.toString());
} }
public void testParseTemplateCodeOnly() throws IOException, ParserException, EvalException { public void testParseTemplateCodeOnly() throws IOException, ParserException, EvalException {
Context c = new Template("<? =a ?>").execute(new Context().setVar("a", "My")); Context c = exec("<? =a ?>", new Context().setVar("a", "My"));
assertEquals("My", c.toString()); assertEquals("My", c.toString());
} }
public void testParseTemplatePrint() throws IOException, ParserException, EvalException { public void testParseTemplatePrint() throws IOException, ParserException, EvalException {
Context c = new Template("Hello <? print(\"My\"); ?> World!").execute(); Context c = exec("Hello <? print(\"My\"); ?> World!");
assertEquals("Hello My World!", c.toString()); assertEquals("Hello My World!", c.toString());
} }
public void testParseTemplateFor() throws IOException, ParserException, EvalException { public void testParseTemplateFor() throws IOException, ParserException, EvalException {
Context c = new Template("Hello <? for (i=0;i<10;i++) print(i); ?> World!").execute(); Context c = exec("Hello <? for (i=0;i<10;i++) print(i); ?> World!");
assertEquals("Hello 0123456789 World!", c.toString()); assertEquals("Hello 0123456789 World!", c.toString());
} }
public void testParseTemplateForStatements() throws IOException, ParserException, EvalException { public void testParseTemplateForStatements() throws IOException, ParserException, EvalException {
Context c = new Template("<? for (i=0;i<10;i++) { print(i, 9-i); } ?>").execute(); Context c = exec("<? for (i=0;i<10;i++) { print(i, 9-i); } ?>");
assertEquals("09182736455463728190", c.toString()); assertEquals("09182736455463728190", c.toString());
} }
public void testParseTemplateForNested() throws IOException, ParserException, EvalException { public void testParseTemplateForNested() throws IOException, ParserException, EvalException {
Context c = new Template("Hello <? for (i=0;i<10;i++) { ?>n<? } ?> World!").execute(); Context c = exec("Hello <? for (i=0;i<10;i++) { ?>n<? } ?> World!");
assertEquals("Hello nnnnnnnnnn World!", c.toString()); assertEquals("Hello nnnnnnnnnn World!", c.toString());
} }
public void testParseTemplateForNested2() throws IOException, ParserException, EvalException { public void testParseTemplateForNested2() throws IOException, ParserException, EvalException {
Context c = new Template("Hello <? for (i=0;i<3;i++) { ?>(<? for(j=0;j<2;j++) { ?>:<? } ?>)<? } ?> World!").execute(); Context c = exec("Hello <? for (i=0;i<3;i++) { ?>(<? for(j=0;j<2;j++) { ?>:<? } ?>)<? } ?> World!");
assertEquals("Hello (::)(::)(::) World!", c.toString()); assertEquals("Hello (::)(::)(::) World!", c.toString());
} }
public void testParseTemplateForNested3() throws IOException, ParserException, EvalException { public void testParseTemplateForNested3() throws IOException, ParserException, EvalException {
Context c = new Template("Hello <? for (i=1;i<4;i++) { ?>(<? for(j=1;j<3;j++) { print(i*j); } ?>)<? } ?> World!").execute(); Context c = exec("Hello <? for (i=1;i<4;i++) { ?>(<? for(j=1;j<3;j++) { print(i*j); } ?>)<? } ?> World!");
assertEquals("Hello (12)(24)(36) World!", c.toString()); assertEquals("Hello (12)(24)(36) World!", c.toString());
} }
public void testParseTemplateElementAttibutes() throws IOException, ParserException, EvalException { public void testParseTemplateElementAttibutes() throws IOException, ParserException, EvalException {
ElementAttributes attr = new ElementAttributes().set(Keys.BITS, 5); ElementAttributes attr = new ElementAttributes().set(Keys.BITS, 5);
Context c = new Context().setVar("elem", attr); Context c = exec("bits=<?=elem.Bits?>;", new Context().setVar("elem", attr));
new Template("bits=<?=elem.Bits?>;").execute(c);
assertEquals("bits=5;", c.toString()); assertEquals("bits=5;", c.toString());
} }
public void testParseTemplateFormat() throws IOException, ParserException, EvalException { public void testParseTemplateFormat() throws IOException, ParserException, EvalException {
ElementAttributes attr = new ElementAttributes().set(Keys.BITS, 17); ElementAttributes attr = new ElementAttributes().set(Keys.BITS, 17);
Context c = new Context().setVar("elem", attr); Context c = new Context().setVar("elem", attr);
new Template("<? a=format(\"hex=%x;\",elem.Bits); print(a);?>").execute(c); exec("<? a=format(\"hex=%x;\",elem.Bits); print(a);?>", c);
assertEquals("hex=11;", c.toString()); assertEquals("hex=11;", c.toString());
} }
public void testParseTemplateArray() throws IOException, ParserException, EvalException { public void testParseTemplateArray() throws IOException, ParserException, EvalException {
Context c = new Context().setVar("a", new ArrayList()); Context c = new Context().setVar("a", new ArrayList());
new Template("<? a[0]=1; a[1]=7; print(a[1]); ?>;").execute(c); exec("<? a[0]=1; a[1]=7; print(a[1]); ?>;", c);
assertEquals("7;", c.toString()); assertEquals("7;", c.toString());
Object lo = c.getVar("a"); Object lo = c.getVar("a");
assertTrue(lo instanceof List); assertTrue(lo instanceof List);
@ -159,7 +171,7 @@ public class ParserTest extends TestCase {
} }
public void testParseTemplateMap() throws IOException, ParserException, EvalException { public void testParseTemplateMap() throws IOException, ParserException, EvalException {
Context c = new Template("<? m=newMap(); m.test=newMap(); m.test.val=7; print(m.test.val); ?>;").execute(); Context c = exec("<? m=newMap(); m.test=newMap(); m.test.val=7; print(m.test.val); ?>;");
assertEquals("7;", c.toString()); assertEquals("7;", c.toString());
Object mo = c.getVar("m"); Object mo = c.getVar("m");
assertTrue(mo instanceof Map); assertTrue(mo instanceof Map);
@ -168,15 +180,63 @@ public class ParserTest extends TestCase {
assertEquals(7L, ((Map) mo).get("val")); assertEquals(7L, ((Map) mo).get("val"));
} }
public void testParseTemplateMapError() throws IOException, ParserException {
try {
Context c = exec("<? m=1; m.test=2; ?>;");
fail();
} catch (EvalException e) {
assertTrue(true);
}
}
public void testParseTemplateArrayError() throws IOException, ParserException {
try {
Context c = exec("<? m=1; m[0]=2; ?>;");
fail();
} catch (EvalException e) {
assertTrue(true);
}
}
public void testParseTemplateIsSet() throws IOException, ParserException, EvalException { public void testParseTemplateIsSet() throws IOException, ParserException, EvalException {
Template t = new Template("<? if (isset(m)) print(m); else print(\"false\"); ?>;"); Statement t = new Parser("<? if (isset(m)) print(m); else print(\"false\"); ?>;").parse();
assertEquals("false;", t.execute().toString()); assertEquals("false;", exec(t).toString());
assertEquals("4;", t.execute(new Context().setVar("m", 4)).toString()); assertEquals("4;", exec(t, new Context().setVar("m", 4)).toString());
} }
public void testComment() throws IOException, ParserException, EvalException { public void testComment() throws IOException, ParserException, EvalException {
Template t = new Template("<? // comment\nprint(\"false\"); // zzz\n ?>;"); Context c = exec("<? // comment\nprint(\"false\"); // zzz\n ?>;");
assertEquals("false;", t.execute().toString()); assertEquals("false;", c.toString());
}
public void testAddFunction() throws IOException, ParserException, EvalException {
Statement s = new Parser("a : in <?=type(elem.Bits)?>;")
.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; <? @gen[0]=\"a\"; ?>");
final ArrayList<Object> generics = new ArrayList<>();
p.getStaticContext().setVar("gen", generics);
p.parse();
assertEquals(1, generics.size());
assertEquals("a", generics.get(0));
} }