
net.sandius.rembulan.lib.impl.DefaultBasicLib Maven / Gradle / Ivy
/*
* Copyright 2016 Miroslav Janíček
*
* 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 net.sandius.rembulan.lib.impl;
import net.sandius.rembulan.*;
import net.sandius.rembulan.impl.UnimplementedFunction;
import net.sandius.rembulan.lib.AssertionFailedException;
import net.sandius.rembulan.lib.BadArgumentException;
import net.sandius.rembulan.lib.BasicLib;
import net.sandius.rembulan.load.ChunkLoader;
import net.sandius.rembulan.load.LoaderException;
import net.sandius.rembulan.runtime.Dispatch;
import net.sandius.rembulan.runtime.ExecutionContext;
import net.sandius.rembulan.runtime.IllegalOperationAttemptException;
import net.sandius.rembulan.runtime.LuaFunction;
import net.sandius.rembulan.runtime.ProtectedResumable;
import net.sandius.rembulan.runtime.ResolvedControlThrowable;
import net.sandius.rembulan.runtime.ReturnBuffer;
import net.sandius.rembulan.runtime.UnresolvedControlThrowable;
import net.sandius.rembulan.util.Check;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.Arrays;
public class DefaultBasicLib extends BasicLib {
private final LuaFunction _print;
private final LuaFunction _dofile;
private final LuaFunction _load;
private final LuaFunction _loadfile;
public DefaultBasicLib(PrintStream out, ChunkLoader loader, Object defaultEnv) {
this._print = out != null ? new Print(out) : null;
this._dofile = new UnimplementedFunction("dofile"); // TODO
if (loader != null) {
this._load = new Load(loader, defaultEnv);
}
else {
// no loader supplied
this._load = null;
}
this._loadfile = new UnimplementedFunction("loadfile"); // TODO
}
@Override
public String __VERSION() {
return "Lua 5.3";
}
@Override
public LuaFunction _print() {
return _print;
}
@Override
public LuaFunction _type() {
return Type.INSTANCE;
}
@Override
public LuaFunction _next() {
return Next.INSTANCE;
}
@Override
public LuaFunction _pairs() {
return Pairs.INSTANCE;
}
@Override
public LuaFunction _ipairs() {
return IPairs.INSTANCE;
}
@Override
public LuaFunction _tostring() {
return ToString.INSTANCE;
}
@Override
public LuaFunction _tonumber() {
return ToNumber.INSTANCE;
}
@Override
public LuaFunction _error() {
return Error.INSTANCE;
}
@Override
public LuaFunction _assert() {
return Assert.INSTANCE;
}
@Override
public LuaFunction _getmetatable() {
return GetMetatable.INSTANCE;
}
@Override
public LuaFunction _setmetatable() {
return SetMetatable.INSTANCE;
}
@Override
public LuaFunction _pcall() {
return PCall.INSTANCE;
}
@Override
public LuaFunction _xpcall() {
return XPCall.INSTANCE;
}
@Override
public LuaFunction _rawequal() {
return RawEqual.INSTANCE;
}
@Override
public LuaFunction _rawget() {
return RawGet.INSTANCE;
}
@Override
public LuaFunction _rawset() {
return RawSet.INSTANCE;
}
@Override
public LuaFunction _rawlen() {
return RawLen.INSTANCE;
}
@Override
public LuaFunction _select() {
return Select.INSTANCE;
}
@Override
public LuaFunction _collectgarbage() {
return CollectGarbage.INSTANCE;
}
@Override
public LuaFunction _dofile() {
return _dofile;
}
@Override
public LuaFunction _load() {
return _load;
}
@Override
public LuaFunction _loadfile() {
return _loadfile;
}
public static class Print extends AbstractLibFunction {
private final PrintStream out;
public Print(PrintStream out) {
this.out = Check.notNull(out);
}
@Override
protected String name() {
return "print";
}
private void run(ExecutionContext context, Object[] args) throws ResolvedControlThrowable {
for (int i = 0; i < args.length; i++) {
Object a = args[i];
try {
Dispatch.call(context, ToString.INSTANCE, a);
}
catch (UnresolvedControlThrowable ct) {
throw ct.resolve(this, Arrays.copyOfRange(args, i + 1, args.length));
}
Object s = context.getReturnBuffer().get0();
if (LuaType.isString(s)) {
out.print(s);
}
else {
throw new LuaRuntimeException("error calling 'print' ('tostring' must return a string to 'print')");
}
if (i + 1 < args.length) {
out.print('\t');
}
}
out.println();
// returning nothing
context.getReturnBuffer().setTo();
}
@Override
protected void invoke(ExecutionContext context, ArgumentIterator args) throws ResolvedControlThrowable {
run(context, args.getAll());
}
@Override
public void resume(ExecutionContext context, Object suspendedState) throws ResolvedControlThrowable {
run(context, (Object[]) suspendedState);
}
}
public static class Type extends AbstractLibFunction {
public static final Type INSTANCE = new Type();
@Override
protected String name() {
return "type";
}
@Override
protected void invoke(ExecutionContext context, ArgumentIterator args) throws ResolvedControlThrowable {
ByteString typeName = PlainValueTypeNamer.INSTANCE.typeNameOf(args.nextAny());
context.getReturnBuffer().setTo(typeName);
}
}
public static class Next extends AbstractLibFunction {
public static final Next INSTANCE = new Next();
@Override
protected String name() {
return "next";
}
@Override
protected void invoke(ExecutionContext context, ArgumentIterator args) throws ResolvedControlThrowable {
Table table = args.nextTable();
Object index = args.optNextAny();
final Object nxt;
if (index != null) {
nxt = table.successorKeyOf(index);
}
else {
nxt = table.initialKey();
}
if (nxt == null) {
// we've reached the end
context.getReturnBuffer().setTo(null);
}
else {
Object value = table.rawget(nxt);
context.getReturnBuffer().setTo(nxt, value);
}
}
}
public static class INext extends AbstractLibFunction {
public static final INext INSTANCE = new INext();
@Override
protected String name() {
return "inext";
}
@Override
protected void invoke(ExecutionContext context, ArgumentIterator args)
throws ResolvedControlThrowable {
Table table = args.nextTable();
long index = args.nextInteger();
index += 1;
try {
Dispatch.index(context, table, index);
}
catch (UnresolvedControlThrowable ct) {
throw ct.resolve(this, index);
}
Object result = context.getReturnBuffer().get0();
processResult(context, index, result);
}
@Override
public void resume(ExecutionContext context, Object suspendedState) throws ResolvedControlThrowable {
long index = (Long) suspendedState;
Object result = context.getReturnBuffer().get0();
processResult(context, index, result);
}
private static void processResult(ExecutionContext context, long index, Object o) throws ResolvedControlThrowable {
if (o != null) {
context.getReturnBuffer().setTo(index, o);
}
else {
context.getReturnBuffer().setTo(null);
}
}
}
public static class Pairs extends AbstractLibFunction {
public static final Pairs INSTANCE = new Pairs();
@Override
protected String name() {
return "pairs";
}
@Override
protected void invoke(ExecutionContext context, ArgumentIterator args) throws ResolvedControlThrowable {
Table t = args.nextTable();
Object metamethod = Metatables.getMetamethod(context, MT_PAIRS, t);
if (metamethod != null) {
try {
Dispatch.call(context, metamethod, t);
}
catch (UnresolvedControlThrowable ct) {
throw ct.resolve(this, null);
}
ReturnBuffer rbuf = context.getReturnBuffer();
rbuf.setTo(rbuf.get0(), rbuf.get1(), rbuf.get2());
}
else {
ReturnBuffer rbuf = context.getReturnBuffer();
rbuf.setTo(Next.INSTANCE, t, null);
}
}
@Override
public void resume(ExecutionContext context, Object suspendedState) throws ResolvedControlThrowable {
ReturnBuffer rbuf = context.getReturnBuffer();
rbuf.setTo(rbuf.get0(), rbuf.get1(), rbuf.get2());
}
}
public static class IPairs extends AbstractLibFunction {
public static final IPairs INSTANCE = new IPairs();
@Override
protected String name() {
return "ipairs";
}
@Override
protected void invoke(ExecutionContext context, ArgumentIterator args) throws ResolvedControlThrowable {
Table t = args.nextTable();
context.getReturnBuffer().setTo(INext.INSTANCE, t, 0L);
}
}
public static class ToString extends AbstractLibFunction {
public static final ToString INSTANCE = new ToString();
@Override
protected String name() {
return "tostring";
}
@Override
protected void invoke(ExecutionContext context, ArgumentIterator args) throws ResolvedControlThrowable {
Object arg = args.nextAny();
Object meta = Metatables.getMetamethod(context, MT_TOSTRING, arg);
if (meta != null) {
try {
Dispatch.call(context, meta, arg);
}
catch (UnresolvedControlThrowable ct) {
throw ct.resolve(this, null);
}
// resume
resume(context, null);
}
else {
// no metamethod, just call the default toString
ByteString s = Conversions.toHumanReadableString(arg);
context.getReturnBuffer().setTo(s);
}
}
@Override
public void resume(ExecutionContext context, Object suspendedState) throws ResolvedControlThrowable {
// trim to single value
Object result = context.getReturnBuffer().get0();
context.getReturnBuffer().setTo(result);
}
}
public static class ToNumber extends AbstractLibFunction {
public static final ToNumber INSTANCE = new ToNumber();
public static Long toNumber(ByteString s, int base) {
try {
return Long.parseLong(s.toString().trim(), base);
}
catch (NumberFormatException ex) {
return null;
}
}
@Override
protected String name() {
return "tonumber";
}
@Override
protected void invoke(ExecutionContext context, ArgumentIterator args) throws ResolvedControlThrowable {
if (args.size() < 2) {
// no base
Object o = args.nextAny();
Number n = Conversions.numericalValueOf(o);
context.getReturnBuffer().setTo(n);
}
else {
// We do the argument checking gymnastics in order to achieve the same error
// reporting as in PUC-Lua. We first check that base (#2) is an integer, then
// retrieve the string (#1), and then check that the base is within range.
args.skip();
args.nextInteger();
args.rewind();
ByteString s = args.nextStrictString();
int base = args.nextIntRange("base", Character.MIN_RADIX, Character.MAX_RADIX);
context.getReturnBuffer().setTo(toNumber(s, base));
}
}
}
public static class GetMetatable extends AbstractLibFunction {
public static final GetMetatable INSTANCE = new GetMetatable();
@Override
protected String name() {
return "getmetatable";
}
@Override
protected void invoke(ExecutionContext context, ArgumentIterator args) throws ResolvedControlThrowable {
Object arg = args.nextAny();
Object meta = Metatables.getMetamethod(context, MT_METATABLE, arg);
Object result = meta != null
? meta // __metatable field present, return its value
: context.getMetatable(arg); // return the entire metatable
context.getReturnBuffer().setTo(result);
}
}
public static class SetMetatable extends AbstractLibFunction {
public static final SetMetatable INSTANCE = new SetMetatable();
@Override
protected String name() {
return "setmetatable";
}
@Override
protected void invoke(ExecutionContext context, ArgumentIterator args) throws ResolvedControlThrowable {
Table t = args.nextTable();
Table mt = args.nextTableOrNil();
if (Metatables.getMetamethod(context, MT_METATABLE, t) != null) {
throw new IllegalOperationAttemptException("cannot change a protected metatable");
}
else {
t.setMetatable(mt);
context.getReturnBuffer().setTo(t);
}
}
}
public static class Error extends AbstractLibFunction {
public static final Error INSTANCE = new Error();
@Override
protected String name() {
return "error";
}
@Override
protected void invoke(ExecutionContext context, ArgumentIterator args) throws ResolvedControlThrowable {
// TODO: handle levels
Object arg1 = args.optNextAny();
throw new LuaRuntimeException(arg1);
}
}
public static class Assert extends AbstractLibFunction {
public static final Assert INSTANCE = new Assert();
@Override
protected String name() {
return "assert";
}
@Override
protected void invoke(ExecutionContext context, ArgumentIterator args) throws ResolvedControlThrowable {
if (Conversions.booleanValueOf(args.nextAny())) {
context.getReturnBuffer().setToContentsOf(args.getAll());
}
else {
final AssertionFailedException ex;
if (args.hasNext()) {
// message is defined
Object message = args.nextAny();
ByteString stringMessage = Conversions.stringValueOf(message);
if (stringMessage != null) {
ex = new AssertionFailedException(stringMessage);
}
else {
ex = new AssertionFailedException(message);
}
}
else {
// message not defined, use the default
ex = new AssertionFailedException("assertion failed!");
}
throw ex;
}
}
}
public static class PCall extends AbstractLibFunction implements ProtectedResumable {
public static final PCall INSTANCE = new PCall();
@Override
protected String name() {
return "pcall";
}
@Override
protected void invoke(ExecutionContext context, ArgumentIterator args) throws ResolvedControlThrowable {
Object callTarget = args.nextAny();
Object[] callArgs = args.getTail();
try {
Dispatch.call(context, callTarget, callArgs);
}
catch (UnresolvedControlThrowable ct) {
throw ct.resolve(this, null);
}
catch (Exception ex) {
resumeError(context, null, Conversions.toErrorObject(ex));
return;
}
resume(context, null);
}
@Override
public void resume(ExecutionContext context, Object suspendedState) throws ResolvedControlThrowable {
// success: prepend true
ReturnBuffer rbuf = context.getReturnBuffer();
ArrayList
© 2015 - 2025 Weber Informatics LLC | Privacy Policy