added first class functions

This commit is contained in:
hneemann 2018-03-12 20:26:34 +01:00
parent 11588d270f
commit 4d15ea268a
4 changed files with 117 additions and 13 deletions

View File

@ -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<String> args;
private final Statement st;
/**
* Creates a new instance
*
* @param args the names of the arguments
* @param st the function body
*/
public FirstClassFunction(ArrayList<String> 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<Expression> 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);
}
}

View File

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

View File

@ -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<String, Token> 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;

View File

@ -182,7 +182,7 @@ public class ParserTest extends TestCase {
public void testParseTemplateMapError() throws IOException, ParserException {
try {
Context c = exec("<? m=1; m.test=2; ?>;");
exec("<? m=1; m.test=2; ?>;");
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("<? m=1; m[0]=2; ?>;");
exec("<? m=1; m[0]=2; ?>;");
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("<? @f=func(a){return=a*a+2;}; print(f(4));?>");
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("<? f=func(a){return=a*a+2;}; print(f(4));?>").toString());
assertEquals("5", exec("<? f=func(a,b){return=a+2*b;}; print(f(1,2));?>").toString());
assertEquals("13", exec("<? f=func(a,b){return=a+2*b;}; print(f(1,a*2));?>",
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();
// }
// }
// }