mirror of
https://github.com/hneemann/Digital.git
synced 2025-09-17 17:04:42 -04:00
added first class functions
This commit is contained in:
parent
11588d270f
commit
4d15ea268a
@ -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);
|
||||
}
|
||||
}
|
@ -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");
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
Loading…
x
Reference in New Issue
Block a user