mirror of
https://github.com/hneemann/Digital.git
synced 2025-09-16 16:34:47 -04:00
refactoring and added a static context
This commit is contained in:
parent
a5d104076a
commit
11588d270f
@ -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);
|
||||
|
@ -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());
|
||||
}
|
||||
|
@ -29,6 +29,7 @@ public class Parser {
|
||||
|
||||
private final Tokenizer tok;
|
||||
private HashMap<String, Function> 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<Expression> args) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
});
|
||||
|
||||
functions.put("newMap", new Function(0) {
|
||||
addFunction("newMap", new Function(0) {
|
||||
@Override
|
||||
public Object calcValue(Context c, ArrayList<Expression> 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();
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
@ -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<String, Token> 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;
|
||||
|
@ -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);
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
@ -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("<? if (1) print(\"true\"); ?>").execute().toString());
|
||||
assertEquals("", new Template("<? if (0) print(\"true\"); ?>").execute().toString());
|
||||
assertEquals("true", exec("<? if (1) print(\"true\"); ?>").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 {
|
||||
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 <? =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());
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
|
||||
|
||||
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=<?=elem.Bits?>;").execute(c);
|
||||
Context c = exec("bits=<?=elem.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("<? 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());
|
||||
}
|
||||
|
||||
public void testParseTemplateArray() throws IOException, ParserException, EvalException {
|
||||
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());
|
||||
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("<? 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());
|
||||
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("<? 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 {
|
||||
Template t = new Template("<? if (isset(m)) print(m); else print(\"false\"); ?>;");
|
||||
assertEquals("false;", t.execute().toString());
|
||||
assertEquals("4;", t.execute(new Context().setVar("m", 4)).toString());
|
||||
Statement t = new Parser("<? if (isset(m)) print(m); else print(\"false\"); ?>;").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("<? // comment\nprint(\"false\"); // zzz\n ?>;");
|
||||
assertEquals("false;", t.execute().toString());
|
||||
Context c = exec("<? // comment\nprint(\"false\"); // zzz\n ?>;");
|
||||
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));
|
||||
}
|
||||
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user