mirror of
https://github.com/hneemann/Digital.git
synced 2025-09-17 08:55:05 -04:00
added lambdas and a return statement
This commit is contained in:
parent
c03e29a710
commit
05b19ddcfb
@ -19,6 +19,7 @@ public class Context {
|
||||
private final Context parent;
|
||||
private final StringBuilder code;
|
||||
private HashMap<String, Object> map;
|
||||
private boolean functionContext = false;
|
||||
|
||||
/**
|
||||
* Creates a new context
|
||||
@ -156,6 +157,45 @@ public class Context {
|
||||
return parent.length();
|
||||
}
|
||||
|
||||
/**
|
||||
* Flags this context as context belonging to a function call.
|
||||
* This allows to use the return statement.
|
||||
*
|
||||
* @return this for chained calls
|
||||
*/
|
||||
public Context isFunctionContext() {
|
||||
functionContext = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns from a function call.
|
||||
*
|
||||
* @param returnValue the return value
|
||||
* @throws HGSEvalException HGSEvalException
|
||||
*/
|
||||
public void returnFromFunc(Object returnValue) throws HGSEvalException {
|
||||
if (!functionContext)
|
||||
throw new HGSEvalException("The return statement is allowed only in a function!");
|
||||
|
||||
throw new ReturnException(returnValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a function from this context
|
||||
*
|
||||
* @param funcName the functions name
|
||||
* @return the function
|
||||
* @throws HGSEvalException HGSEvalException
|
||||
*/
|
||||
public FuncAdapter getFunction(String funcName) throws HGSEvalException {
|
||||
Object fObj = getVar(funcName);
|
||||
if (fObj instanceof FuncAdapter)
|
||||
return (FuncAdapter) fObj;
|
||||
else
|
||||
throw new HGSEvalException("Variable '" + funcName + "' is not a function");
|
||||
}
|
||||
|
||||
private static final class FunctionPrint extends Function {
|
||||
|
||||
private FunctionPrint() {
|
||||
@ -163,7 +203,7 @@ public class Context {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object calcValue(Context c, ArrayList<Expression> args) throws HGSEvalException {
|
||||
public Object callWithExpressions(Context c, ArrayList<Expression> args) throws HGSEvalException {
|
||||
for (Expression arg : args)
|
||||
c.print(arg.value(c).toString());
|
||||
return null;
|
||||
@ -177,7 +217,7 @@ public class Context {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object calcValue(Context c, ArrayList<Expression> args) throws HGSEvalException {
|
||||
public Object callWithExpressions(Context c, ArrayList<Expression> args) throws HGSEvalException {
|
||||
c.print(format(c, args));
|
||||
return null;
|
||||
}
|
||||
@ -190,7 +230,7 @@ public class Context {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object calcValue(Context c, ArrayList<Expression> args) throws HGSEvalException {
|
||||
public Object callWithExpressions(Context c, ArrayList<Expression> args) throws HGSEvalException {
|
||||
return format(c, args);
|
||||
}
|
||||
}
|
||||
@ -213,7 +253,7 @@ public class Context {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object calcValue(Context c, ArrayList<Expression> args) {
|
||||
public Object callWithExpressions(Context c, ArrayList<Expression> args) {
|
||||
try {
|
||||
args.get(0).value(c);
|
||||
return true;
|
||||
@ -233,4 +273,28 @@ public class Context {
|
||||
throw new HGSEvalException(args[0].toString());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Exception used to return a value from a function
|
||||
*/
|
||||
public static final class ReturnException extends HGSEvalException {
|
||||
private final Object returnValue;
|
||||
|
||||
/**
|
||||
* Creates a new instance
|
||||
*
|
||||
* @param returnValue the return value
|
||||
*/
|
||||
ReturnException(Object returnValue) {
|
||||
super("return");
|
||||
this.returnValue = returnValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the return value
|
||||
*/
|
||||
public Object getReturnValue() {
|
||||
return returnValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -7,6 +7,7 @@ package de.neemann.digital.hdl.hgs;
|
||||
|
||||
import de.neemann.digital.core.Bits;
|
||||
import de.neemann.digital.hdl.hgs.function.FirstClassFunction;
|
||||
import de.neemann.digital.hdl.hgs.function.FirstClassFunctionCall;
|
||||
import de.neemann.digital.hdl.hgs.refs.*;
|
||||
|
||||
import java.io.IOException;
|
||||
@ -179,6 +180,10 @@ public class Parser {
|
||||
while (!nextIs(CLOSEDBRACE))
|
||||
s.add(parseStatement());
|
||||
return s.optimize();
|
||||
case RETURN:
|
||||
Expression retExp = parseExpression();
|
||||
expect(SEMICOLON);
|
||||
return c -> c.returnFromFunc(retExp.value(c));
|
||||
default:
|
||||
throw newUnexpectedToken(token);
|
||||
}
|
||||
@ -413,7 +418,7 @@ public class Parser {
|
||||
return exp;
|
||||
case FUNC:
|
||||
FirstClassFunction func = parseFunction();
|
||||
return c -> func;
|
||||
return c -> new FirstClassFunctionCall(func, c);
|
||||
default:
|
||||
throw newUnexpectedToken(t);
|
||||
}
|
||||
|
@ -18,7 +18,7 @@ class Tokenizer {
|
||||
UNKNOWN, IDENT, AND, OR, XOR, NOT, OPEN, CLOSE, NUMBER, EOL, EOF, SHIFTLEFT, SHIFTRIGHT, COMMA, EQUAL,
|
||||
ADD, SUB, MUL, GREATER, LESS, DIV, MOD, END, IF, ELSE, FOR, WHILE, SEMICOLON, NOTEQUAL, STRING,
|
||||
OPENBRACE, CLOSEDBRACE, CODEEND, OPENSQUARE, CLOSEDSQUARE, DOT, STATIC, FUNC, GREATEREQUAL, LESSEQUAL,
|
||||
REPEAT, UNTIL
|
||||
REPEAT, RETURN, UNTIL
|
||||
}
|
||||
|
||||
private static HashMap<String, Token> statementMap = new HashMap<>();
|
||||
@ -31,6 +31,7 @@ class Tokenizer {
|
||||
statementMap.put("func", Token.FUNC);
|
||||
statementMap.put("repeat", Token.REPEAT);
|
||||
statementMap.put("until", Token.UNTIL);
|
||||
statementMap.put("return", Token.RETURN);
|
||||
}
|
||||
|
||||
private final Reader in;
|
||||
|
@ -5,48 +5,33 @@
|
||||
*/
|
||||
package de.neemann.digital.hdl.hgs.function;
|
||||
|
||||
import de.neemann.digital.hdl.hgs.Context;
|
||||
import de.neemann.digital.hdl.hgs.HGSEvalException;
|
||||
import de.neemann.digital.hdl.hgs.Statement;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* callable first class function
|
||||
* Description of a first class function.
|
||||
*/
|
||||
public class FirstClassFunction extends FuncAdapter {
|
||||
public class FirstClassFunction {
|
||||
private final ArrayList<String> args;
|
||||
private final Statement st;
|
||||
private final Statement statement;
|
||||
|
||||
/**
|
||||
* Creates a new instance
|
||||
*
|
||||
* @param args the names of the arguments
|
||||
* @param st the function body
|
||||
* @param args the names of the arguments
|
||||
* @param statement the function body
|
||||
*/
|
||||
public FirstClassFunction(ArrayList<String> args, Statement st) {
|
||||
super(args.size());
|
||||
public FirstClassFunction(ArrayList<String> args, Statement statement) {
|
||||
this.args = args;
|
||||
this.st = st;
|
||||
this.statement = statement;
|
||||
}
|
||||
|
||||
/**
|
||||
* Evaluates this function
|
||||
*
|
||||
* @param args the arguments
|
||||
* @return the result
|
||||
* @throws HGSEvalException HGSEvalException
|
||||
*/
|
||||
@Override
|
||||
public Object f(Object... args) throws HGSEvalException {
|
||||
Context c = new Context();
|
||||
for (int i = 0; i < args.length; i++)
|
||||
c.setVar(this.args.get(i), args[i]);
|
||||
st.execute(c);
|
||||
if (c.contains("return"))
|
||||
return c.getVar("return");
|
||||
else
|
||||
throw new HGSEvalException("A function must define the variable 'return'!");
|
||||
ArrayList<String> getArgs() {
|
||||
return args;
|
||||
}
|
||||
|
||||
Statement getStatement() {
|
||||
return statement;
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,42 @@
|
||||
/*
|
||||
* 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.HGSEvalException;
|
||||
|
||||
/**
|
||||
* A call to a first class function
|
||||
*/
|
||||
public final class FirstClassFunctionCall extends FuncAdapter {
|
||||
private final FirstClassFunction func;
|
||||
private final Context capturedContext;
|
||||
|
||||
/**
|
||||
* Creates a new instance
|
||||
*
|
||||
* @param func the function
|
||||
* @param context the captured context
|
||||
*/
|
||||
public FirstClassFunctionCall(FirstClassFunction func, Context context) {
|
||||
super(func.getArgs().size());
|
||||
this.func = func;
|
||||
this.capturedContext = context;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object f(Object... args) throws HGSEvalException {
|
||||
Context c = new Context(capturedContext).isFunctionContext();
|
||||
for (int i = 0; i < args.length; i++)
|
||||
c.setVar(func.getArgs().get(i), args[i]);
|
||||
try {
|
||||
func.getStatement().execute(c);
|
||||
return null;
|
||||
} catch (Context.ReturnException e) {
|
||||
return e.getReturnValue();
|
||||
}
|
||||
}
|
||||
}
|
@ -25,10 +25,7 @@ public abstract class FuncAdapter extends Function {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object calcValue(Context c, ArrayList<Expression> args) throws HGSEvalException {
|
||||
if (getArgCount() != args.size())
|
||||
throw new HGSEvalException("wrong number of arguments! found: " + args.size() + ", expected: " + getArgCount());
|
||||
|
||||
public Object callWithExpressions(Context c, ArrayList<Expression> args) throws HGSEvalException {
|
||||
Object[] data = new Object[args.size()];
|
||||
for (int i = 0; i < args.size(); i++)
|
||||
data[i] = args.get(i).value(c);
|
||||
@ -36,11 +33,25 @@ public abstract class FuncAdapter extends Function {
|
||||
}
|
||||
|
||||
/**
|
||||
* The function
|
||||
* Evaluates this function.
|
||||
*
|
||||
* @param args the evaluated arguments
|
||||
* @param args the arguments
|
||||
* @return the result
|
||||
* @throws HGSEvalException HGSEvalException
|
||||
*/
|
||||
protected abstract Object f(Object... args) throws HGSEvalException;
|
||||
|
||||
/**
|
||||
* Use this method to call the function from your java code.
|
||||
*
|
||||
* @param args the arguments of this function
|
||||
* @return the function result
|
||||
* @throws HGSEvalException HGSEvalException
|
||||
*/
|
||||
public Object call(Object... args) throws HGSEvalException {
|
||||
if (getArgCount() >= 0 && getArgCount() != args.length)
|
||||
throw new HGSEvalException("wrong number of arguments! found: " + args.length + ", expected: " + getArgCount());
|
||||
return f(args);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -42,5 +42,5 @@ public abstract class Function {
|
||||
* @return the value
|
||||
* @throws HGSEvalException HGSEvalException
|
||||
*/
|
||||
public abstract Object calcValue(Context c, ArrayList<Expression> args) throws HGSEvalException;
|
||||
public abstract Object callWithExpressions(Context c, ArrayList<Expression> args) throws HGSEvalException;
|
||||
}
|
||||
|
@ -38,8 +38,12 @@ public class ReferenceToFunc implements Reference {
|
||||
@Override
|
||||
public Object get(Context context) throws HGSEvalException {
|
||||
Object funcObj = parent.get(context);
|
||||
if (funcObj instanceof Function)
|
||||
return ((Function) funcObj).calcValue(context, args);
|
||||
if (funcObj instanceof Function) {
|
||||
final Function func = (Function) funcObj;
|
||||
if (func.getArgCount() >= 0 && func.getArgCount() != args.size())
|
||||
throw new HGSEvalException("wrong number of arguments! found: " + args.size() + ", expected: " + func.getArgCount());
|
||||
return func.callWithExpressions(context, args);
|
||||
}
|
||||
throw new HGSEvalException("Value is not a function!");
|
||||
}
|
||||
}
|
||||
|
@ -36,14 +36,14 @@ public class VHDLTemplate implements VHDLEntity {
|
||||
.addFunc("value", new FunctionValue())
|
||||
.addFunc("beginGenericPort", new Function(0) {
|
||||
@Override
|
||||
public Object calcValue(Context c, ArrayList<Expression> args) throws HGSEvalException {
|
||||
public Object callWithExpressions(Context c, ArrayList<Expression> args) throws HGSEvalException {
|
||||
c.setVar("portStartPos", c.length());
|
||||
return null;
|
||||
}
|
||||
})
|
||||
.addFunc("endGenericPort", new Function(0) {
|
||||
@Override
|
||||
public Object calcValue(Context c, ArrayList<Expression> args) throws HGSEvalException {
|
||||
public Object callWithExpressions(Context c, ArrayList<Expression> args) throws HGSEvalException {
|
||||
int start = Value.toInt(c.getVar("portStartPos"));
|
||||
String portDecl = c.toString().substring(start);
|
||||
c.setVar("portDecl", portDecl);
|
||||
@ -52,7 +52,7 @@ public class VHDLTemplate implements VHDLEntity {
|
||||
})
|
||||
.addFunc("registerGeneric", new Function(-1) {
|
||||
@Override
|
||||
public Object calcValue(Context c, ArrayList<Expression> args) throws HGSEvalException {
|
||||
public Object callWithExpressions(Context c, ArrayList<Expression> args) throws HGSEvalException {
|
||||
List<Generic> generics;
|
||||
if (c.contains("generics"))
|
||||
generics = (List<Generic>) c.getVar("generics");
|
||||
|
@ -300,25 +300,25 @@ public class ParserTest extends TestCase {
|
||||
}
|
||||
|
||||
public void testFirstClassFunctionStatic() throws IOException, ParserException, HGSEvalException {
|
||||
Parser p = new Parser("<? @f=func(a){return=a*a+2;}; print(f(4));?>");
|
||||
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.f(3));
|
||||
assertTrue(fObj instanceof FuncAdapter);
|
||||
FuncAdapter f = (FuncAdapter) fObj;
|
||||
assertEquals(11L, f.call(3));
|
||||
}
|
||||
|
||||
public void testFirstClassFunction() throws IOException, ParserException, HGSEvalException {
|
||||
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));?>",
|
||||
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());
|
||||
|
||||
assertEquals("18", exec("<? m=newMap(); m.f=func(a){return=newMap(); return.v=a*a+2;}; print(m.f(4).v);?>").toString());
|
||||
assertEquals("18", exec("<? m=newList(); m[0]=func(a){ l=newList(); l[0]=a*a+2; return=l;}; print(m[0](4)[0]);?>").toString());
|
||||
assertEquals("18", exec("<? m=newMap(); m.f=func(a){m=newMap(); m.v=a*a+2; return m;}; print(m.f(4).v);?>").toString());
|
||||
assertEquals("18", exec("<? m=newList(); m[0]=func(a){ l=newList(); l[0]=a*a+2; return l;}; print(m[0](4)[0]);?>").toString());
|
||||
|
||||
try {
|
||||
assertEquals("18", exec("<? f=func(a){return=a;}; f(1)=5; ?>").toString());
|
||||
assertEquals("18", exec("<? f=func(a){return a;}; f(1)=5; ?>").toString());
|
||||
fail();
|
||||
} catch (HGSEvalException e) {
|
||||
}
|
||||
@ -326,9 +326,25 @@ public class ParserTest extends TestCase {
|
||||
|
||||
public void testFirstClassFunctionOutput() throws IOException, ParserException, HGSEvalException {
|
||||
assertEquals("testtext12testtext15",
|
||||
exec("<? f=func(a){ ?>testtext<? print(a*3); return=output; }; print(f(4),f(5));?>").toString());
|
||||
exec("<? f=func(a){ ?>testtext<? print(a*3); return output; }; print(f(4),f(5));?>").toString());
|
||||
}
|
||||
|
||||
public void testFirstClassFunctionLambda() throws IOException, ParserException, HGSEvalException {
|
||||
Context c = exec("<? outer=5; f=func(x) {return x+outer;}; ?>");
|
||||
FuncAdapter f = c.getFunction("f");
|
||||
assertEquals(6L,f.call(1));
|
||||
assertEquals(7L,f.call(2));
|
||||
|
||||
c = exec("<? f=func(x){ return func(u){return u*x;};}; a=f(2); b=f(5); ?>");
|
||||
FuncAdapter a = c.getFunction("a");
|
||||
FuncAdapter b = c.getFunction("b");
|
||||
assertEquals(4L,a.call(2));
|
||||
assertEquals(6L,a.call(3));
|
||||
assertEquals(10L,b.call(2));
|
||||
assertEquals(15L,b.call(3));
|
||||
}
|
||||
|
||||
|
||||
public void testPanic() throws IOException, ParserException, HGSEvalException {
|
||||
Statement s = new Parser("<? if (i>1) panic(\"myError\"); ?>").parse();
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user