/*
* Copyright (C) 2011 René Jeschke <rene_jeschke@yahoo.de>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.github.rjeschke.weel;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import com.github.rjeschke.weel.ValueMap.ValueMapIterator;
import com.github.rjeschke.weel.annotations.WeelRawMethod;
/**
* Weel system library.
*
* @author René Jeschke <rene_jeschke@yahoo.de>
*/
public final class WeelLibSys
{
private WeelLibSys()
{
// empty
}
/**
* <code>size(a)</code>
* <p>
* Returns the size of a value.
* </p>
*
* @param runtime
* The Weel runtime.
*/
@WeelRawMethod(args = 1, returnsValue = true)
public final static void size(final WeelRuntime runtime)
{
runtime.load(runtime.popSize());
}
/**
* <code>clock()</code>
* <p>
* Returns the current value of the most precise available system timer, in
* seconds.
* </p>
*
* @param runtime
* The Weel runtime.
* @see java.lang.System#nanoTime()
*/
@WeelRawMethod(returnsValue = true)
public final static void clock(final WeelRuntime runtime)
{
runtime.load(System.nanoTime() * 1e-9);
}
/**
* <code>array(size)</code>
* <p>
* Creates an array of the given size.
* </p>
*
* @param runtime
* The Weel runtime.
*/
@WeelRawMethod(args = 1, returnsValue = true)
public final static void array(final WeelRuntime runtime)
{
final int size = (int) runtime.popNumber();
final ValueMap ret = new ValueMap();
final Value val = new Value();
for (int i = 0; i < size; i++)
ret.append(val);
runtime.load(ret);
}
/**
* <code>array(size, val)</code>
* <p>
* Creates an array of the given size and fills it with val.
* </p>
*
* @param runtime
* The Weel runtime.
*/
@WeelRawMethod(name = "array", args = 2, returnsValue = true)
public final static void array2(final WeelRuntime runtime)
{
final Value val = runtime.pop();
final int size = (int) runtime.popNumber();
final ValueMap ret = new ValueMap();
for (int i = 0; i < size; i++)
ret.append(val);
runtime.load(ret);
}
/**
* <code>isNull(a)</code>
* <p>
* Returns <code>true</code> if 'a' is null.
* </p>
*
* @param runtime
* The Weel runtime.
*/
@WeelRawMethod(args = 1, returnsValue = true)
public final static void isNull(final WeelRuntime runtime)
{
runtime.load(runtime.popType() == ValueType.NULL ? -1 : 0);
}
/**
* <code>isNotNull(a)</code>
* <p>
* Returns <code>true</code> if 'a' is not null.
* </p>
*
* @param runtime
* The Weel runtime.
*/
@WeelRawMethod(args = 1, returnsValue = true)
public final static void isNotNull(final WeelRuntime runtime)
{
runtime.load(runtime.popType() != ValueType.NULL ? -1 : 0);
}
/**
* <code>isNumber(a)</code>
* <p>
* Returns <code>true</code> if 'a' is a number.
* </p>
*
* @param runtime
* The Weel runtime.
*/
@WeelRawMethod(args = 1, returnsValue = true)
public final static void isNumber(final WeelRuntime runtime)
{
runtime.load(runtime.popType() == ValueType.NUMBER ? -1 : 0);
}
/**
* <code>isString(a)</code>
* <p>
* Returns <code>true</code> if 'a' is a string.
* </p>
*
* @param runtime
* The Weel runtime.
*/
@WeelRawMethod(args = 1, returnsValue = true)
public final static void isString(final WeelRuntime runtime)
{
runtime.load(runtime.popType() == ValueType.STRING ? -1 : 0);
}
/**
* <code>isMap(a)</code>
* <p>
* Returns <code>true</code> if 'a' is a map.
* </p>
*
* @param runtime
* The Weel runtime.
*/
@WeelRawMethod(args = 1, returnsValue = true)
public final static void isMap(final WeelRuntime runtime)
{
runtime.load(runtime.popType() == ValueType.MAP ? -1 : 0);
}
/**
* <code>isFunction(a)</code>
* <p>
* Returns <code>true</code> if 'a' is a function.
* </p>
*
* @param runtime
* The Weel runtime.
*/
@WeelRawMethod(args = 1, returnsValue = true)
public final static void isFunc(final WeelRuntime runtime)
{
runtime.load(runtime.popType() == ValueType.FUNCTION ? -1 : 0);
}
/**
* <code>isObject(a)</code>
* <p>
* Returns <code>true</code> if 'a' is an object.
* </p>
*
* @param runtime
* The Weel runtime.
*/
@WeelRawMethod(args = 1, returnsValue = true)
public final static void isObject(final WeelRuntime runtime)
{
runtime.load(runtime.popType() == ValueType.OBJECT ? -1 : 0);
}
/**
* <code>funcName(func)</code>
* <p>
* Returns the name of the supplied function.
* </p>
*
* @param runtime
* The Weel runtime.
*/
@WeelRawMethod(args = 1, returnsValue = true)
public final static void funcName(final WeelRuntime runtime)
{
runtime.load(runtime.popFunction().name);
}
/**
* <code>funcArgs(func)</code>
* <p>
* Returns the number of arguments of the supplied function.
* </p>
*
* @param runtime
* The Weel runtime.
*/
@WeelRawMethod(args = 1, returnsValue = true)
public final static void funcArgs(final WeelRuntime runtime)
{
runtime.load(runtime.popFunction().arguments);
}
/**
* <code>funcReturns(func)</code>
* <p>
* Returns <code>true</code> if the supplied functions returns a value.
* </p>
*
* @param runtime
* The Weel runtime.
*/
@WeelRawMethod(args = 1, returnsValue = true)
public final static void funcReturns(final WeelRuntime runtime)
{
runtime.load(runtime.popFunction().returnsValue ? -1 : 0);
}
/**
* <code>funcFind(name, args)</code>
* <p>
* Returns the function with the specified name and number of arguments,
* <code>null</code> if non could be found.
* </p>
*
* @param runtime
* The Weel runtime.
*/
@WeelRawMethod(args = 2, returnsValue = true)
public final static void funcFind(final WeelRuntime runtime)
{
final int args = (int) runtime.popNumber();
final String name = runtime.popString();
final WeelFunction func = runtime.getMother().findFunction(name, args);
if (func != null)
runtime.load(func);
else
runtime.load();
}
/**
* <code>funcreg(type, map)</code>
* <p>
* Registers type bound functions.
* </p>
*
* @param runtime
* The Weel runtime.
*/
@WeelRawMethod(args = 2)
public final static void funcReg(final WeelRuntime runtime)
{
final ValueMap map = runtime.popMap();
final String typeName = runtime.popString().toUpperCase();
final ValueType type = ValueType.fromString(typeName);
if (type == null)
{
throw new WeelException("Unknown type '" + typeName + "'");
}
final TypeFunctions funcs = runtime.mother.typeFunctions[type.ordinal()];
if (funcs == null)
{
throw new WeelException("Unsupported type '" + type + "'");
}
final Value vkey = new Value();
final Value val = new Value();
final ValueMapIterator it = map.createIterator();
while (it.next(vkey, val))
{
final String key = vkey.toString().toLowerCase();
if (val.isFunction())
{
final String name;
final WeelFunction func = (WeelFunction) val.object;
if (key.endsWith("_"))
{
int i = key.length() - 2;
while (i > 0 && key.charAt(i) == '_')
{
--i;
}
name = key.substring(0, i + 1) + "#" + func.arguments;
}
else
{
name = key + "#" + func.arguments;
}
if (funcs.findFunction(name) != null)
{
throw new WeelException("Duplicate function '" + key + "("
+ func.arguments + ")' for type '" + type + "'");
}
funcs.addFunction(name, func);
}
}
}
/**
* <code>funcCheck(func, args, returnsValue)</code>
* <p>
* Returns true if 'func' is a sub/function (depends on 'returnsValue')
* taking 'args' arguments .
* </p>
*
* @param runtime
* The Weel runtime.
*/
@WeelRawMethod(args = 3, returnsValue = true)
public final static void funcCheck(final WeelRuntime runtime)
{
final boolean ret = runtime.popBoolean();
final int args = (int) runtime.popNumber();
final Value val = runtime.pop();
runtime.load(val.type == ValueType.FUNCTION
&& ((WeelFunction) val.object).returnsValue == ret
&& ((WeelFunction) val.object).arguments == args);
}
/**
* <code>funcreg(type, name, func)</code>
* <p>
* Registers type bound functions.
* </p>
*
* @param runtime
* The Weel runtime.
*/
@WeelRawMethod(name = "funcReg", args = 3)
public final static void funcReg3(final WeelRuntime runtime)
{
final WeelFunction func = runtime.popFunction();
final String name = runtime.popString();
final String iname = name + "#" + func.arguments;
final String typeName = runtime.popString().toUpperCase();
final ValueType type = ValueType.fromString(typeName);
if (type == null)
{
throw new WeelException("Unknown type '" + typeName + "'");
}
final TypeFunctions funcs = runtime.mother.typeFunctions[type.ordinal()];
if (funcs == null)
{
throw new WeelException("Unsupported type '" + type + "'");
}
if (funcs.findFunction(iname) != null)
{
throw new WeelException("Duplicate function '" + name + "("
+ func.arguments + ")' for type '" + type + "'");
}
funcs.addFunction(iname, func);
}
/**
* <code>toNum(s)</code>
* <p>
* Returns the string argument as a number.
* </p>
*
* @param runtime
* The runtime.
*/
@WeelRawMethod(args = 1, returnsValue = true)
public final static void toNum(final WeelRuntime runtime)
{
final Value val = runtime.pop();
if (val.type != ValueType.STRING)
{
runtime.load(0);
}
else
{
try
{
final String str = ((String) val.object).toLowerCase();
if (str.length() > 2)
{
if (str.startsWith("0b"))
runtime.load(Integer.parseInt(str.substring(2), 2));
else if (str.startsWith("0o"))
runtime.load(Integer.parseInt(str.substring(2), 8));
else if (str.startsWith("0x"))
runtime.load(Integer.parseInt(str.substring(2), 16));
else
runtime.load(Double.parseDouble(str));
}
else
runtime.load(Double.parseDouble(str));
}
catch (NumberFormatException e)
{
runtime.load(0);
}
}
}
/**
* <code>toStr(v)</code>
* <p>
* Returns the argument as a string.
* </p>
*
* @param runtime
* The runtime.
*/
@WeelRawMethod(args = 1, returnsValue = true)
public final static void toStr(final WeelRuntime runtime)
{
runtime.load(runtime.pop().toString());
}
/**
* <code>typeOf(v)</code>
* <p>
* Returns the argument's type as a string.
* </p>
*
* @param runtime
* The runtime.
*/
@WeelRawMethod(args = 1, returnsValue = true)
public final static void typeOf(final WeelRuntime runtime)
{
runtime.load(runtime.pop().type.toString());
}
/**
* <code>sleep(ms)</code>
* <p>
* Sleeps for ne given number of milliseconds.
* </p>
*
* @param runtime
* The runtime.
*/
@WeelRawMethod(args = 1, returnsValue = true)
public final static void sleep(final WeelRuntime runtime)
{
try
{
Thread.sleep((long) runtime.popNumber());
runtime.load(true);
}
catch (InterruptedException e)
{
runtime.load(false);
}
}
/**
* Returns the amount of memory used by this virtual machine in bytes.
*
* @param runtime
* The runtime.
*/
@WeelRawMethod(returnsValue = true)
public final static void usedMem(final WeelRuntime runtime)
{
runtime.load(Runtime.getRuntime().totalMemory()
- Runtime.getRuntime().freeMemory());
}
/**
* <code>compile(str)</code>
* <p>
* Compiles a Weel function contained in 'str' and returns it. Runtime
* compiled functions are designed to get garbage collected if there are no
* more references pointing at them.
* </p>
* <p>
* Remember: Compiled script classes use a long counter to avoid naming
* conflicts (the long counter will overflow and wrap after
* 9,223,372,036,854,775,809 compiled scripts).
* </p>
*
* @param runtime
* The Weel runtime.
*/
@WeelRawMethod(args = 1, returnsValue = true)
public final static void compile(final WeelRuntime runtime)
{
final String code = runtime.popString();
Compiler compiler = new Compiler(runtime.getMother());
runtime.load(compiler.compileFunction(code));
}
@WeelRawMethod(args = 1)
public final static void error(final WeelRuntime runtime)
{
throw new WeelException(runtime.pop());
}
/**
* Calls a Weel function with error catching.
*
* @param runtime
* The runtime.
* @param argc
* Number of arguments.
*/
private final static void pcall(final WeelRuntime runtime, final int argc)
{
final ValueMap ret = new ValueMap();
final int sp = runtime.getStackPointer();
final int fp = runtime.fp;
try
{
runtime.stackCall(argc, true);
ret.append(new Value(-1));
ret.append(runtime.pop());
}
catch (Exception e)
{
runtime.npop(runtime.getStackPointer() - sp + argc + 1);
runtime.fp = fp;
ret.append(new Value(0));
if (e instanceof WeelException)
{
final WeelException ex = (WeelException) e;
final Value v = ex.getValue();
ret.append(v != null ? v : new Value(e.toString()));
}
else
{
ret.append(new Value(e.toString()));
}
}
runtime.load(ret);
}
/**
* Protected call.
*
* @param runtime
* The runtime.
*/
@WeelRawMethod(name = "pcall", args = 1, returnsValue = true)
public final static void pcall1(final WeelRuntime runtime)
{
pcall(runtime, 0);
}
/**
* Protected call.
*
* @param runtime
* The runtime.
*/
@WeelRawMethod(name = "pcall", args = 2, returnsValue = true)
public final static void pcall2(final WeelRuntime runtime)
{
pcall(runtime, 1);
}
/**
* Protected call.
*
* @param runtime
* The runtime.
*/
@WeelRawMethod(name = "pcall", args = 3, returnsValue = true)
public final static void pcall3(final WeelRuntime runtime)
{
pcall(runtime, 2);
}
/**
* Protected call.
*
* @param runtime
* The runtime.
*/
@WeelRawMethod(name = "pcall", args = 4, returnsValue = true)
public final static void pcall4(final WeelRuntime runtime)
{
pcall(runtime, 3);
}
/**
* Protected call.
*
* @param runtime
* The runtime.
*/
@WeelRawMethod(name = "pcall", args = 5, returnsValue = true)
public final static void pcall5(final WeelRuntime runtime)
{
pcall(runtime, 4);
}
/**
* Protected call.
*
* @param runtime
* The runtime.
*/
@WeelRawMethod(name = "pcall", args = 6, returnsValue = true)
public final static void pcall6(final WeelRuntime runtime)
{
pcall(runtime, 5);
}
/**
* Protected call.
*
* @param runtime
* The runtime.
*/
@WeelRawMethod(name = "pcall", args = 7, returnsValue = true)
public final static void pcall7(final WeelRuntime runtime)
{
pcall(runtime, 6);
}
/**
* Protected call.
*
* @param runtime
* The runtime.
*/
@WeelRawMethod(name = "pcall", args = 8, returnsValue = true)
public final static void pcall8(final WeelRuntime runtime)
{
pcall(runtime, 7);
}
/**
* Protected call.
*
* @param runtime
* The runtime.
*/
@WeelRawMethod(name = "pcall", args = 9, returnsValue = true)
public final static void pcall9(final WeelRuntime runtime)
{
pcall(runtime, 8);
}
/**
* Executes an external command.
*
* @param cmd
* The command.
* @param args
* The arguments.
* @return The return values.
*/
private final static ValueMap pexec(final ValueMap args)
{
final ValueMap ret = new ValueMap();
if (args != null && args.size > 0)
{
final String[] cmda = new String[args.size];
for (int i = 0; i < args.size; i++)
{
cmda[i] = args.data.get(i).toString();
}
try
{
final ProcessBuilder pb = new ProcessBuilder(cmda);
pb.redirectErrorStream();
final Process p = pb.start();
final BufferedReader reader = new BufferedReader(
new InputStreamReader(p.getInputStream(), "UTF-8"));
final StringBuilder sb = new StringBuilder();
int r = 0;
for (;;)
{
try
{
r = p.exitValue();
break;
}
catch (IllegalThreadStateException e)
{
//
}
int c = reader.read();
while (c != -1)
{
sb.append((char) c);
c = reader.read();
}
try
{
Thread.sleep(10);
}
catch (InterruptedException e)
{
// ignore
}
}
int c = reader.read();
while (c != -1)
{
sb.append((char) c);
c = reader.read();
}
reader.close();
ret.append(new Value(r));
ret.append(new Value(sb.toString()));
}
catch (IOException e)
{
ret.append(new Value(-1));
ret.append(new Value(e.toString()));
}
}
else
{
ret.append(new Value(-1));
ret.append(new Value());
}
return ret;
}
/**
* <code>ret = pexec(cmd)</code>
*
* @param runtime
* The runtime.
*/
@WeelRawMethod(name = "pexec", args = 1, returnsValue = true)
public final static void pexec1(final WeelRuntime runtime)
{
final Value val = runtime.pop();
if(!val.isMap())
{
final ValueMap args = new ValueMap();
args.append(new Value(val.toString()));
runtime.load(pexec(args));
}
else
{
runtime.load(pexec(val.getMap()));
}
}
}