1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 package org.millscript.millscript.functions;
23
24 import org.millscript.commons.util.list.ELinkedList;
25 import org.millscript.millscript.action.Action;
26 import org.millscript.millscript.expr.LocalIdent;
27 import org.millscript.millscript.vm.Machine;
28 import org.millscript.millscript.vm.Ref;
29
30 /**
31 * This class implements a function defined within a piece of MillScript code.
32 * As such it may take a variable number of arguments, depending on its
33 * definition.
34 */
35 public final class LambdaFunction extends Function {
36
37 /**
38 * The idents for the local arguments. This is required when we add support
39 * for debugging.
40 */
41 private final LocalIdent[] args;
42
43 /**
44 * The local ident for the optional last argument that soaks up all the
45 * extra parameters.
46 */
47 private final LocalIdent rest;
48
49 /**
50 * The action for the body of the function.
51 */
52 private final Action body;
53
54 /**
55 * The number of arguments this function has.
56 */
57 private final int numArgs;
58
59 /**
60 * The Ref's for each arguments value.
61 */
62 private final Ref[] refs;
63
64 /**
65 * The Ref for the optional last argument that soaks up all the extra
66 * parameters.
67 */
68 private final Ref restRef;
69
70 /**
71 * Constructs a new <code>LambdaFunction</code> to execute the specified
72 * body with the specified number of arguments.
73 *
74 * @param a the local idents for the functions arguments
75 * @param r the optional ident for the argument that can soak up any
76 * additional parameters to the function
77 * @param b the body of the function
78 */
79 public LambdaFunction( final LocalIdent[] a, final LocalIdent r, final Action b ) {
80 this.args = a;
81 this.rest = r;
82 this.body = b;
83 this.numArgs = args.length;
84 this.refs = new Ref[ numArgs ];
85 for ( int i = 0; i < numArgs; i++ ) {
86 refs[ i ] = args[ i ].getRef();
87 }
88 if ( rest != null ) {
89 this.restRef = this.rest.getRef();
90 } else {
91 this.restRef = null;
92 }
93 }
94
95 /**
96 * @see org.millscript.millscript.functions.Function#apply(org.millscript.millscript.vm.Machine, int)
97 */
98 @Override
99 public void apply( final Machine mc, final int nargs ) {
100 if ( nargs != numArgs ) {
101 if ( rest == null || numArgs > nargs ) {
102 throw(
103 FunctionAlert.eval(
104 "Wrong number of arguments",
105 rest == null ?
106 "Exactly " + numArgs + " needed" :
107 numArgs + " or more arguments needed"
108 ).culpritArgs( this, mc, nargs ).mishap()
109 );
110 }
111 }
112 mc.startSaving();
113 if ( restRef != null ) {
114 int nrest = nargs - numArgs;
115 ELinkedList< Object > restList = new ELinkedList< Object >();
116 for ( int i = 0; i < nrest; i++ ) {
117 restList.addFirst( mc.popObject() );
118 }
119 restRef.value = restList;
120 }
121 for ( int i = numArgs - 1; i >= 0; i-- ) {
122 Ref r = refs[ i ];
123 mc.saveRef( r );
124 r.value = mc.popObject();
125 }
126 body.act( mc );
127 mc.restoreSaved();
128 }
129
130 /**
131 * @see java.lang.Object#toString()
132 */
133 @Override
134 public String toString() {
135 String arity = "(" + numArgs + ( restRef == null ? "" : "+" ) + ")";
136 if ( arity.equals( "(0+)" ) ) {
137 arity = "";
138 }
139 String name = getName();
140 return (
141 name == null ?
142 "<function" + arity + ">" :
143 "<function " + name + arity + ">"
144 );
145 }
146
147 }