diff --git a/src/main/java/de/neemann/digital/hdl/hgs/FirstClassFunction.java b/src/main/java/de/neemann/digital/hdl/hgs/FirstClassFunction.java new file mode 100644 index 000000000..b87f7e2ad --- /dev/null +++ b/src/main/java/de/neemann/digital/hdl/hgs/FirstClassFunction.java @@ -0,0 +1,61 @@ +/* + * 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.util.ArrayList; + +/** + * callable first class function + */ +public class FirstClassFunction { + private final ArrayList args; + private final Statement st; + + /** + * Creates a new instance + * + * @param args the names of the arguments + * @param st the function body + */ + public FirstClassFunction(ArrayList args, Statement st) { + this.args = args; + this.st = st; + } + + /** + * Evaluates this function + * + * @param args the arguments + * @return the result + * @throws EvalException EvalException + */ + public Object evaluate(Object... args) throws EvalException { + if (args.length != this.args.size()) + throw new EvalException("wrong number of arguments! found: " + args.length + ", expected: " + this.args.size()); + + Context c = new Context(); + for (int i = 0; i < args.length; i++) + c.setVar(this.args.get(i), args[i]); + st.execute(c); + return c.getVar("return"); + } + + /** + * Calls a first class function + * + * @param context the context + * @param args the arguments + * @return the result + * @throws EvalException EvalException + */ + public Object calcValue(Context context, ArrayList args) throws EvalException { + Object[] data = new Object[args.size()]; + for (int i = 0; i < args.size(); i++) + data[i] = args.get(i).value(context); + + return evaluate(data); + } +} 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 a3bc574b5..65fc41052 100644 --- a/src/main/java/de/neemann/digital/hdl/hgs/Parser.java +++ b/src/main/java/de/neemann/digital/hdl/hgs/Parser.java @@ -415,13 +415,13 @@ public class Parser { } case NUMBER: long num = convToLong(tok.getIdent()); - return (c) -> num; + return c -> num; case STRING: String s = tok.getIdent(); - return (c) -> s; + return c -> s; case SUB: Expression negExp = parseIdent(); - return (c) -> -Expression.toLong(negExp.value(c)); + return c -> -Expression.toLong(negExp.value(c)); case NOT: Expression notExp = parseIdent(); return (c) -> Expression.not(notExp.value(c)); @@ -429,19 +429,45 @@ public class Parser { Expression exp = parseExpression(); expect(Tokenizer.Token.CLOSE); return exp; + case FUNC: + FirstClassFunction func = parseFunction(); + return c -> func; default: throw newUnexpectedToken(t); } } + private FirstClassFunction parseFunction() throws IOException, ParserException { + expect(OPEN); + ArrayList args = new ArrayList<>(); + if (!isToken(CLOSE)) { + expect(IDENT); + args.add(tok.getIdent()); + while (!isToken(CLOSE)) { + expect(COMMA); + expect(IDENT); + args.add(tok.getIdent()); + } + } + Statement st = parseStatements(); + return new FirstClassFunction(args, st); + } + private Expression findFunction(String name, ArrayList args) throws ParserException { Function f = functions.get(name); - if (f == null) - throw newParserException("function " + name + " not found"); - if (f.getArgCount() != args.size() && f.getArgCount() >= 0) - throw newParserException("function " + name + " needs " + f.getArgCount() + "arguments, but found " + args.size()); - - return (c) -> f.calcValue(c, args); + if (f != null) { + if (f.getArgCount() != args.size() && f.getArgCount() >= 0) + throw newParserException("function " + name + " needs " + f.getArgCount() + "arguments, but found " + args.size()); + return c -> f.calcValue(c, args); + } else { + return c -> { + Object func = c.getVar(name); + if (func instanceof FirstClassFunction) + return ((FirstClassFunction) func).calcValue(c, args); + else + throw new EvalException("first class function " + name + " not found"); + }; + } } } 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 b8ba91ad8..eb44a437c 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, STATIC, PRINTF + OPENBRACE, CLOSEDBRACE, CODEEND, OPENSQUARE, CLOSEDSQUARE, DOT, PRINT, STATIC, FUNC, PRINTF } private static HashMap statementMap = new HashMap<>(); @@ -29,6 +29,7 @@ public class Tokenizer { statementMap.put("while", Token.FOR); statementMap.put("print", Token.PRINT); statementMap.put("printf", Token.PRINTF); + statementMap.put("func", Token.FUNC); } private final Reader in; 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 7685968b7..244b8695b 100644 --- a/src/test/java/de/neemann/digital/hdl/hgs/ParserTest.java +++ b/src/test/java/de/neemann/digital/hdl/hgs/ParserTest.java @@ -182,7 +182,7 @@ public class ParserTest extends TestCase { public void testParseTemplateMapError() throws IOException, ParserException { try { - Context c = exec(";"); + exec(";"); fail(); } catch (EvalException e) { assertTrue(true); @@ -191,7 +191,7 @@ public class ParserTest extends TestCase { public void testParseTemplateArrayError() throws IOException, ParserException { try { - Context c = exec(";"); + exec(";"); fail(); } catch (EvalException e) { assertTrue(true); @@ -239,13 +239,29 @@ public class ParserTest extends TestCase { assertEquals("a", generics.get(0)); } + public void testFirstClassFunctionStatic() throws IOException, ParserException, EvalException { + Parser p = new Parser(""); + p.parse(); + Object fObj = p.getStaticContext().getVar("f"); + assertTrue(fObj instanceof FirstClassFunction); + FirstClassFunction f = (FirstClassFunction) fObj; + assertEquals(11L, f.evaluate(3)); + } + + public void testFirstClassFunction() throws IOException, ParserException, EvalException { + assertEquals("18", exec("").toString()); + assertEquals("5", exec("").toString()); + assertEquals("13", exec("", + new Context().setVar("a", 3)).toString()); + } + // public void testCode() throws IOException, ParserException { // File dir = new File("/home/hneemann/temp/Digital/ideras/Digital/src/main/resources/verilog"); // for (File f : dir.listFiles()) { // try (Reader r = new FileReader(f)) { // System.out.println(f); -// new Template(r); +// new Parser(r).parse(); // } // } // }