Embedding Lua, in Scala, using Java(转)

LuaJ

I was reading over the list of features that CurioDB lacks compared to Redis , that I’d previously documented. It contained the item “no Lua scripting”, which I’d written confidently at the time, certain that I wouldn’t possibly be able to implement something so serious in my toy project. But then I thought to myself, “why not?”, and after a bit of research, I discovered the fantastic LuaJ library , which transformed the task from a significant engineering feat, into yet another case of merely gluing some libraries together.

LuaJ provides a complete API for compiling and running Lua scripts, and exposing JVM code to them as Lua functions. LuaJ also makes a complete range of Lua data types available to your JVM code. I purposely refer to the JVM ( Java Virtual Machine ) here rather than Java the language, as in my case, it’s actually Scala being used to code against LuaJ, which makes the overall accomplishment seem even more death-defying.

Obligatory Blog Snippets

Let’s take a look. First up, we’ll compile and run a snippet of Lua (sans imports and class scaffolding, which you can pick up from the final result in CurioDB if you wish):

// The global namespace we‘ll run the script in, which we could preload
// variables into if needed.
val globals = JsePlatform.standardGlobals()

// A tiny string of Lua code - it returns the second string from a
// table called "names" (Lua tables use 1-based indexes).
val script = "names = {‘foo‘, ‘bar‘, ‘baz‘}; return names[2]"

// Running the script returns a LuaValue object, which from our Lua
// code above, we know to be a string, so we cast it to one and print
// the value, which should be "bar".
val result = globals.load(script).call()
println(result.asjstring)

As simple as that. Now let’s look at passing Lua variables from JVM land into a Lua script, and pulling Lua variables back out as JVM objects:

// The same table declared above in Lua, but this time in Scala.
val names = LuaValue.listOf(Array("foo", "bar", "baz").map(LuaValue.valueOf))

// Create the global namespace again, and pass the Lua table into it.
val globals = JsePlatform.standardGlobals()
globals.set("names", names)

// Our Lua code is inline here, and just returns the variable "names".
val result = globals.load("return names").call()

// The result is our original table back in Scala, where we can access
// items by index, just as we previously did in Lua.
println(result.get(2).asjstring)

There you have it - JVM objects representing Lua variables, moving into and back out of Lua code. Now let’s look at the reverse, calling JVM code from inside Lua. To do this, we implement a class representing a Lua function:

// Simple function that takes a string, and prefixes it with "Hello ".
class SayHelloFunction extends OneArgFunction {
  override def call(name: LuaValue) =
    LuaValue.valueOf("Hello " + name.tojstring)
}

// Again, build the global namespace, this time adding the function
// object to it with a given name, and calling it from within Lua.
val globals = JsePlatform.standardGlobals()
globals.set("say_hello", new SayHelloFunction())
val result = globals.load("return say_hello(‘grandma‘)").call()
println(result.asjstring)  // prints "Hello grandma".

That’s it, the full round trip - Lua calling JVM code, and vice versa. With that working, the rest is up to your imagination. I’ve only scratched the surface of what LuaJ provides - all of the data types found in Lua, its standard library, and much more.

The final result was a little more involved than the above implies. CurioDB is carefully designed to be non-blocking, using tell rather than ask , where actors only ever send messages forwards, without expectation of a reply. The challenge here was introducing the synchronous redis.call API into a fundamentally asynchronous system. The solution involved modelling the scripting API as a client actor, with a controlled amount of blocking, much like the way TCP and HTTP connections are managed in the system.

Call-ception

A really fun and whacky side effect of implementing this possibly a little too correctly(for lack of a better term), is that it unintentionally allows the Lua API to recursively call itself:

$ redis-cli EVAL "return redis.call(‘EVAL‘, ‘return redis.call(\‘EVAL\‘, \‘return redis.call(\\\\\‘TIME\\\\\‘)\‘, 0)‘, 0)" 0
1) (integer) 227734
2) (integer) 541653

Is this a feature, or a bug? The scripting API in Redis specifically disallows this, most likely for good reason.

http://www.tuicool.com/articles/M36byiy

http://stackoverflow.com/questions/32573748/multiple-return-values-in-luaj

http://levelup.sinaapp.com/

http://www.luaj.org/luaj/3.0/README.html

http://luaj.org/luaj/3.0/api/org/luaj/vm2/LuaValue.html

http://stackoverflow.com/questions/12358047/how-can-i-pass-objects-to-an-exposed-luaj-function

时间: 2024-12-16 07:59:08

Embedding Lua, in Scala, using Java(转)的相关文章

scala与java之间的那些事

scala与java之间的关系,我认为可以用一句话来开头:scala来源于java,但又高于java. scala的设计者Martin Odersky就是一个JAVA控,这位牛人设计了javac和编写了jdk中的通用代码.可以说java语言本身就是Martin Odersky一步一步看着长大的.所以scala可以说打根起就和JAVA有着远远悠长的血缘关系. Martin Odersky还在写java那会,就立志开发达成一个目标:让写程序这样一个基础工作变得高效.简单.且令人愉悦!因此可以说sca

Spark:用Scala和Java实现WordCount

为了在IDEA中编写scala,今天安装配置学习了IDEA集成开发环境.IDEA确实很优秀,学会之后,用起来很顺手.关于如何搭建scala和IDEA开发环境,请看文末的参考资料. 用Scala和Java实现WordCount,其中Java实现的JavaWordCount是spark自带的例子($SPARK_HOME/examples/src/main/java/org/apache/spark/examples/JavaWordCount.java) 1.环境 OS:Red Hat Enterp

Scala入门到精通——第二十八节 Scala与JAVA互操作

本节主要内容 JAVA中调用Scala类 Scala中调用JAVA类 Scala类型参数与JAVA泛型互操作 Scala与Java间的异常处理互操作 1. JAVA中调用Scala类 Java可以直接操作纵Scala类,如同Scala直接使用Java中的类一样,例如: //在Person.scala文件中定义Scala语法的Person类 package cn.scala.xtwy.scalaToJava class Person(val name:String,val age:Int) //伴

Scala与Java交互

Scala的一个强项在于可以很简单的于已有的Java代码交互,所有java.lang中的类都已经被自动导入了,而其他的类需要显式声明导入.来看看演示代码吧.我们希望对日期进行格式化处理,比如说用法国的格式.Java类库定义了一系列很有用的类,比如Date和DateFormat.由于Scala于Java能够进行很好的交互,我们不需要在Scala类库中实现等效的代码,而只需直接吧Java的相关类导入就可以了:import java.util.{Date, Locale}import java.tex

编写Spark的WordCount程序并提交到集群运行[含scala和java两个版本]

编写Spark的WordCount程序并提交到集群运行[含scala和java两个版本] 1. 开发环境 Jdk 1.7.0_72 Maven 3.2.1 Scala 2.10.6 Spark 1.6.2 Hadoop 2.6.4 IntelliJ IDEA 2016.1.1 2. 创建项目1) 新建Maven项目 2) 在pom文件中导入依赖pom.xml文件内容如下: <?xml version="1.0" encoding="UTF-8"?> &l

用maven来创建scala和java项目代码环境(图文详解)(Intellij IDEA(Ultimate版本)、Intellij IDEA(Community版本)和Scala IDEA for Eclipse皆适用)(博主推荐)

为什么要写这篇博客? 首先,对于spark项目,强烈建议搭建,用Intellij IDEA(Ultimate版本),如果你还有另所爱好尝试Scala IDEA for Eclipse,有时间自己去玩玩.但最好追随大流. 对于hadoop项目,强烈建议用eclipse.   其次,出于有博友给我留言的索求需要,为了更高效率和高质量帮助大家,梳理写下这篇博客. 这篇博客 是在Scala IDEA for Eclipse里手动创建scala代码编写环境. 这篇博客 是在Scala IDEA for E

scala与java的区别

(1)scala与java都有7中数值类型:int.short.long.byte.float.double.boolean这7种,但是scala中这7种值类型是类,在java中属于基本类型,java中,数据类型分成基本类型和引用类型,scala中不区分.(2)scala中的变量或函数的类型总是写在变量或者函数名的后面(3)scala中的操作符与java中的操作符没有什么不同,但是在scala中,操作符是方法,在java中操作符不是方法,且在scala中,除了字母数字之外的其他特殊字符也可以作为

初学Scala和 Java的一些区别

初学scala,和java存在很多不一致,有很多奇葩的语法,在日常使用中,可能自己不会这么写,但是很多存在于源码中,看源码的时候,经常看的一脸懵逼,因此在此总结部门差异点,部分. 文件名 java要求文件名和公共类名必须要求一致,scala不要求. 即 java: test.java -> public class test{} scala: test.scala -> class xxx(任意){} 关键字 scala 中没有public关键字,默认访问权限就是public scala中没有

scala与java的==的比较

如果你想比较一下看看两个对象是否相等,可以使用或者==,或它的反义 !=.(对所有对象都适用,而不仅仅是基本数据类型) ? 1 2 3 4 scala> 1 ==  2 res24: Boolean = false scala> 1 !=  2 res25: Boolean = true 这些操作对所有对象都起作用,而不仅仅是基本类型.例如,你可以用他比较列表: ? 1 2 3 4 scala> List(1, 2, 3) == List(1, 2, 3) res27: Boolean