diff --git a/.classpath b/.classpath index a50c56c6..4cfd46e4 100644 --- a/.classpath +++ b/.classpath @@ -1,14 +1,18 @@ - - - - - - + + + + + + + + - + + + diff --git a/README.html b/README.html index a57b27bb..4ece1a49 100644 --- a/README.html +++ b/README.html @@ -17,7 +17,7 @@ Getting Started with LuaJ -James Roseborough, Ian Farmer, Version 1.0.1 +James Roseborough, Ian Farmer, Version 1.9.50

Copyright © 2007-2009 Luaj.org. @@ -41,32 +41,36 @@ Freely available under the terms of the

+ +This is a development release for a planned luaj 2.0. The most recent stable release is 1.0.1 + +

1 - Simple Examples

-

Run a script in J2SE

+

Run a script in Java SE

From the main distribution directory line type:

-	java -cp lib/luaj-j2se-1.0.jar lua src/test/res/test4.lua
+	java -cp lib/luaj-jse-1.9.50.jar lua examples/lua/hello.lua
 

You should see the following output:

-	40
+	hello, world
 
-

Compile a script in J2SE

+

Compile a script in Java SE

From the main distribution directory line type:

-	java -cp lib/luaj-j2se-1.0.jar luac src/test/res/test4.lua
-	java -cp lib/luaj-j2se-1.0.jar lua luac.out
+	java -cp lib/luaj-jse-1.0.jar luac examples/lua/hello.lua
+	java -cp lib/luaj-jse-1.0.jar lua luac.out
 

@@ -75,29 +79,27 @@ The compiled output should run and produce the same result.

Run a script in a Java Application

-The following pattern is used within J2SE +The following pattern is used within Java SE

-	import org.luaj.platform.*;
-	import org.luaj.vm.*;
+	import org.luaj.vm2.*;
+	import org.luaj.vm2.lib.*;
+	import org.luaj.vm2.compiler.LuaC;
 
-	String script = "main.lua";
-	Platform.setInstance( new J2sePlatform() );
-	LuaState vm = Platform.newLuaState();
-	org.luaj.compiler.LuaC.install();
-	vm.getglobal( "dofile" );
-	vm.pushstring( script );
-	vm.call( 1, 0 );
+	String script = "examples/lua/hello.lua";
+	LuaC.install();
+	LuaValue _G = JsePlatform.standardGlobals();
+	_G.get("dofile").call( LuaValue.valueOf(script) );
 

A simple example may be found in

-	src/sample/SampleJ2seMain.java
+	examples/jse/SampleJseMain.java
 

-You must include the library lib/luaj-j2se-1.0.jar in your class path. +You must include the library lib/luaj-jse-1.9.50.jar in your class path.

Run a script in a MIDlet

@@ -105,16 +107,14 @@ You must include the library lib/luaj-j2se-1.0.jar in your class path. The following pattern is used within MIDlets:
-	import org.luaj.platform.*;
-	import org.luaj.vm.*;
+	import org.luaj.vm2.*;
+	import org.luaj.vm2.lib.*;
+	import org.luaj.vm2.compiler.LuaC;
 
-	String script = "main.lua";
-	Platform.setInstance( new J2meMidp20Cldc11Platform( midlet ) );
-	LuaState vm = Platform.newLuaState();
-	org.luaj.compiler.LuaC.install();
-	vm.getglobal( "dofile" );
-	vm.pushstring( script );
-	vm.call( 1, 0 );
+	String script = "examples/lua/hello.lua";
+	LuaC.install();
+	LuaValue _G = JmePlatform.standardGlobals();
+	_G.get("dofile").call( LuaValue.valueOf(script) );
 

@@ -124,22 +124,23 @@ Any files included via require() must also be part of the midlet resour

A simple example may be found in

-	src/sample/SampleMIDlet.java
+	examples/jme/SampleMIDlet.java
 

-You must include the library lib/luaj-j2me-1.0.jar in your midlet jar. +You must include the library lib/luaj-jme-1.9.50.jar in your midlet jar. They can be obfuscated if desired.

Including the compiler

-By default, the compiler is not included to minimize footprint. +By default, the compiler is not included so as to minimize footprint. +Without a compiler, files can still be executed, but they must be compiled elsewhere beforehand. +The "luac" utility is provided in the jse jar for this purpose, or a standard lua compiler can be used.

-To include it, include the following after the Platform is created, -but before the script is executed: +To include the Java it, include the following sometime before lua source files are loaded:

-	org.luaj.compiler.LuaC.install();
+	org.luaj.vm2.compiler.LuaC.install();
 

@@ -162,32 +163,35 @@ The standard use of JSR-233 scripting engines may be used: All standard aspects of script engines including compiled statements should be supported.

-You must include the library lib/luaj-j2se-1.0.jar in your class path. +You must include the library lib/luaj-jse-1.9.50.jar in your class path.

A working example may be found in

-	src/script/ScriptEngineSample.java
+	examples/jse/ScriptEngineSample.java
 

2 - Concepts

-

Platforms

-A Platform is required to set up basic filesystem behavior as well as -contolling mappings to underlying math functions. +

Globals

+The old notion of platform has been replaced with creation of globals. +Two classes are provided to encapsulate common combinations of libraries. -

J2sePlatform

+

JsePlatform

-This platform is used to set up the basic environment for a J2SE application. +This class can be used as a factory for globals in a typical Java SE application. +All standard libraries are included, as well as the luajava library. The default search path is the current directory, -and the math operations include all those supported by J2SE. +and the math operations include all those supported by Java SE. -

J2mePlatform

+

JmePlatform

-This platform is used to set up the basic environment for a J2ME application. +This class can be used to set up the basic environment for a Java ME application. The default search path is limited to the jar resources, -and the math operations are limited to those supported by J2ME. +and the math operations are limited to those supported by Java ME. +All libraries are included except luajava, and the os, io, and math libraries are +limited to those functions that can be supported on that platform.

3 - Libraries

@@ -198,7 +202,7 @@ Libraries are coded to closely match the behavior specified in See standard lua documentation for details on the library API's

-The following libraries are loaded by default in J2ME and J2SE platforms: +The following libraries are loaded by default in Java ME and Java SE platforms:

 	base
 	coroutine
@@ -220,32 +224,23 @@ The following libraries are optional, but preconfigured for some platforms and t
 

Optional Libraries

I/O Library

-The J2SE platform contains the io library by default. +The Java SE platform contains the io library by default.

-The J2ME platform has an optional, partial implementation of the io in +The Java ME platform has an optional, partial implementation of the io in

-	src/j2me/org/luaj/lib/j2me/Cldc10IoLib.java
+	src/jme/org/luaj/vm2/lib/jme/JmeIoLib.java
 
-To install into your vm instance use (j2me only): -
-	LuaState vm = Platform.newLuaState();
-	org.luaj.lib.j2me.Cldc10IoLib.install(vm._G);
-
- -

-See the sample midlet int src/sample/SampleMIDlet for an example. -

OS Library

-A basic os library implementation for either J2ME or J2SE is provided ins +A basic os library implementation for either Java ME or Java SE is provided ins
 	src/core/org/luaj/lib/OsLib.java
 
-A slightly more complete version for J2SE is in: +A slightly more complete version for Java SE is in:
-	src/j2se/org/luaj/lib/j2se/J2seOsLib.java
+	src/jse/org/luaj/vm2/lib/jse/JseOsLib.java
 
Time is a represented as number of milliseconds since the epoch, @@ -260,24 +255,23 @@ The following library is optional: Install from Java using:
-	LuaState vm = Platform.newLuaState();
-	org.luaj.lib.DebugLib.install(vm);
+	org.luaj.vm2.lib.DebugLib.install(vm);
 
or install from lua using:
-	require 'org.luaj.lib.DebugLib'
+	require 'org.luaj.vm2.lib.DebugLib'
 
The lua command line utility includes the debug library by default.

The Luajava Library

-The J2SE platform includes the luajava library, which simplifies binding to Java classes and methods. +The Java SE platform includes the luajava library, which simplifies binding to Java classes and methods. It is patterned after the original luajava project.

-The following lua script will open a swiing frame on J2SE: +The following lua script will open a swiing frame on Java SE:

 	jframe = luajava.bindClass( "javax.swing.JFrame" )
 	frame = luajava.newInstance( "javax.swing.JFrame", "Texts" );
@@ -289,11 +283,11 @@ The following lua script will open a swiing frame on J2SE:
 

See a longer sample in src/test/res/swingapp.lua for details, or try running it using:

-	java -cp lib/luaj-j2se-1.0.jar lua src/test/res/swingapp.lua
+	java -cp lib/luaj-jse-1.9.50.jar lua src/test/res/swingapp.lua
 

-The J2ME platform does not include this library, and it cannot be made to work because of the lack of a reflection API in J2SE. +The Java ME platform does not include this library, and it cannot be made to work because of the lack of a reflection API in Java SE.

4 - Building and Testing

@@ -308,13 +302,13 @@ Other targets exist for creating distribution file an measuring code coverage of

A large array of test scripts may be found in

-	src/test/res/*.lua
+	test/lua/*.lua
 

A large set of JUnit tests are invoked by the JUnit 3 suite:

-	src/test/java/AllTests.lua
+	test/junit/org/luaj/vm2/AllTests.lua
 

@@ -323,7 +317,9 @@ These tests are used for to produce code coverage statistics using build-coverag

5 - Downloads

Downloads and Project Pages

-Downloads are currently hosted on SourceForge +Downloads for version 1.0.1 are currently hosted on SourceForge. +Downloads of built packages for 2.0 are not yet available. +Sources are available via sourceforge.net
 	SourceForge Luaj Project Page
@@ -340,7 +336,5 @@ and LuaForge:
 
 Main changes by version:
 
- - -
  1.0Initial publicly supported release.
  1.0.1Fix arg check and behavior of xpcall() to leave stack intact. -Fix debug.sethook() to when called from hook function. Fix debug.gethook() return values. Array support in luajava bindings.
+  1.9.50Most interpreter features are working. Initial port of all libraries has been done. + diff --git a/build-coverage.xml b/build-coverage.xml index 95ff6391..d2c5438f 100644 --- a/build-coverage.xml +++ b/build-coverage.xml @@ -23,6 +23,12 @@ + + + + + + @@ -42,12 +48,11 @@ + - - - - - + + + @@ -57,11 +62,16 @@ - - - + + + + + + + + + - @@ -74,11 +84,8 @@ - - - - - + + diff --git a/build.xml b/build.xml index aec3a5d7..4ee9fccd 100644 --- a/build.xml +++ b/build.xml @@ -1,11 +1,20 @@ - - - + + + + + + + + + + + + @@ -13,11 +22,25 @@ + + + + + Generating files using ${antlr.tool.jar} + + + + + - - + + @@ -26,36 +49,32 @@ - + - + - + - - - - - - - + + + - - + + - + - - + + - - - + + + @@ -64,7 +83,7 @@ - + + + + + + @@ -100,11 +125,15 @@ + + + - + + diff --git a/examples/jme/SampleMIDlet.java b/examples/jme/SampleMIDlet.java new file mode 100644 index 00000000..6db80ddc --- /dev/null +++ b/examples/jme/SampleMIDlet.java @@ -0,0 +1,34 @@ + + +import javax.microedition.midlet.MIDlet; +import javax.microedition.midlet.MIDletStateChangeException; + +import org.luaj.vm2.*; +import org.luaj.vm2.lib.*; +import org.luaj.vm2.compiler.LuaC; + + +public class SampleMIDlet extends MIDlet { + + // the script will be loaded as a resource + private static final String DEFAULT_SCRIPT = "hello.lua"; + + protected void startApp() throws MIDletStateChangeException { + // get the script as an app property + String script = this.getAppProperty("script"); + if ( script == null ) + script = DEFAULT_SCRIPT; + + // create an environment to run in + LuaC.install(); + LuaValue _G = JmePlatform.standardGlobals(); + _G.get("dofile").call( LuaValue.valueOf(script) ); + } + + protected void destroyApp(boolean arg0) throws MIDletStateChangeException { + } + + protected void pauseApp() { + } + +} diff --git a/examples/jse/SampleJseMain.java b/examples/jse/SampleJseMain.java new file mode 100644 index 00000000..a014c930 --- /dev/null +++ b/examples/jse/SampleJseMain.java @@ -0,0 +1,20 @@ + + +import org.luaj.vm2.*; +import org.luaj.vm2.lib.*; +import org.luaj.vm2.compiler.LuaC; + +public class SampleJseMain { + + + public static void main(String[] args) throws Exception { + String script = "examples/lua/hello.lua"; + + // create an environment to run in + LuaC.install(); + LuaValue _G = JsePlatform.standardGlobals(); + _G.get("dofile").call( LuaValue.valueOf(script) ); + } + + +} diff --git a/examples/jse/ScriptEngineSample.java b/examples/jse/ScriptEngineSample.java new file mode 100644 index 00000000..0cef43fe --- /dev/null +++ b/examples/jse/ScriptEngineSample.java @@ -0,0 +1,50 @@ + + +import javax.script.Bindings; +import javax.script.Compilable; +import javax.script.CompiledScript; +import javax.script.ScriptEngine; +import javax.script.ScriptEngineFactory; +import javax.script.ScriptEngineManager; +import javax.script.ScriptException; + +public class ScriptEngineSample { + + public static void main(String [] args) { + ScriptEngineManager sem = new ScriptEngineManager(); + ScriptEngine e = sem.getEngineByExtension(".lua"); + ScriptEngineFactory f = e.getFactory(); + System.out.println( "Engine name: " +f.getEngineName() ); + System.out.println( "Engine Version: " +f.getEngineVersion() ); + System.out.println( "LanguageName: " +f.getLanguageName() ); + System.out.println( "Language Version: " +f.getLanguageVersion() ); + String statement = f.getOutputStatement("\"hello, world\""); + System.out.println(statement); + try { + e.eval(statement); + + e.put("x", 25); + e.eval("y = math.sqrt(x)"); + System.out.println( "y="+e.get("y") ); + + e.put("x", 2); + e.eval("y = math.sqrt(x)"); + System.out.println( "y="+e.get("y") ); + + CompiledScript cs = ((Compilable)e).compile("y = math.sqrt(x); return y"); + Bindings b = e.createBindings(); + b.put("x", 3); + System.out.println( "eval: "+cs.eval(b) ); + System.out.println( "y="+b.get("y") ); + + try { + e.eval("\n\nbogus example\n\n"); + } catch ( ScriptException se ) { + System.out.println("script threw ScriptException as expected, message is '"+se.getMessage()+"'"); + } + } catch (ScriptException ex) { + ex.printStackTrace(); + } + } + +} diff --git a/examples/lua/hello.lua b/examples/lua/hello.lua new file mode 100644 index 00000000..5f66bcec --- /dev/null +++ b/examples/lua/hello.lua @@ -0,0 +1 @@ +print( 'hello, world' ) \ No newline at end of file diff --git a/examples/lua/swingapp.lua b/examples/lua/swingapp.lua new file mode 100644 index 00000000..37775b6d --- /dev/null +++ b/examples/lua/swingapp.lua @@ -0,0 +1,29 @@ + +frame = luajava.newInstance( "javax.swing.JFrame", "Texts" ); +pane = luajava.newInstance( "javax.swing.JPanel" ); +borderFactory = luajava.bindClass( "javax.swing.BorderFactory" ) +border = borderFactory:createEmptyBorder( 30, 30, 10, 30 ) +pane:setBorder( border ) +label = luajava.newInstance( "javax.swing.JLabel", "This is a Label" ); + + +layout = luajava.newInstance( "java.awt.GridLayout", 2, 2 ); +pane:setLayout( layout ) +pane:add( label ) +pane:setBounds( 20, 30, 10, 30 ) + +borderLayout = luajava.bindClass( "java.awt.BorderLayout" ) +frame:getContentPane():add(pane, borderLayout.CENTER ) +jframe = luajava.bindClass( "javax.swing.JFrame" ) +frame:setDefaultCloseOperation(jframe.EXIT_ON_CLOSE) +frame:pack() +frame:setVisible(true) + +local listener = luajava.createProxy("java.awt.event.MouseListener", + { + mouseClicked = function(me) + print("clicked!", me) + end + }) + +frame:addMouseListener(listener) diff --git a/names.csv b/names.csv new file mode 100644 index 00000000..066caff1 --- /dev/null +++ b/names.csv @@ -0,0 +1,89 @@ +LuaValue Consructors,,Return type,,,,,,,,,, +,valueOf(boolean),LuaBoolean,,,,,,,,,, +,valueOf(null),LuaNil,,,,,,,,,, +,valueOf(int) ,LuaInteger,,,,,,,,,, +,valueOf(double),LuaNumber,,,,,,,,,, +,valueOf(long),LuaNumber,,,,,,,,,, +,valueOf(String),LuaString,,,,,,,,,, +,tableOf(...),LuaTable,,,,,,,,,, +,listOf(LuaValue[]),LuaTable,,,,,,,,,, +,userdataOf(Object),LuaUserdata,,,,,,,,,, +,"uerdataOf(Object,Value)",LuaUserdata,,,,,,,,,, +,,,,,,,Arugment type,,,,, +,,,LuaBoolean,LuaClosure,LuaFunction,LuaDouble,LuaInteger,LuaNil,LuaString,LuaTable,LuaThread,LuaUserdata +Type Check Functions,,,,,,,,,,,, +,isboolean,boolean,TRUE,f,f,f,f,f,f,f,f,f +,isclosure,boolean,f,TRUE,f,f,f,f,f,f,f,f +,isfunction,boolean,f,TRUE,TRUE,f,f,f,f,f,f,f +,isint,boolean,f,f,f,f,TRUE,f,true | f,f,f,f +,isinttype,boolean,f,f,f,f,TRUE,f,f,f,f,f +,isnumber,boolean,f,f,f,TRUE,TRUE,f,true | f,f,f,f +,islong,boolean,f,f,f,true | f,TRUE,f,true | f,f,f,f +,isnil,boolean,f,f,f,f,f,TRUE,f,f,f,f +,isstring,boolean,f,f,f,true | f,TRUE,f,TRUE,f,f,f +,istable,boolean,f,f,f,f,f,f,f,TRUE,f,f +,isthread,boolean,f,f,f,f,f,f,f,f,TRUE,f +,isuserdata,boolean,f,f,f,f,f,f,f,f,f,TRUE +,isuserdata(Class c),boolean,f,f,f,f,f,f,f,f,f,true | f + + +Java Type Coercion Functions,,,,,,,,,,,, +,toboolean,boolean,this.v,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE,TRUE +,tobyte,byte,0,0,0,0,this.v | 0,0,this.v | 0,0,0,0 +,tochar,char,0,0,0,0,this.v | 0,0,this.v | 0,0,0,0 +,todouble,double,0,0,0,this.v,this.v,0,this.v | 0,0,0,0 +,tofloat,float,0,0,0,this.v | 0,this.v,0,this.v | 0,0,0,0 +,toint,int,0,0,0,0,this.v,0,this.v | 0,0,0,0 +,tolong,long,0,0,0,0,this.v,0,this.v | 0,0,0,0 +,toshort,short,0,0,0,0,this.v | 0,0,this.v | 0,0,0,0 +,toString,String,"""true""|""false""","""closure: x""","""name""",(str) this.v,(str) this.v,"""nil""",this.v,"""table: x""","""thread: x""","""userdata: x""" +,touserdata,Object,null,null,null,null,null,null,null,null,this,this.instance + +,,,LuaBoolean,LuaClosure,LuaFunction,LuaDouble,LuaInteger,LuaNil,LuaString,LuaTable,LuaThread,LuaUserdata +Optional Argument Conversion Functions,,,,,,,,,,,, +,optboolean,boolean,this,e,e,e,e,defval,e,e,e,e +,optclosure,LuaClosure,n,this,e,e,e,defval,e,e,e,e +,optdouble,double,e,e,e,this,this,defval,this | e,e,e,e +,optfunction,LuaFunction,n,this,this,e,e,defval,e,e,e,e +,optint,int,e,e,e,(int) this,this,defval,this | e,e,e,e +,optinteger,LuaInteger,e,e,e,(int) this,this,defval,this | e,e,e,e +,optlong,long,e,e,e,(long) this,this,defval,this | e,e,e,e +,optnumber,LuaNumber,e,e,e,this,this,defval,this | e,e,e,e +,optString,String,e,e,e,(str) this.v,(str) this.v,defval,this,e,e,e +,optstring,LuaString,e,e,e,(str) this.v,(str) this.v,defval,this,e,e,e +,opttable,LuaTable,e,e,e,e,e,defval,e,this,e,e +,optthread,LuaThread,e,e,e,e,e,defval,e,e,this,n +,optuserdata,Object,e,e,e,e,e,defval,e,e,e,instance +,optuserdata(Class c),Object,e,e,e,e,e,defval,e,e,e,instance | e + +Required Argument Conversion Functions,,,,,,,,,,,, +,checkboolean,boolean,this,e,e,e,e,e,e,e,e,e +,checkclosure,LuaClosure,e,this,e,e,e,e,e,e,e,e +,checkdouble,double,e,e,e,this,this,e,e,e,e,e +,checkfunction,LuaFunction,e,this,this,e,e,e,e,e,e,e +,checkint,int,e,e,e,this | e,this,e,e,e,e,e +,checkinteger,LuaInteger,e,e,e,e,this,e,e,e,e,e +,checklong,LuaNumber,e,e,e,this | e,this,e,e,e,e,e +,checknumber,LuaNumber,e,e,e,this,this,e,e,e,e,e +,checkString,String,e,e,e,(str) this.v,(str) this.v,e,(str) this.v,e,e,e +,checkstring,LuaString,e,e,e,(str) this.v,(str) this.v,e,this,e,e,e +,checktable,LuaTable,e,e,e,e,e,e,e,this,e,e +,checkthread,LuaThread,e,e,e,e,e,e,e,e,this,e +,checkuserdata,Object,e,e,e,e,e,e,e,e,e,instance +,checkuserdata(Class c),Object,e,e,e,e,e,e,e,e,e,instance | e +,checkvalue,LuaValue,this,this,this,this,this,e,this,this,this,this + +,,,LuaBoolean,LuaClosure,LuaFunction,LuaDouble,LuaInteger,LuaNil,LuaString,LuaTable,LuaThread,LuaUserdata +Lua Language Operations,,,,,,,,,,,, +,type,int,TBOOLEAN,TFUNCTION,TFUNCTION,TNUMBER,TNUMBER,TNIL,TSTRING,TTABLE,TTHREAD,TUSERDATA +,typename,string,"""boolean""","""function""","""function""","""number""","""number""","""nil""","""string""","""table""","""thread""","""userdata""" +,len,LuaInteger,e,e,e,e,e,e,#v,#v,e,e +,length,int,e,e,e,e,e,e,#v,#v,e,e +,getmetatable,LuaValue,static,static,static,static,static,static,static,instance,static ,instance +,setmetatable,LuaValue,e,e,e,e,e,e,e,instance,e,instance +,getfenv,LuaTable,e,instance,instance,e,e,e,e,e,instance,e +,setfenv,LuaFunction,e,instance,instance,e,e,e,e,e,instance,e +,call,LuaValue,__call,call,call,__call,__call,__call,__call,__call,__call,__call +,invoke,Varargs,__call,call,call,__call,__call,__call,__call,__call,__call,__call +,get,LuaValue,__index,__index,__index,__index,__index,__index,__index,get,__index,__index +,set,LuaValue,__newindex,__newindex,__newindex,__newindex,__newindex,__newindex,__newindex,set,__newindex,__newindex diff --git a/src/core/org/luaj/vm2/Buffer.java b/src/core/org/luaj/vm2/Buffer.java new file mode 100644 index 00000000..12549649 --- /dev/null +++ b/src/core/org/luaj/vm2/Buffer.java @@ -0,0 +1,95 @@ +/******************************************************************************* + * Copyright (c) 2009 Luaj.org. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + ******************************************************************************/ +package org.luaj.vm2; + + +/** + * String buffer for use in string library methods, optimized for production + * of StrValue instances. + */ +public class Buffer { + private static final int DEFAULT_CAPACITY = 64; + + private byte[] bytes; + private int length; + + public Buffer() { + this(DEFAULT_CAPACITY); + } + + public String toString() { + return new LuaString(bytes, 0, length).toString(); + } + + public Buffer( int initialCapacity ) { + bytes = new byte[ initialCapacity ]; + length = 0; + } + + public void append( byte b ) { + ensureCapacity( length + 1 ); + bytes[ length++ ] = b; + } + + public void append( LuaValue val ) { + append( val.strvalue() ); + } + + public void append( LuaString str ) { + final int alen = str.length(); + ensureCapacity( length + alen ); + str.copyInto( 0, bytes, length, alen ); + length += alen; + } + + public void append( String str ) { + char[] chars = str.toCharArray(); + final int alen = LuaString.lengthAsUtf8( chars ); + ensureCapacity( length + alen ); + LuaString.encodeToUtf8( chars, bytes, length ); + length += alen; + } + + public void setLength( int length ) { + ensureCapacity( length ); + this.length = length; + } + + public LuaString tostrvalue() { + return new LuaString( realloc( bytes, length ) ); + } + + public void ensureCapacity( int minSize ) { + if ( minSize > bytes.length ) + realloc( minSize ); + } + + private void realloc( int minSize ) { + bytes = realloc( bytes, Math.max( bytes.length * 2, minSize ) ); + } + + private static byte[] realloc( byte[] b, int newSize ) { + byte[] newBytes = new byte[ newSize ]; + System.arraycopy( b, 0, newBytes, 0, Math.min( b.length, newSize ) ); + return newBytes; + } +} diff --git a/src/core/org/luaj/vm2/LoadState.java b/src/core/org/luaj/vm2/LoadState.java new file mode 100644 index 00000000..33577ba5 --- /dev/null +++ b/src/core/org/luaj/vm2/LoadState.java @@ -0,0 +1,316 @@ +/******************************************************************************* +* Copyright (c) 2009 Luaj.org. All rights reserved. +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +* THE SOFTWARE. +******************************************************************************/ +package org.luaj.vm2; + +import java.io.DataInputStream; +import java.io.IOException; +import java.io.InputStream; + +/* +** Loader to load compiled function prototypes +*/ +public class LoadState { + + /** format corresponding to non-number-patched lua, all numbers are floats or doubles */ + public static final int NUMBER_FORMAT_FLOATS_OR_DOUBLES = 0; + + /** format corresponding to non-number-patched lua, all numbers are ints */ + public static final int NUMBER_FORMAT_INTS_ONLY = 1; + + /** format corresponding to number-patched lua, all numbers are 32-bit (4 byte) ints */ + public static final int NUMBER_FORMAT_NUM_PATCH_INT32 = 4; + + // type constants + + public static final int LUA_TINT = (-2); + public static final int LUA_TNONE = (-1); + public static final int LUA_TNIL = 0; + public static final int LUA_TBOOLEAN = 1; + public static final int LUA_TLIGHTUSERDATA = 2; + public static final int LUA_TNUMBER = 3; + public static final int LUA_TSTRING = 4; + public static final int LUA_TTABLE = 5; + public static final int LUA_TFUNCTION = 6; + public static final int LUA_TUSERDATA = 7; + public static final int LUA_TTHREAD = 8; + public static final int LUA_TVALUE = 9; + + /** Interface for the compiler, if it is installed. */ + public interface LuaCompiler { + public Prototype compile(int firstByte, InputStream stream, String name) throws IOException; + } + + /** Compiler instance, if installed */ + public static LuaCompiler compiler = null; + + /** Signature byte indicating the file is a compiled binary chunk */ + private static final byte[] LUA_SIGNATURE = { '\033', 'L', 'u', 'a' }; + + /** Name for compiled chunks */ + public static final String SOURCE_BINARY_STRING = "binary string"; + + + /** for header of binary files -- this is Lua 5.1 */ + public static final int LUAC_VERSION = 0x51; + + /** for header of binary files -- this is the official format */ + public static final int LUAC_FORMAT = 0; + + /** size of header of binary files */ + public static final int LUAC_HEADERSIZE = 12; + + // values read from the header + private int luacVersion; + private int luacFormat; + private boolean luacLittleEndian; + private int luacSizeofInt; + private int luacSizeofSizeT; + private int luacSizeofInstruction; + private int luacSizeofLuaNumber; + private int luacNumberFormat; + + /** input stream from which we are loading */ + public final DataInputStream is; + + /** Name of what is being loaded? */ + String name; + + private static final LuaValue[] NOVALUES = {}; + private static final Prototype[] NOPROTOS = {}; + private static final LocVars[] NOLOCVARS = {}; + private static final LuaString[] NOSTRVALUES = {}; + private static final int[] NOINTS = {}; + + /** Read buffer */ + private byte[] buf = new byte[512]; + + + int loadInt() throws IOException { + is.readFully(buf,0,4); + return luacLittleEndian? + (buf[3] << 24) | ((0xff & buf[2]) << 16) | ((0xff & buf[1]) << 8) | (0xff & buf[0]): + (buf[0] << 24) | ((0xff & buf[1]) << 16) | ((0xff & buf[2]) << 8) | (0xff & buf[3]); + } + + int[] loadIntArray() throws IOException { + int n = loadInt(); + if ( n == 0 ) + return NOINTS; + + // read all data at once + int m = n << 2; + if ( buf.length < m ) + buf = new byte[m]; + is.readFully(buf,0,m); + int[] array = new int[n]; + for ( int i=0, j=0; i> 52) & 0x7ffL) - 1023; + + if ( e >= 0 && e < 31 ) { + long f = bits & 0xFFFFFFFFFFFFFL; + int shift = 52 - e; + long intPrecMask = ( 1L << shift ) - 1; + if ( ( f & intPrecMask ) == 0 ) { + int intValue = (int)( f >> shift ) | ( 1 << e ); + return LuaInteger.valueOf( ( ( bits >> 63 ) != 0 ) ? -intValue : intValue ); + } + } + + return LuaValue.valueOf( Double.longBitsToDouble(bits) ); + } + + LuaValue loadNumber() throws IOException { + if ( luacNumberFormat == NUMBER_FORMAT_INTS_ONLY ) { + return LuaInteger.valueOf( loadInt() ); + } else { + return longBitsToLuaNumber( loadInt64() ); + } + } + + void loadConstants(Prototype f) throws IOException { + int n = loadInt(); + LuaValue[] values = n>0? new LuaValue[n]: NOVALUES; + for ( int i=0; i0? new Prototype[n]: NOPROTOS; + for ( int i=0; i0? new LocVars[n]: NOLOCVARS; + for ( int i=0; i0? new LuaString[n]: NOSTRVALUES; + for ( int i=0; i>1); /* `sBx' is signed */ + + public static final int MASK_OP = ((1<> POS_OP) & MAX_OP; + } + + public static int GETARG_A(int i) { + return (i >> POS_A) & MAXARG_A; + } + + public static int GETARG_B(int i) { + return (i >> POS_B) & MAXARG_B; + } + + public static int GETARG_C(int i) { + return (i >> POS_C) & MAXARG_C; + } + + public static int GETARG_Bx(int i) { + return (i >> POS_Bx) & MAXARG_Bx; + } + + public static int GETARG_sBx(int i) { + return ((i >> POS_Bx) & MAXARG_Bx) - MAXARG_sBx; + } + + + /* + ** Macros to operate RK indices + */ + + /** this bit 1 means constant (0 means register) */ + public static final int BITRK = (1 << (SIZE_B - 1)); + + /** test whether value is a constant */ + public static boolean ISK(int x) { + return 0 != ((x) & BITRK); + } + + /** gets the index of the constant */ + public static int INDEXK(int r) { + return ((int)(r) & ~BITRK); + } + + public static final int MAXINDEXRK = (BITRK - 1); + + /** code a constant index as a RK value */ + public static int RKASK(int x) { + return ((x) | BITRK); + } + + + /** + ** invalid register that fits in 8 bits + */ + public static final int NO_REG = MAXARG_A; + + + /* + ** R(x) - register + ** Kst(x) - constant (in constant table) + ** RK(x) == if ISK(x) then Kst(INDEXK(x)) else R(x) + */ + + + /* + ** grep "ORDER OP" if you change these enums + */ + + /*---------------------------------------------------------------------- + name args description + ------------------------------------------------------------------------*/ + public static final int OP_MOVE = 0;/* A B R(A) := R(B) */ + public static final int OP_LOADK = 1;/* A Bx R(A) := Kst(Bx) */ + public static final int OP_LOADBOOL = 2;/* A B C R(A) := (Bool)B; if (C) pc++ */ + public static final int OP_LOADNIL = 3; /* A B R(A) := ... := R(B) := nil */ + public static final int OP_GETUPVAL = 4; /* A B R(A) := UpValue[B] */ + + public static final int OP_GETGLOBAL = 5; /* A Bx R(A) := Gbl[Kst(Bx)] */ + public static final int OP_GETTABLE = 6; /* A B C R(A) := R(B)[RK(C)] */ + + public static final int OP_SETGLOBAL = 7; /* A Bx Gbl[Kst(Bx)] := R(A) */ + public static final int OP_SETUPVAL = 8; /* A B UpValue[B] := R(A) */ + public static final int OP_SETTABLE = 9; /* A B C R(A)[RK(B)] := RK(C) */ + + public static final int OP_NEWTABLE = 10; /* A B C R(A) := {} (size = B,C) */ + + public static final int OP_SELF = 11; /* A B C R(A+1) := R(B); R(A) := R(B)[RK(C)] */ + + public static final int OP_ADD = 12; /* A B C R(A) := RK(B) + RK(C) */ + public static final int OP_SUB = 13; /* A B C R(A) := RK(B) - RK(C) */ + public static final int OP_MUL = 14; /* A B C R(A) := RK(B) * RK(C) */ + public static final int OP_DIV = 15; /* A B C R(A) := RK(B) / RK(C) */ + public static final int OP_MOD = 16; /* A B C R(A) := RK(B) % RK(C) */ + public static final int OP_POW = 17; /* A B C R(A) := RK(B) ^ RK(C) */ + public static final int OP_UNM = 18; /* A B R(A) := -R(B) */ + public static final int OP_NOT = 19; /* A B R(A) := not R(B) */ + public static final int OP_LEN = 20; /* A B R(A) := length of R(B) */ + + public static final int OP_CONCAT = 21; /* A B C R(A) := R(B).. ... ..R(C) */ + + public static final int OP_JMP = 22; /* sBx pc+=sBx */ + + public static final int OP_EQ = 23; /* A B C if ((RK(B) == RK(C)) ~= A) then pc++ */ + public static final int OP_LT = 24; /* A B C if ((RK(B) < RK(C)) ~= A) then pc++ */ + public static final int OP_LE = 25; /* A B C if ((RK(B) <= RK(C)) ~= A) then pc++ */ + + public static final int OP_TEST = 26; /* A C if not (R(A) <=> C) then pc++ */ + public static final int OP_TESTSET = 27; /* A B C if (R(B) <=> C) then R(A) := R(B) else pc++ */ + + public static final int OP_CALL = 28; /* A B C R(A), ... ,R(A+C-2) := R(A)(R(A+1), ... ,R(A+B-1)) */ + public static final int OP_TAILCALL = 29; /* A B C return R(A)(R(A+1), ... ,R(A+B-1)) */ + public static final int OP_RETURN = 30; /* A B return R(A), ... ,R(A+B-2) (see note) */ + + public static final int OP_FORLOOP = 31; /* A sBx R(A)+=R(A+2); + if R(A) =) R(A)*/ + public static final int OP_CLOSURE = 36; /* A Bx R(A) := closure(KPROTO[Bx], R(A), ... ,R(A+n)) */ + public static final int OP_VARARG = 37; /* A B R(A), R(A+1), ..., R(A+B-1) = vararg */ + + public static final int NUM_OPCODES = OP_VARARG + 1; + + /*=========================================================================== + Notes: + (*) In OP_CALL, if (B == 0) then B = top. C is the number of returns - 1, + and can be 0: OP_CALL then sets `top' to last_result+1, so + next open instruction (OP_CALL, OP_RETURN, OP_SETLIST) may use `top'. + + (*) In OP_VARARG, if (B == 0) then use actual number of varargs and + set top (like in OP_CALL with C == 0). + + (*) In OP_RETURN, if (B == 0) then return up to `top' + + (*) In OP_SETLIST, if (B == 0) then B = `top'; + if (C == 0) then next `instruction' is real C + + (*) For comparisons, A specifies what condition the test should accept + (true or false). + + (*) All `skips' (pc++) assume that next instruction is a jump + ===========================================================================*/ + + + /* + ** masks for instruction properties. The format is: + ** bits 0-1: op mode + ** bits 2-3: C arg mode + ** bits 4-5: B arg mode + ** bit 6: instruction set register A + ** bit 7: operator is a test + */ + + public static final int OpArgN = 0; /* argument is not used */ + public static final int OpArgU = 1; /* argument is used */ + public static final int OpArgR = 2; /* argument is a register or a jump offset */ + public static final int OpArgK = 3; /* argument is a constant or register/constant */ + + public static final int[] luaP_opmodes = { + /* T A B C mode opcode */ + (0<<7) | (1<<6) | (OpArgR<<4) | (OpArgN<<2) | (iABC), /* OP_MOVE */ + (0<<7) | (1<<6) | (OpArgK<<4) | (OpArgN<<2) | (iABx), /* OP_LOADK */ + (0<<7) | (1<<6) | (OpArgU<<4) | (OpArgU<<2) | (iABC), /* OP_LOADBOOL */ + (0<<7) | (1<<6) | (OpArgR<<4) | (OpArgN<<2) | (iABC), /* OP_LOADNIL */ + (0<<7) | (1<<6) | (OpArgU<<4) | (OpArgN<<2) | (iABC), /* OP_GETUPVAL */ + (0<<7) | (1<<6) | (OpArgK<<4) | (OpArgN<<2) | (iABx), /* OP_GETGLOBAL */ + (0<<7) | (1<<6) | (OpArgR<<4) | (OpArgK<<2) | (iABC), /* OP_GETTABLE */ + (0<<7) | (0<<6) | (OpArgK<<4) | (OpArgN<<2) | (iABx), /* OP_SETGLOBAL */ + (0<<7) | (0<<6) | (OpArgU<<4) | (OpArgN<<2) | (iABC), /* OP_SETUPVAL */ + (0<<7) | (0<<6) | (OpArgK<<4) | (OpArgK<<2) | (iABC), /* OP_SETTABLE */ + (0<<7) | (1<<6) | (OpArgU<<4) | (OpArgU<<2) | (iABC), /* OP_NEWTABLE */ + (0<<7) | (1<<6) | (OpArgR<<4) | (OpArgK<<2) | (iABC), /* OP_SELF */ + (0<<7) | (1<<6) | (OpArgK<<4) | (OpArgK<<2) | (iABC), /* OP_ADD */ + (0<<7) | (1<<6) | (OpArgK<<4) | (OpArgK<<2) | (iABC), /* OP_SUB */ + (0<<7) | (1<<6) | (OpArgK<<4) | (OpArgK<<2) | (iABC), /* OP_MUL */ + (0<<7) | (1<<6) | (OpArgK<<4) | (OpArgK<<2) | (iABC), /* OP_DIV */ + (0<<7) | (1<<6) | (OpArgK<<4) | (OpArgK<<2) | (iABC), /* OP_MOD */ + (0<<7) | (1<<6) | (OpArgK<<4) | (OpArgK<<2) | (iABC), /* OP_POW */ + (0<<7) | (1<<6) | (OpArgR<<4) | (OpArgN<<2) | (iABC), /* OP_UNM */ + (0<<7) | (1<<6) | (OpArgR<<4) | (OpArgN<<2) | (iABC), /* OP_NOT */ + (0<<7) | (1<<6) | (OpArgR<<4) | (OpArgN<<2) | (iABC), /* OP_LEN */ + (0<<7) | (1<<6) | (OpArgR<<4) | (OpArgR<<2) | (iABC), /* OP_CONCAT */ + (0<<7) | (0<<6) | (OpArgR<<4) | (OpArgN<<2) | (iAsBx), /* OP_JMP */ + (1<<7) | (0<<6) | (OpArgK<<4) | (OpArgK<<2) | (iABC), /* OP_EQ */ + (1<<7) | (0<<6) | (OpArgK<<4) | (OpArgK<<2) | (iABC), /* OP_LT */ + (1<<7) | (0<<6) | (OpArgK<<4) | (OpArgK<<2) | (iABC), /* OP_LE */ + (1<<7) | (1<<6) | (OpArgR<<4) | (OpArgU<<2) | (iABC), /* OP_TEST */ + (1<<7) | (1<<6) | (OpArgR<<4) | (OpArgU<<2) | (iABC), /* OP_TESTSET */ + (0<<7) | (1<<6) | (OpArgU<<4) | (OpArgU<<2) | (iABC), /* OP_CALL */ + (0<<7) | (1<<6) | (OpArgU<<4) | (OpArgU<<2) | (iABC), /* OP_TAILCALL */ + (0<<7) | (0<<6) | (OpArgU<<4) | (OpArgN<<2) | (iABC), /* OP_RETURN */ + (0<<7) | (1<<6) | (OpArgR<<4) | (OpArgN<<2) | (iAsBx), /* OP_FORLOOP */ + (0<<7) | (1<<6) | (OpArgR<<4) | (OpArgN<<2) | (iAsBx), /* OP_FORPREP */ + (1<<7) | (0<<6) | (OpArgN<<4) | (OpArgU<<2) | (iABC), /* OP_TFORLOOP */ + (0<<7) | (0<<6) | (OpArgU<<4) | (OpArgU<<2) | (iABC), /* OP_SETLIST */ + (0<<7) | (0<<6) | (OpArgN<<4) | (OpArgN<<2) | (iABC), /* OP_CLOSE */ + (0<<7) | (1<<6) | (OpArgU<<4) | (OpArgN<<2) | (iABx), /* OP_CLOSURE */ + (0<<7) | (1<<6) | (OpArgU<<4) | (OpArgN<<2) | (iABC), /* OP_VARARG */ + }; + + public static int getOpMode(int m) { + return luaP_opmodes[m] & 3; + } + public static int getBMode(int m) { + return (luaP_opmodes[m] >> 4) & 3; + } + public static int getCMode(int m) { + return (luaP_opmodes[m] >> 2) & 3; + } + public static boolean testAMode(int m) { + return 0 != (luaP_opmodes[m] & (1 << 6)); + } + public static boolean testTMode(int m) { + return 0 != (luaP_opmodes[m] & (1 << 7)); + } + + /* number of list items to accumulate before a SETLIST instruction */ + public static final int LFIELDS_PER_FLUSH = 50; + +} diff --git a/src/core/org/luaj/vm2/LuaBoolean.java b/src/core/org/luaj/vm2/LuaBoolean.java new file mode 100644 index 00000000..5c609cfd --- /dev/null +++ b/src/core/org/luaj/vm2/LuaBoolean.java @@ -0,0 +1,73 @@ +/******************************************************************************* + * Copyright (c) 2009 Luaj.org. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + ******************************************************************************/ +package org.luaj.vm2; + +public class LuaBoolean extends LuaValue { + + public static LuaValue s_metatable; + + public final boolean v; + + LuaBoolean(boolean b) { + this.v = b; + } + + public int type() { + return LuaValue.TBOOLEAN; + } + + public String typename() { + return "boolean"; + } + + public boolean isboolean() { + return true; + } + + public LuaValue not() { + return v ? FALSE : LuaValue.TRUE; + } + + public boolean booleanValue() { + return v; + } + + public boolean toboolean() { + return v; + } + + public String toString() { + return v ? "true" : "false"; + } + + public boolean optboolean(boolean defval) { + return this.v; + } + + public boolean checkboolean() { + return v; + } + + public LuaValue getmetatable() { + return s_metatable; + } +} diff --git a/src/core/org/luaj/vm2/LuaClosure.java b/src/core/org/luaj/vm2/LuaClosure.java new file mode 100644 index 00000000..9b93a903 --- /dev/null +++ b/src/core/org/luaj/vm2/LuaClosure.java @@ -0,0 +1,491 @@ +/******************************************************************************* +* Copyright (c) 2009 Luaj.org. All rights reserved. +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +* THE SOFTWARE. +******************************************************************************/ +package org.luaj.vm2; + +import org.luaj.vm2.lib.DebugLib; + +public class LuaClosure extends LuaFunction { + private static final UpValue[] NOUPVALUES = new UpValue[0]; + + public LuaValue s_metatable; + + public final Prototype p; + public final UpValue[] upValues; + private UpValue openUpValues = null; + + LuaClosure() { + p = null; + upValues = null; + } + /** Supply the initial environment */ + public LuaClosure(Prototype p, LuaValue env) { + super( env ); + this.p = p; + this.upValues = p.nups>0? new UpValue[p.nups]: NOUPVALUES; + } + + public boolean isclosure() { + return true; + } + + public LuaClosure optclosure(LuaClosure defval) { + return this; + } + + public LuaClosure checkclosure() { + return this; + } + + public LuaValue getmetatable() { + return s_metatable; + } + + public final LuaValue call() { + LuaValue[] stack = new LuaValue[p.maxstacksize]; + System.arraycopy(NILS, 0, stack, 0, p.maxstacksize); + return execute(stack,NONE).arg1(); + } + + public final LuaValue call(LuaValue arg) { + LuaValue[] stack = new LuaValue[p.maxstacksize]; + System.arraycopy(NILS, 0, stack, 0, p.maxstacksize); + switch ( p.numparams ) { + default: stack[0]=arg; return execute(stack,NONE).arg1(); + case 0: return execute(stack,arg).arg1(); + } + } + + public final LuaValue call(LuaValue arg1, LuaValue arg2) { + LuaValue[] stack = new LuaValue[p.maxstacksize]; + System.arraycopy(NILS, 0, stack, 0, p.maxstacksize); + switch ( p.numparams ) { + default: stack[0]=arg1; stack[1]=arg2; return execute(stack,NONE).arg1(); + case 1: stack[0]=arg1; return execute(stack,arg2).arg1(); + case 0: return execute(stack,p.is_vararg!=0? varargsOf(arg1,arg2): NONE).arg1(); + } + } + + public final LuaValue call(LuaValue arg1, LuaValue arg2, LuaValue arg3) { + LuaValue[] stack = new LuaValue[p.maxstacksize]; + System.arraycopy(NILS, 0, stack, 0, p.maxstacksize); + switch ( p.numparams ) { + default: stack[0]=arg1; stack[1]=arg2; stack[2]=arg3; return execute(stack,NONE).arg1(); + case 2: stack[0]=arg1; stack[1]=arg2; return execute(stack,arg3).arg1(); + case 1: stack[0]=arg1; return execute(stack,p.is_vararg!=0? varargsOf(arg2,arg3): NONE).arg1(); + case 0: return execute(stack,p.is_vararg!=0? varargsOf(arg1,arg2,arg3): NONE).arg1(); + } + } + + public final Varargs invoke(Varargs varargs) { + LuaValue[] stack = new LuaValue[p.maxstacksize]; + System.arraycopy(NILS, 0, stack, 0, p.maxstacksize); + for ( int i=0; i= Lua.VARARG_NEEDSARG ) + stack[p.numparams] = new LuaTable(varargs); + + // debug wants args to this function + if (DebugLib.DEBUG_ENABLED) + DebugLib.debugSetupCall(varargs, stack); + + // process instructions + LuaThread.onCall( this ); + try { + while ( true ) { + if (DebugLib.DEBUG_ENABLED) + DebugLib.debugBytecode(pc, v, top); + + // pull out instruction + i = code[pc++]; + a = ((i>>6) & 0xff); + + // process the op code + switch ( i & 0x3f ) { + + case Lua.OP_MOVE:/* A B R(A):= R(B) */ + stack[a] = stack[i>>>23]; + continue; + + case Lua.OP_LOADK:/* A Bx R(A):= Kst(Bx) */ + stack[a] = k[i>>>14]; + continue; + + case Lua.OP_LOADBOOL:/* A B C R(A):= (Bool)B: if (C) pc++ */ + stack[a] = (i>>>23!=0)? LuaValue.TRUE: LuaValue.FALSE; + if ((i&(0x1ff<<14)) != 0) + pc++; /* skip next instruction (if C) */ + continue; + + case Lua.OP_LOADNIL: /* A B R(A):= ...:= R(B):= nil */ + for ( b=i>>>23; a<=b; ) + stack[a++] = LuaValue.NIL; + continue; + + case Lua.OP_GETUPVAL: /* A B R(A):= UpValue[B] */ + stack[a] = upValues[i>>>23].getValue(); + continue; + + case Lua.OP_GETGLOBAL: /* A Bx R(A):= Gbl[Kst(Bx)] */ + stack[a] = env.get(k[i>>>14]); + continue; + + case Lua.OP_GETTABLE: /* A B C R(A):= R(B)[RK(C)] */ + stack[a] = stack[i>>>23].get((c=(i>>14)&0x1ff)>0xff? k[c&0x0ff]: stack[c]); + continue; + + case Lua.OP_SETGLOBAL: /* A Bx Gbl[Kst(Bx)]:= R(A) */ + env.set(k[i>>>14], stack[a]); + continue; + + case Lua.OP_SETUPVAL: /* A B UpValue[B]:= R(A) */ + upValues[i>>>23].setValue(stack[a]); + continue; + + case Lua.OP_SETTABLE: /* A B C R(A)[RK(B)]:= RK(C) */ + stack[a].set(((b=i>>>23)>0xff? k[b&0x0ff]: stack[b]), (c=(i>>14)&0x1ff)>0xff? k[c&0x0ff]: stack[c]); + continue; + + case Lua.OP_NEWTABLE: /* A B C R(A):= {} (size = B,C) */ + stack[a] = new LuaTable(i>>>23,(i>>14)&0x1ff); + continue; + + case Lua.OP_SELF: /* A B C R(A+1):= R(B): R(A):= R(B)[RK(C)] */ + stack[a+1] = (o = stack[i>>>23]); + stack[a] = o.get((c=(i>>14)&0x1ff)>0xff? k[c&0x0ff]: stack[c]); + continue; + + case Lua.OP_ADD: /* A B C R(A):= RK(B) + RK(C) */ + stack[a] = ((b=i>>>23)>0xff? k[b&0x0ff]: stack[b]).add((c=(i>>14)&0x1ff)>0xff? k[c&0x0ff]: stack[c]); + continue; + + case Lua.OP_SUB: /* A B C R(A):= RK(B) - RK(C) */ + stack[a] = ((b=i>>>23)>0xff? k[b&0x0ff]: stack[b]).sub((c=(i>>14)&0x1ff)>0xff? k[c&0x0ff]: stack[c]); + continue; + + case Lua.OP_MUL: /* A B C R(A):= RK(B) * RK(C) */ + stack[a] = ((b=i>>>23)>0xff? k[b&0x0ff]: stack[b]).mul((c=(i>>14)&0x1ff)>0xff? k[c&0x0ff]: stack[c]); + continue; + + case Lua.OP_DIV: /* A B C R(A):= RK(B) / RK(C) */ + stack[a] = ((b=i>>>23)>0xff? k[b&0x0ff]: stack[b]).div((c=(i>>14)&0x1ff)>0xff? k[c&0x0ff]: stack[c]); + continue; + + case Lua.OP_MOD: /* A B C R(A):= RK(B) % RK(C) */ + stack[a] = ((b=i>>>23)>0xff? k[b&0x0ff]: stack[b]).mod((c=(i>>14)&0x1ff)>0xff? k[c&0x0ff]: stack[c]); + continue; + + case Lua.OP_POW: /* A B C R(A):= RK(B) ^ RK(C) */ + stack[a] = ((b=i>>>23)>0xff? k[b&0x0ff]: stack[b]).pow((c=(i>>14)&0x1ff)>0xff? k[c&0x0ff]: stack[c]); + continue; + + case Lua.OP_UNM: /* A B R(A):= -R(B) */ + stack[a] = stack[i>>>23].neg(); + continue; + + case Lua.OP_NOT: /* A B R(A):= not R(B) */ + stack[a] = stack[i>>>23].not(); + continue; + + case Lua.OP_LEN: /* A B R(A):= length of R(B) */ + stack[a] = stack[i>>>23].len(); + continue; + + case Lua.OP_CONCAT: /* A B C R(A):= R(B).. ... ..R(C) */ + b = i>>>23; + c = (i>>14)&0x1ff; + { + Buffer sb = new Buffer(); + for ( ; b<=c; ) + sb.append( stack[b++].checkstring() ); + stack[a] = sb.tostrvalue(); + } + continue; + + case Lua.OP_JMP: /* sBx pc+=sBx */ + pc += (i>>>14)-0x1ffff; + continue; + + case Lua.OP_EQ: /* A B C if ((RK(B) == RK(C)) ~= A) then pc++ */ + if ( ((b=i>>>23)>0xff? k[b&0x0ff]: stack[b]).eq_b((c=(i>>14)&0x1ff)>0xff? k[c&0x0ff]: stack[c]) != (a!=0) ) + ++pc; + continue; + + case Lua.OP_LT: /* A B C if ((RK(B) < RK(C)) ~= A) then pc++ */ + if ( ((b=i>>>23)>0xff? k[b&0x0ff]: stack[b]).lt_b((c=(i>>14)&0x1ff)>0xff? k[c&0x0ff]: stack[c]) != (a!=0) ) + ++pc; + continue; + + case Lua.OP_LE: /* A B C if ((RK(B) <= RK(C)) ~= A) then pc++ */ + if ( ((b=i>>>23)>0xff? k[b&0x0ff]: stack[b]).lteq_b((c=(i>>14)&0x1ff)>0xff? k[c&0x0ff]: stack[c]) != (a!=0) ) + ++pc; + continue; + + case Lua.OP_TEST: /* A C if not (R(A) <=> C) then pc++ */ + if ( stack[a].toboolean() != ((i&(0x1ff<<14))!=0) ) + ++pc; + continue; + + case Lua.OP_TESTSET: /* A B C if (R(B) <=> C) then R(A):= R(B) else pc++ */ + /* note: doc appears to be reversed */ + if ( (o=stack[i>>>23]).toboolean() != ((i&(0x1ff<<14))!=0) ) + ++pc; + else + stack[a] = o; // TODO: should be sBx? + continue; + + case Lua.OP_CALL: /* A B C R(A), ... ,R(A+C-2):= R(A)(R(A+1), ... ,R(A+B-1)) */ + switch ( i & (Lua.MASK_B | Lua.MASK_C) ) { + case (1<>>23; + c = (i>>14)&0x1ff; + v = b>0? + varargsOf(stack,a+1,b-1): // exact arg count + varargsOf(stack, a+1, top-v.narg()-(a+1), v); // from prev top + v = stack[a].invoke(v); + if ( c > 0 ) { + while ( --c > 0 ) + stack[a+c-1] = v.arg(c); + v = NONE; // TODO: necessary? + } else { + top = a + v.narg(); + } + continue; + } + + case Lua.OP_TAILCALL: /* A B C return R(A)(R(A+1), ... ,R(A+B-1)) */ + switch ( i & (Lua.MASK_B | Lua.MASK_C) ) { + case (1<>>23; + c = (i>>14)&0x1ff; + v = b>0? + varargsOf(stack,a+1,b-1): // exact arg count + varargsOf(stack, a+1, top-v.narg()-(a+1), v); // from prev top + switch ( c ) { + case 1: stack[a].invoke(v); return NONE; + case 2: return stack[a].invoke(v).arg1(); + default: return stack[a].invoke(v); + } + } + + case Lua.OP_RETURN: /* A B return R(A), ... ,R(A+B-2) (see note) */ + closeUpValues(); + b = i>>>23; + switch ( b ) { + case 0: return varargsOf(stack, a, top-v.narg()-a, v); + case 1: return NONE; + case 2: return stack[a]; + default: + return varargsOf(stack, a, b-1); + } + + case Lua.OP_FORLOOP: /* A sBx R(A)+=R(A+2): if R(A) >>14)-0x1ffff; + } + } + continue; + + case Lua.OP_FORPREP: /* A sBx R(A)-=R(A+2): pc+=sBx */ + { + LuaValue init = stack[a].checknumber(); + LuaValue limit = stack[a + 1].checknumber(); + LuaValue step = stack[a + 2].checknumber(); + stack[a] = init.sub(step); + stack[a + 1] = limit; + stack[a + 2] = step; + pc += (i>>>14)-0x1ffff; + } + continue; + + case Lua.OP_TFORLOOP: /* + * A C R(A+3), ... ,R(A+2+C):= R(A)(R(A+1), + * R(A+2)): if R(A+3) ~= nil then R(A+2)=R(A+3) + * else pc++ + */ + // TODO: stack call on for loop body, such as: stack[a].call(ci); + v = stack[a].invoke(varargsOf(stack[a+1],stack[a+2])); + if ( (o=v.arg1()).isnil() ) + ++pc; + else { + stack[a+2] = stack[a+3] = o; + for ( c=(i>>14)&0x1ff; c>1; --c ) + stack[a+2+c] = v.arg(c); + v = NONE; // todo: necessary? + } + continue; + + case Lua.OP_SETLIST: /* A B C R(A)[(C-1)*FPF+i]:= R(A+i), 1 <= i <= B */ + { + if ( (c=(i>>14)&0x1ff) == 0 ) + c = code[pc++]; + int offset = (c-1) * Lua.LFIELDS_PER_FLUSH; + o = stack[a]; + if ( (b=i>>>23) == 0 ) { + b = top - a - 1; + int m = b - v.narg(); + int j=1; + for ( ;j<=m; j++ ) + o.set(offset+j, stack[a + j]); + for ( ;j<=b; j++ ) + o.set(offset+j, v.arg(j-m)); + } else { + o.presize( offset + b ); + for (int j=1; j<=b; j++) + o.set(offset+j, stack[a + j]); + } + } + continue; + + case Lua.OP_CLOSE: /* A close all variables in the stack up to (>=) R(A)*/ + closeUpValues( a ); + continue; + + case Lua.OP_CLOSURE: /* A Bx R(A):= closure(KPROTO[Bx], R(A), ... ,R(A+n)) */ + { + Prototype newp = p.p[i>>>14]; + LuaClosure newcl = new LuaClosure(newp, env); + for ( int j=0, nup=newp.nups; j>>23; + newcl.upValues[j] = (i&4) != 0? + upValues[b]: + findUpValue(stack,b); + } + stack[a] = newcl; + } + continue; + + case Lua.OP_VARARG: /* A B R(A), R(A+1), ..., R(A+B-1) = vararg */ + b = i>>>23; + if ( b == 0 ) { + top = a + (b = varargs.narg()); + v = varargs; + } else { + for ( int j=1; j=pc ) + le.addTracebackLine(p.source+":"+p.lineinfo[pc-1]); + throw le; + } catch ( Throwable t ) { + LuaError le = new LuaError(t); + if ( p.lineinfo!=null && p.lineinfo.length>=pc ) + le.addTracebackLine(p.source+":"+p.lineinfo[pc-1]); + throw le; + } finally { + LuaThread.onReturn(); + } + } + + // TODO: inline, optimize when targs come in ascending order? + UpValue findUpValue(LuaValue[] stack, int target) { + UpValue prev=null,up=openUpValues; + while ( up != null ) { + if (up.index == target) { + return up; + } else if (up.index < target) { + break; + } + up = (prev=up).next; + } + up = new UpValue(stack, target, up); + if ( prev!=null ) + prev.next = up; + else + this.openUpValues = up; + return up; + } + + // TODO: inline? + void closeUpValues(int limit) { + UpValue prev=null,up=openUpValues; + while ( up != null ) { + UpValue next = up.next; + if ( up.close(limit) ) { + if ( prev==null ) + this.openUpValues = up.next; + else + prev.next = up.next; + up.next = null; + } else { + prev = up; + } + up = next; + } + } + + // TODO: inline? + void closeUpValues() { + UpValue up=openUpValues; + while ( up != null ) { + UpValue next = up.next; + up.close(-1); + up.next = null; + up = next; + } + openUpValues = null; + } + +} diff --git a/src/core/org/luaj/vm2/LuaDouble.java b/src/core/org/luaj/vm2/LuaDouble.java new file mode 100644 index 00000000..ccd151ab --- /dev/null +++ b/src/core/org/luaj/vm2/LuaDouble.java @@ -0,0 +1,192 @@ +/******************************************************************************* +* Copyright (c) 2009 Luaj.org. All rights reserved. +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +* THE SOFTWARE. +******************************************************************************/ +package org.luaj.vm2; + +import java.util.Hashtable; + +import org.luaj.vm2.lib.MathLib; + +public class LuaDouble extends LuaNumber { + + public static final LuaDouble NAN = new LuaDouble( Double.NaN ); + public static final LuaDouble POSINF = new LuaDouble( Double.POSITIVE_INFINITY ); + public static final LuaDouble NEGINF = new LuaDouble( Double.NEGATIVE_INFINITY ); + + private static Hashtable ALIASES = new Hashtable(); + static { + ALIASES.put( "NaN", "nan" ); + ALIASES.put( "Infinity", "inf" ); + ALIASES.put( "-Infinity", "-inf" ); + } + + final double v; + + public static LuaNumber valueOf(double d) { + int id = (int) d; + return d==id? (LuaNumber) LuaInteger.valueOf(id): (LuaNumber) new LuaDouble(d); + } + + /** Don't allow ints to be boxed by DoubleValues */ + private LuaDouble(double d) { + this.v = d; + } + + public int hashCode() { + return (int) Double.doubleToLongBits(v); + } + + public boolean islong() { + return v == (long) v; + } + + public byte tobyte() { return (byte) (long) v; } + public char tochar() { return (char) (long) v; } + public double todouble() { return v; } + public float tofloat() { return (float) v; } + public int toint() { return (int) (long) v; } + public long tolong() { return (long) v; } + public short toshort() { return (short) (long) v; } + + public double optdouble(double defval) { return v; } + public int optint(int defval) { return (int) (long) v; } + public LuaInteger optinteger(LuaInteger defval) { return LuaInteger.valueOf((int) (long)v); } + public long optlong(long defval) { return (long) v; } + + public LuaInteger checkinteger() { return LuaInteger.valueOf( (int) (long) v ); } + + // unary operators + public LuaValue neg() { return valueOf(-v); } + + // object equality, used for key comparison + public boolean equals(Object o) { return o instanceof LuaDouble? ((LuaDouble)o).v == v: false; } + + // arithmetic equality + public boolean eq_b( LuaValue rhs ) { return rhs.eq_b(v); } + public boolean eq_b( double rhs ) { return v == rhs; } + public boolean eq_b( int rhs ) { return v == rhs; } + + // basic binary arithmetic + public LuaValue add( LuaValue rhs ) { return rhs.add(v); } + public LuaValue add( double lhs ) { return LuaDouble.valueOf(lhs + v); } + public LuaValue sub( LuaValue rhs ) { return rhs.subFrom(v); } + public LuaValue subFrom( double lhs ) { return LuaDouble.valueOf(lhs - v); } + public LuaValue mul( LuaValue rhs ) { return rhs.mul(v); } + public LuaValue mul( double lhs ) { return LuaDouble.valueOf(lhs * v); } + public LuaValue mul( int lhs ) { return LuaDouble.valueOf(lhs * v); } + public LuaValue pow( LuaValue rhs ) { return rhs.powWith(v); } + public LuaValue powWith( double lhs ) { return MathLib.dpow(lhs,v); } + public LuaValue powWith( int lhs ) { return MathLib.dpow(lhs,v); } + public LuaValue div( LuaValue rhs ) { return rhs.divInto(v); } + public LuaValue divInto( double lhs ) { return LuaDouble.ddiv(lhs,v); } + public LuaValue mod( LuaValue rhs ) { return rhs.modFrom(v); } + public LuaValue modFrom( double lhs ) { return LuaDouble.dmod(lhs,v); } + + /** lua division is always double, specific values for singularities */ + public static LuaValue ddiv(double lhs, double rhs) { + return rhs!=0? valueOf( lhs / rhs ): lhs>0? POSINF: lhs==0? NAN: NEGINF; + } + + /** lua module is always wrt double. */ + public static LuaValue dmod(double lhs, double rhs) { + return rhs!=0? valueOf( lhs-rhs*Math.floor(lhs/rhs) ): NAN; + } + + // relational operators + public LuaValue lt( LuaValue rhs ) { return rhs.gt_b(v)? LuaValue.TRUE: FALSE; } + public boolean lt_b( LuaValue rhs ) { return rhs.gt_b(v); } + public boolean lt_b( int rhs ) { return v < rhs; } + public boolean lt_b( double rhs ) { return v < rhs; } + public LuaValue lteq( LuaValue rhs ) { return rhs.gteq_b(v)? LuaValue.TRUE: FALSE; } + public boolean lteq_b( LuaValue rhs ) { return rhs.gteq_b(v); } + public boolean lteq_b( int rhs ) { return v <= rhs; } + public boolean lteq_b( double rhs ) { return v <= rhs; } + public LuaValue gt( LuaValue rhs ) { return rhs.lt_b(v)? LuaValue.TRUE: FALSE; } + public boolean gt_b( LuaValue rhs ) { return rhs.lt_b(v); } + public boolean gt_b( int rhs ) { return v > rhs; } + public boolean gt_b( double rhs ) { return v > rhs; } + public LuaValue gteq( LuaValue rhs ) { return rhs.lteq_b(v)? LuaValue.TRUE: FALSE; } + public boolean gteq_b( LuaValue rhs ) { return rhs.lteq_b(v); } + public boolean gteq_b( int rhs ) { return v >= rhs; } + public boolean gteq_b( double rhs ) { return v >= rhs; } + + // string comparison + public int strcmp( LuaString rhs ) { typerror("attempt to compare number with string"); return 0; } + + // concatenation + public String concat_s(LuaValue rhs) { return rhs.concatTo_s(Double.toString(v)); } + public String concatTo_s(String lhs) { return lhs + v; } + + public String toString() { + /* + if ( v == 0.0 ) { // never occurs in J2me + long bits = Double.doubleToLongBits( v ); + return ( bits >> 63 == 0 ) ? "0" : "-0"; + } + */ + long l = (long) v; + if ( l == v ) return Long.toString(l); + String s = Double.toString(v); + Object n = ALIASES.get(s); + return n!=null? (String)n: s; + } + + public LuaString strvalue() { + return LuaString.valueOf(toString()); + } + + public LuaString optstring(LuaString defval) { + return LuaString.valueOf(toString()); + } + + public String optString(String defval) { + long l = (long)v; + return v==l? Long.toString(l): Double.toString(v); + } + + public LuaNumber optnumber(LuaNumber defval) { + return this; + } + + public boolean isnumber() { + return true; + } + + public boolean isstring() { + return true; + } + + public LuaValue tonumber() { + return this; + } + public int checkint() { return (int) (long) v; } + public long checklong() { return (long) v; } + public LuaNumber checknumber() { return this; } + public double checkdouble() { return v; } + + public String checkString() { + return toString(); + } + public LuaString checkstring() { + return LuaString.valueOf(toString()); + } + +} diff --git a/src/core/org/luaj/vm2/LuaError.java b/src/core/org/luaj/vm2/LuaError.java new file mode 100644 index 00000000..cf1bad9b --- /dev/null +++ b/src/core/org/luaj/vm2/LuaError.java @@ -0,0 +1,98 @@ +/******************************************************************************* +* Copyright (c) 2009 Luaj.org. All rights reserved. +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +* THE SOFTWARE. +******************************************************************************/ +package org.luaj.vm2; + +import java.util.Vector; + +/** + * RuntimeException that is thrown and caught in response to a lua error. + * This error does not indicate any problem with the normal functioning + * of the Lua VM, but rather indicates that the lua script being interpreted + * has encountered a lua error, eigher via LuaState.error() or lua error() calls. + * + */ +public class LuaError extends RuntimeException { + private static final long serialVersionUID = 1L; + + private LuaValue msgvalue = null; + private Vector traceback = null; + + /** Run the error hook if there is one */ + private static String errorHook(String msg) { + LuaThread thread = LuaThread.getRunning(); + if ( thread.err != null ) { + try { + return thread.err.call( LuaValue.valueOf(msg) ).toString(); + } catch ( Throwable t ) { + return "error in error handling"; + } + } + return msg; + } + + /** + * Construct a LuaErrorException in response to a Throwable that was caught + * indicating a problem with the VM rather than the lua code. + * + * All errors generated from lua code should throw LuaError(String) instead. + */ + public LuaError(Throwable cause) { + this( errorHook( "vm error: "+cause ) ); + } + + /** + * Construct a LuaError with a specific message indicating a problem + * within the lua code itself such as an argument type error. + * + * @param message message to supply + */ + public LuaError(String message) { + super( errorHook( message ) ); + } + + /** Get the message, including source line info if there is any */ + public String getMessage() { + String msg = super.getMessage(); + return msg!=null && traceback!=null? traceback.elementAt(0)+": "+msg: msg; + } + + /** Add a line of traceback info */ + public void addTracebackLine( String line ) { + if ( traceback == null ) { + traceback = new Vector(); + } + traceback.addElement( line ); + } + + /** Print the message and stack trace */ + public void printStackTrace() { + System.out.println( toString() ); + if ( traceback != null ) { + for ( int i=0,n=traceback.size(); i=-256? intValues[i+256]: new LuaInteger(i); + }; + + public static LuaNumber valueOf(long l) { + int i = (int) l; + return l==i? (i<=255 && i>=-256? intValues[i+256]: + (LuaNumber) new LuaInteger(i)): + (LuaNumber) LuaDouble.valueOf(l); + } + + public final int v; + + /** package protected constructor, @see IntValue.valueOf(int) */ + LuaInteger(int i) { + this.v = i; + } + + public boolean isint() { return true; } + public boolean isinttype() { return true; } + public boolean islong() { return true; } + + public byte tobyte() { return (byte) v; } + public char tochar() { return (char) v; } + public double todouble() { return v; } + public float tofloat() { return v; } + public int toint() { return v; } + public long tolong() { return v; } + public short toshort() { return (short) v; } + + public double optdouble(double defval) { return v; } + public int optint(int defval) { return v; } + public LuaInteger optinteger(LuaInteger defval) { return this; } + public long optlong(long defval) { return v; } + + public String toString() { + return Integer.toString(v); + } + + public LuaString strvalue() { + return LuaString.valueOf(Integer.toString(v)); + } + + public LuaString optstring(LuaString defval) { + return LuaString.valueOf(Integer.toString(v)); + } + + public String optString(String defval) { + return Integer.toString(v); + } + + public LuaInteger checkinteger() { + return this; + } + + public boolean isstring() { + return true; + } + + public int hashCode() { + return v; + } + + // unary operators + public LuaValue neg() { return valueOf(-(long)v); } + + // object equality, used for key comparison + public boolean equals(Object o) { return o instanceof LuaInteger? ((LuaInteger)o).v == v: false; } + + // arithmetic equality + public boolean eq_b( LuaValue rhs ) { return rhs.eq_b(v); } + public boolean eq_b( double rhs ) { return v == rhs; } + public boolean eq_b( int rhs ) { return v == rhs; } + + // arithmetic operators + public LuaValue add( LuaValue rhs ) { return rhs.add(v); } + public LuaValue add( double lhs ) { return LuaDouble.valueOf(lhs + v); } + public LuaValue add( int lhs ) { return LuaInteger.valueOf(lhs + (long)v); } + public LuaValue sub( LuaValue rhs ) { return rhs.subFrom(v); } + public LuaValue subFrom( double lhs ) { return LuaDouble.valueOf(lhs - v); } + public LuaValue subFrom( int lhs ) { return LuaInteger.valueOf(lhs - (long)v); } + public LuaValue mul( LuaValue rhs ) { return rhs.mul(v); } + public LuaValue mul( double lhs ) { return LuaDouble.valueOf(lhs * v); } + public LuaValue mul( int lhs ) { return LuaInteger.valueOf(lhs * (long)v); } + public LuaValue pow( LuaValue rhs ) { return rhs.powWith(v); } + public LuaValue powWith( double lhs ) { return MathLib.dpow(lhs,v); } + public LuaValue powWith( int lhs ) { return MathLib.dpow(lhs,v); } + public LuaValue div( LuaValue rhs ) { return rhs.divInto(v); } + public LuaValue divInto( double lhs ) { return LuaDouble.ddiv(lhs,v); } + public LuaValue mod( LuaValue rhs ) { return rhs.modFrom(v); } + public LuaValue modFrom( double lhs ) { return LuaDouble.dmod(lhs,v); } + + // relational operators + public LuaValue lt( LuaValue rhs ) { return rhs.gt_b(v)? LuaValue.TRUE: FALSE; } + public boolean lt_b( LuaValue rhs ) { return rhs.gt_b(v); } + public boolean lt_b( int rhs ) { return v < rhs; } + public boolean lt_b( double rhs ) { return v < rhs; } + public LuaValue lteq( LuaValue rhs ) { return rhs.gteq_b(v)? LuaValue.TRUE: FALSE; } + public boolean lteq_b( LuaValue rhs ) { return rhs.gteq_b(v); } + public boolean lteq_b( int rhs ) { return v <= rhs; } + public boolean lteq_b( double rhs ) { return v <= rhs; } + public LuaValue gt( LuaValue rhs ) { return rhs.lt_b(v)? LuaValue.TRUE: FALSE; } + public boolean gt_b( LuaValue rhs ) { return rhs.lt_b(v); } + public boolean gt_b( int rhs ) { return v > rhs; } + public boolean gt_b( double rhs ) { return v > rhs; } + public LuaValue gteq( LuaValue rhs ) { return rhs.lteq_b(v)? LuaValue.TRUE: FALSE; } + public boolean gteq_b( LuaValue rhs ) { return rhs.lteq_b(v); } + public boolean gteq_b( int rhs ) { return v >= rhs; } + public boolean gteq_b( double rhs ) { return v >= rhs; } + + // string comparison + public int strcmp( LuaString rhs ) { typerror("attempt to compare number with string"); return 0; } + + // concatenation + public String concat_s(LuaValue rhs) { return rhs.concatTo_s(Integer.toString(v)); } + public String concatTo_s(String lhs) { return lhs + v; } + + public int checkint() { + return v; + } + public long checklong() { + return v; + } + public double checkdouble() { + return v; + } + public String checkString() { + return String.valueOf(v); + } + public LuaString checkstring() { + return valueOf( String.valueOf(v) ); + } + +} diff --git a/src/core/org/luaj/vm2/LuaNil.java b/src/core/org/luaj/vm2/LuaNil.java new file mode 100644 index 00000000..367af705 --- /dev/null +++ b/src/core/org/luaj/vm2/LuaNil.java @@ -0,0 +1,82 @@ +/******************************************************************************* +* Copyright (c) 2009 Luaj.org. All rights reserved. +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +* THE SOFTWARE. +******************************************************************************/ +package org.luaj.vm2; + +public class LuaNil extends LuaValue { + + public static LuaValue s_metatable; + + LuaNil() {} + + public int type() { + return LuaValue.TNIL; + } + + public String typename() { + return "nil"; + } + + public String toString() { + return "nil"; + } + + public LuaValue not() { + return LuaValue.TRUE; + } + + public boolean toboolean() { + return false; + } + + public boolean isnil() { + return true; + } + + public LuaValue getmetatable() { + return s_metatable; + } + + public boolean equals(Object o) { + return o instanceof LuaNil; + } + + public LuaValue checknotnil() { + return typerror("value"); + } + + // optional argument conversions - nil alwas falls badk to default value + public boolean optboolean(boolean defval) { return defval; } + public LuaClosure optclosure(LuaClosure defval) { return defval; } + public double optdouble(double defval) { return defval; } + public LuaFunction optfunction(LuaFunction defval) { return defval; } + public int optint(int defval) { return defval; } + public LuaInteger optinteger(LuaInteger defval) { return defval; } + public long optlong(long defval) { return defval; } + public LuaNumber optnumber(LuaNumber defval) { return defval; } + public LuaTable opttable(LuaTable defval) { return defval; } + public LuaThread optthread(LuaThread defval) { return defval; } + public String optString(String defval) { return defval; } + public LuaString optstring(LuaString defval) { return defval; } + public Object optuserdata(Object defval) { return defval; } + public Object optuserdata(Class c, Object defval) { return defval; } + public LuaValue optvalue(LuaValue defval) { return defval; } +} diff --git a/src/core/org/luaj/vm2/LuaNumber.java b/src/core/org/luaj/vm2/LuaNumber.java new file mode 100644 index 00000000..f3b8cd46 --- /dev/null +++ b/src/core/org/luaj/vm2/LuaNumber.java @@ -0,0 +1,61 @@ +/******************************************************************************* + * Copyright (c) 2009 Luaj.org. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + ******************************************************************************/ +package org.luaj.vm2; + +abstract +public class LuaNumber extends LuaValue { + + public static LuaValue s_metatable; + + public int type() { + return TNUMBER; + } + + public String typename() { + return "number"; + } + + public LuaNumber checknumber() { + return this; + } + + public LuaNumber optnumber(LuaNumber defval) { + return this; + } + + public LuaValue tonumber() { + return this; + } + + public boolean isnumber() { + return true; + } + + public boolean isstring() { + return true; + } + + public LuaValue getmetatable() { + return s_metatable; + } + +} diff --git a/src/core/org/luaj/vm2/LuaString.java b/src/core/org/luaj/vm2/LuaString.java new file mode 100644 index 00000000..29fa46c6 --- /dev/null +++ b/src/core/org/luaj/vm2/LuaString.java @@ -0,0 +1,473 @@ +/******************************************************************************* +* Copyright (c) 2009 Luaj.org. All rights reserved. +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +* THE SOFTWARE. +******************************************************************************/ +package org.luaj.vm2; + +import java.io.ByteArrayInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.io.InputStream; + +import org.luaj.vm2.lib.MathLib; +import org.luaj.vm2.lib.StringLib; + +public class LuaString extends LuaValue { + + public static LuaValue s_metatable; + + public final byte[] m_bytes; + public final int m_offset; + public final int m_length; + + public static LuaString valueOf(String string) { + char[] c = string.toCharArray(); + byte[] b = new byte[lengthAsUtf8(c)]; + encodeToUtf8(c, b, 0); + return new LuaString(b); + } + + public LuaString(byte[] bytes, int offset, int length) { + this.m_bytes = bytes; + this.m_offset = offset; + this.m_length = length; + } + + public LuaString(byte[] bytes) { + this.m_bytes = bytes; + this.m_offset = 0; + this.m_length = bytes.length; + } + + public boolean isstring() { + return true; + } + + public LuaValue getmetatable() { + return s_metatable; + } + + public int type() { + return LuaValue.TSTRING; + } + + public String typename() { + return "string"; + } + + public String toString() { + return decodeAsUtf8(m_bytes, m_offset, m_length); + } + + // get is delegated to the string library + public LuaValue get(LuaValue key) { + return s_metatable!=null? gettable(this,key): StringLib.instance.get(key); + } + + // unary operators + public LuaValue neg() { return checkarith().neg(); } + + // basic binary arithmetic + public LuaValue add( LuaValue rhs ) { return checkarith().add(rhs); } + public LuaValue add( double lhs ) { return checkarith().add(lhs); } + public LuaValue sub( LuaValue rhs ) { return checkarith().sub(rhs); } + public LuaValue subFrom( double lhs ) { return checkarith().subFrom(lhs); } + public LuaValue mul( LuaValue rhs ) { return checkarith().mul(rhs); } + public LuaValue mul( double lhs ) { return checkarith().mul(lhs); } + public LuaValue mul( int lhs ) { return checkarith().mul(lhs); } + public LuaValue pow( LuaValue rhs ) { return checkarith().pow(rhs); } + public LuaValue powWith( double lhs ) { return checkarith().powWith(lhs); } + public LuaValue powWith( int lhs ) { return checkarith().powWith(lhs); } + public LuaValue div( LuaValue rhs ) { return checkarith().div(rhs); } + public LuaValue divInto( double lhs ) { return checkarith().divInto(lhs); } + public LuaValue mod( LuaValue rhs ) { return checkarith().mod(rhs); } + public LuaValue modFrom( double lhs ) { return checkarith().modFrom(lhs); } + + // relational operators, these only work with other strings + public LuaValue lt( LuaValue rhs ) { return rhs.strcmp(this)>0? LuaValue.TRUE: FALSE; } + public boolean lt_b( LuaValue rhs ) { return rhs.strcmp(this)>0; } + public boolean lt_b( int rhs ) { typerror("attempt to compare string with number"); return false; } + public boolean lt_b( double rhs ) { typerror("attempt to compare string with number"); return false; } + public LuaValue lteq( LuaValue rhs ) { return rhs.strcmp(this)>=0? LuaValue.TRUE: FALSE; } + public boolean lteq_b( LuaValue rhs ) { return rhs.strcmp(this)>=0; } + public boolean lteq_b( int rhs ) { typerror("attempt to compare string with number"); return false; } + public boolean lteq_b( double rhs ) { typerror("attempt to compare string with number"); return false; } + public LuaValue gt( LuaValue rhs ) { return rhs.strcmp(this)<0? LuaValue.TRUE: FALSE; } + public boolean gt_b( LuaValue rhs ) { return rhs.strcmp(this)<0; } + public boolean gt_b( int rhs ) { typerror("attempt to compare string with number"); return false; } + public boolean gt_b( double rhs ) { typerror("attempt to compare string with number"); return false; } + public LuaValue gteq( LuaValue rhs ) { return rhs.strcmp(this)<=0? LuaValue.TRUE: FALSE; } + public boolean gteq_b( LuaValue rhs ) { return rhs.strcmp(this)<=0; } + public boolean gteq_b( int rhs ) { typerror("attempt to compare string with number"); return false; } + public boolean gteq_b( double rhs ) { typerror("attempt to compare string with number"); return false; } + + // concatenation + public String concat_s(LuaValue rhs) { return rhs.concatTo_s(toString()); } + public String concatTo_s(String lhs) { return lhs + toString(); } + + // string comparison + public int strcmp(LuaValue lhs) { return -lhs.strcmp(this); } + public int strcmp(LuaString rhs) { + for ( int i=0, j=0; i>5)+1; /* if string is too long, don't hash all its chars */ + for (int l1=m_length; l1>=step; l1-=step) /* compute hash */ + h = h ^ ((h<<5)+(h>>2)+(((int) m_bytes[m_offset+l1-1] ) & 0x0FF )); + return h; + } + + // object comparison, used in key comparison + public boolean equals( Object o ) { + if ( o instanceof LuaString ) { + LuaString s = (LuaString) o; + if ( s.m_length != m_length ) + return false; + if ( s.hashCode() != hashCode() ) + return false; + for ( int i=0; i=0 ) + if ( a[i++]!=b[j++] ) + return false; + return true; + } + + public void write(DataOutputStream writer, int i, int len) throws IOException { + writer.write(m_bytes,m_offset+i,len); + } + + public LuaValue len() { + return LuaInteger.valueOf(m_length); + } + + public int length() { + return m_length; + } + + public int luaByte(int index) { + return m_bytes[m_offset + index] & 0x0FF; + } + + public int charAt( int index ) { + if ( index < 0 || index >= m_length ) + throw new IndexOutOfBoundsException(); + return luaByte( index ); + } + + public String checkString() { + return toString(); + } + + public LuaString checkstring() { + return this; + } + + public InputStream toInputStream() { + return new ByteArrayInputStream(m_bytes, m_offset, m_length); + } + + /** + * Copy the bytes of the string into the given byte array. + */ + public void copyInto( int strOffset, byte[] bytes, int arrayOffset, int len ) { + System.arraycopy( m_bytes, m_offset+strOffset, bytes, arrayOffset, len ); + } + + /** Java version of strpbrk, which is a terribly named C function. */ + public int indexOfAny( LuaString accept ) { + final int ilimit = m_offset + m_length; + final int jlimit = accept.m_offset + accept.m_length; + for ( int i = m_offset; i < ilimit; ++i ) { + for ( int j = accept.m_offset; j < jlimit; ++j ) { + if ( m_bytes[i] == accept.m_bytes[j] ) { + return i - m_offset; + } + } + } + return -1; + } + + public int indexOf( byte b, int start ) { + for ( int i=0, j=m_offset+start; i < m_length; ++i ) { + if ( m_bytes[j++] == b ) + return i; + } + return -1; + } + + public int indexOf( LuaString s, int start ) { + final int slen = s.length(); + final int limit = m_offset + m_length - slen; + for ( int i = m_offset + start; i <= limit; ++i ) { + if ( equals( m_bytes, i, s.m_bytes, s.m_offset, slen ) ) { + return i; + } + } + return -1; + } + + public int lastIndexOf( LuaString s ) { + final int slen = s.length(); + final int limit = m_offset + m_length - slen; + for ( int i = limit; i >= m_offset; --i ) { + if ( equals( m_bytes, i, s.m_bytes, s.m_offset, slen ) ) { + return i; + } + } + return -1; + } + + // --------------------- utf8 conversion ------------------------- + + /** + * Convert to Java String interpreting as utf8 characters + */ + public static String decodeAsUtf8(byte[] bytes, int offset, int length) { + int i,j,n,b; + for ( i=offset,j=offset+length,n=0; i=0||i>=j)? b: + (b<-32||i+1>=j)? (((b&0x3f) << 6) | (bytes[i++]&0x3f)): + (((b&0xf) << 12) | ((bytes[i++]&0x3f)<<6) | (bytes[i++]&0x3f))); + } + return new String(chars); + } + + /** + * Count the number of bytes required to encode the string as UTF-8. + */ + public static int lengthAsUtf8(char[] chars) { + int i,b; + char c; + for ( i=b=chars.length; --i>=0; ) + if ( (c=chars[i]) >=0x80 ) + b += (c>=0x800)? 2: 1; + return b; + } + + /** + * Encode the given Java string as UTF-8 bytes, writing the result to bytes + * starting at offset. The string should be measured first with lengthAsUtf8 + * to make sure the given byte array is large enough. + */ + public static void encodeToUtf8(char[] chars, byte[] bytes, int off) { + final int n = chars.length; + char c; + for ( int i=0, j=off; i>6) & 0x1f)); + bytes[j++] = (byte) (0x80 | ( c & 0x3f)); + } else { + bytes[j++] = (byte) (0xE0 | ((c>>12) & 0x0f)); + bytes[j++] = (byte) (0x80 | ((c>>6) & 0x3f)); + bytes[j++] = (byte) (0x80 | ( c & 0x3f)); + } + } + } + + // --------------------- number conversion ----------------------- + + /** + * convert to a number using a supplied base, or NIL if it can't be converted + * @return IntValue, DoubleValue, or NIL depending on the content of the string. + */ + public LuaValue tonumber( int base ) { + if ( base >= 2 && base <= 36 ) { + int i=m_offset,j=m_offset+m_length; + while ( i=j ) return FALSE; + if ( ( base == 10 || base == 16 ) && ( m_bytes[i]=='0' && i+1='0'&&m_bytes[i]<='9')? '0': + m_bytes[i]>='A'&&m_bytes[i]<='Z'? ('A'-10): ('a'-10)); + if ( digit < 0 || digit >= base ) + return NIL; + x = x * base + digit; + } + return valueOf(neg? -x: x); + } + + /** + * Scan and convert a double value, or return NIL if not a double. + * @return DoubleValue, IntValue, or NIL depending on what is found. + */ + private LuaValue scandouble(int start, int end) { + if ( end>start+64 ) end=start+64; + for ( int i=start; i>1)); + for ( int i=0; i 0 && nhash < MIN_HASH_CAPACITY ) + nhash = MIN_HASH_CAPACITY; + array = (narray>0? new LuaValue[narray]: NOVALS); + hashKeys = (nhash>0? new LuaValue[nhash]: NOVALS); + hashValues = (nhash>0? new LuaValue[nhash]: NOVALS); + hashEntries = 0; + } + + public int type() { + return LuaValue.TTABLE; + } + + public String typename() { + return "table"; + } + + public boolean istable() { + return true; + } + + public LuaTable checktable() { + return this; + } + + public LuaTable opttable(LuaTable defval) { + return this; + } + + public void presize( int i ) { + if ( i > array.length ) + array = resize( array, i ); + } + + private static LuaValue[] resize( LuaValue[] old, int n ) { + LuaValue[] v = new LuaValue[n]; + System.arraycopy(old, 0, v, 0, old.length); + return v; + } + + public LuaValue getmetatable() { + if ( m_metatable!=null ) + return m_metatable.rawget(METATABLE).optvalue(m_metatable); + return m_metatable; + } + + public LuaValue setmetatable(LuaValue metatable) { + if ( m_metatable!=null && !m_metatable.rawget(METATABLE).isnil() ) + error("cannot change a protected metatable"); + m_metatable = metatable; + return this; + } + + public LuaValue get( int key ) { + LuaValue v = rawget(key); + return v.isnil() && m_metatable!=null? gettable(this,valueOf(key)): v; + } + + public LuaValue get( LuaValue key ) { + LuaValue v = rawget(key); + return v.isnil() && m_metatable!=null? gettable(this,key): v; + } + + public LuaValue rawget( int key ) { + if ( key>0 && key<=array.length ) + return array[key-1]!=null? array[key-1]: NIL; + return hashget( LuaInteger.valueOf(key) ); + } + + public LuaValue rawget( LuaValue key ) { + if ( key.isinttype() ) { + int ikey = key.toint(); + if ( ikey>0 && ikey<=array.length ) + return array[ikey-1]!=null? array[ikey-1]: NIL; + } + return hashget( key ); + } + + private LuaValue hashget(LuaValue key) { + if ( hashEntries > 0 ) { + LuaValue v = hashValues[hashFindSlot(key)]; + return v!=null? v: NIL; + } + return NIL; + } + + public void set( int key, LuaValue value ) { + if ( m_metatable==null || ! rawget(key).isnil() || ! settable(this,LuaInteger.valueOf(key),value) ) + rawset(key, value); + } + + /** caller must ensure key is not nil */ + public void set( LuaValue key, LuaValue value ) { + key.checknotnil(); + if ( m_metatable==null || ! rawget(key).isnil() || ! settable(this,key,value) ) + rawset(key, value); + } + + public void rawset( int key, LuaValue value ) { + if ( ! arrayset(key, value) ) + hashset( LuaInteger.valueOf(key), value ); + } + + /** caller must ensure key is not nil */ + public void rawset( LuaValue key, LuaValue value ) { + if ( !key.isinttype() || !arrayset(key.toint(), value) ) + hashset( key, value ); + } + + private boolean arrayset( int key, LuaValue value ) { + if ( key>0 && key<=array.length ) { + array[key-1] = (value.isnil()? null: value); + return true; + } else if ( key==array.length+1 && !value.isnil() ) { + expandarray(); + array[key-1] = value; + return true; + } + return false; + } + + private void expandarray() { + int n = array.length; + int m = Math.max(2,n*2); + array = resize(array, m); + for ( int i=n; i array.length ) + return NONE; + LuaValue v = rawget(pos); + for ( LuaValue r=v; !r.isnil(); ) { + r = rawget(pos+1); + rawset(pos++, r); + } + return v.isnil()? NONE: v; + } + + public void insert(int pos, LuaValue value) { + if ( pos == 0 ) + pos = length()+1; + while ( ! value.isnil() ) { + LuaValue v = rawget( pos ); + rawset(pos++, value); + value = v; + } + } + + public LuaValue concat(LuaString sep, int i, int j) { + Buffer sb = new Buffer (); + if ( i<=j ) { + sb.append( get(i).checkstring() ); + while ( ++i<=j ) { + sb.append( sep ); + sb.append( get(i).checkstring() ); + } + } + return sb.tostrvalue(); + } + + public LuaValue getn() { + for ( int n=array.length; --n>0; ) + if ( array[n]!=null ) + return LuaInteger.valueOf(n+1); + return ZERO; + } + + /** + * Get the length of this table, as lua defines it. + */ + public int length() { + int n=array.length+1,m=0; + while ( !rawget(n).isnil() ) { + m = n; + n += array.length+hashEntries+1; + } + while ( n > m+1 ) { + int k = (n+m) / 2; + if ( !rawget(k).isnil() ) + m = k; + else + n = k; + } + return m; + } + + public LuaValue len() { + return LuaInteger.valueOf(length()); + } + + public int maxn() { + int n = 0; + for ( int i=0; i n ) + n = key; + } + } + return n; + } + + + /** + * Get the next element after a particular key in the table + * @return key,value or nil + */ + public Varargs next( LuaValue key ) { + int i = 0; + do { + // find current key index + if ( ! key.isnil() ) { + if ( key.isinttype() ) { + i = key.toint(); + if ( i>0 && i<=array.length ) { + if ( array[i-1] == null ) + error( "invalid key to 'next'" ); + break; + } + } + i = hashFindSlot(key); + if ( hashKeys[i] == null ) + error( "invalid key to 'next'" ); + i += 1+array.length; + } + } while ( false ); + + // check array part + for ( ; i=array.length || array[i]==null? + NIL: + varargsOf(LuaInteger.valueOf(i+1),array[i]); + } + + /** + * Call the supplied function once for each key-value pair + * + * @param func function to call + */ + public LuaValue foreach(LuaValue func) { + LuaValue v = NIL; + for ( int i=0; i 0 ) { + int slot = hashFindSlot( key ); + hashClearSlot( slot ); + } + } + + private void hashClearSlot( int i ) { + if ( hashKeys[ i ] != null ) { + + int j = i; + int n = hashKeys.length; + while ( hashKeys[ j = ( ( j + 1 ) % n ) ] != null ) { + final int k = ( ( hashKeys[ j ].hashCode() )& 0x7FFFFFFF ) % n; + if ( ( j > i && ( k <= i || k > j ) ) || + ( j < i && ( k <= i && k > j ) ) ) { + hashKeys[ i ] = hashKeys[ j ]; + hashValues[ i ] = hashValues[ j ]; + i = j; + } + } + + --hashEntries; + hashKeys[ i ] = null; + hashValues[ i ] = null; + + if ( hashEntries == 0 ) { + hashKeys = NOVALS; + hashValues = NOVALS; + } + } + } + + private boolean checkLoadFactor() { + // Using a load factor of (n+1) >= 7/8 because that is easy to compute without + // overflow or division. + final int hashCapacity = hashKeys.length; + return hashEntries+1 >= (hashCapacity - (hashCapacity>>3)); + } + + private void rehash() { + final int oldCapacity = hashKeys.length; + final int newCapacity = oldCapacity+(oldCapacity>>2)+MIN_HASH_CAPACITY; + + final LuaValue[] oldKeys = hashKeys; + final LuaValue[] oldValues = hashValues; + + hashKeys = new LuaValue[ newCapacity ]; + hashValues = new LuaValue[ newCapacity ]; + + for ( int i = 0; i < oldCapacity; ++i ) { + final LuaValue k = oldKeys[i]; + if ( k != null ) { + final LuaValue v = oldValues[i]; + final int slot = hashFindSlot( k ); + hashKeys[slot] = k; + hashValues[slot] = v; + } + } + } + + // ----------------- sort support ----------------------------- + // + // implemented heap sort from wikipedia + // + // Only sorts the contiguous array part. + // + public void sort(LuaValue comparator) { + int n = array.length; + while ( n > 0 && array[n-1] == null ) + --n; + if ( n > 1 ) + heapSort(n, comparator); + } + + private void heapSort(int count, LuaValue cmpfunc) { + heapify(count, cmpfunc); + for ( int end=count-1; end>0; ) { + swap(end, 0); + siftDown(0, --end, cmpfunc); + } + } + + private void heapify(int count, LuaValue cmpfunc) { + for ( int start=count/2-1; start>=0; --start ) + siftDown(start, count - 1, cmpfunc); + } + + private void siftDown(int start, int end, LuaValue cmpfunc) { + for ( int root=start; root*2+1 <= end; ) { + int child = root*2+1; + if (child < end && compare(child, child + 1, cmpfunc)) + ++child; + if (compare(root, child, cmpfunc)) { + swap(root, child); + root = child; + } else + return; + } + } + + private boolean compare(int i, int j, LuaValue cmpfunc) { + LuaValue a = array[i]; + LuaValue b = array[j]; + if ( a == null || b == null ) + return false; + if ( ! cmpfunc.isnil() ) { + return cmpfunc.call(a,b).toboolean(); + } else { + return a.lt_b(b); + } + } + + private void swap(int i, int j) { + LuaValue a = array[i]; + array[i] = array[j]; + array[j] = a; + } + +} diff --git a/src/core/org/luaj/vm2/LuaThread.java b/src/core/org/luaj/vm2/LuaThread.java new file mode 100644 index 00000000..aa40b805 --- /dev/null +++ b/src/core/org/luaj/vm2/LuaThread.java @@ -0,0 +1,225 @@ +/******************************************************************************* +* Copyright (c) 2007 LuaJ. All rights reserved. +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +* THE SOFTWARE. +******************************************************************************/ +package org.luaj.vm2; + +import org.luaj.vm2.lib.DebugLib; + + + +/** + * Implementation of lua coroutines using Java Threads + */ +public class LuaThread extends LuaValue implements Runnable { + + public static LuaValue s_metatable; + + private static final int STATUS_SUSPENDED = 0; + private static final int STATUS_RUNNING = 1; + private static final int STATUS_NORMAL = 2; + private static final int STATUS_DEAD = 3; + private static final String[] STATUS_NAMES = { + "suspended", + "running", + "normal", + "dead" }; + + private int status = STATUS_SUSPENDED; + + private Thread thread; + private LuaValue env; + private LuaValue func; + private Varargs args; + public LuaValue err; + + + public static final int MAX_CALLSTACK = 64; + public final LuaFunction[] callstack = new LuaFunction[MAX_CALLSTACK]; + public int calls = 0; + + private static final LuaThread mainthread = new LuaThread(); + + // state of running thread including call stack + private static LuaThread running_thread = mainthread; + + // thread-local used by DebugLib to store debugging state + public Object debugState; + + + LuaThread() { + } + + public LuaThread(LuaValue func, LuaValue env) { + this.env = env; + this.func = func; + } + + public int type() { + return LuaValue.TTHREAD; + } + + public String typename() { + return "thread"; + } + + public boolean isthread() { + return true; + } + + public LuaThread optthread(LuaThread defval) { + return this; + } + + public LuaThread checkthread() { + return this; + } + + public LuaValue getmetatable() { + return s_metatable; + } + + public LuaValue getfenv() { + return env; + } + + public void setfenv(LuaValue env) { + this.env = env; + } + + public String getStatus() { + return STATUS_NAMES[status]; + } + + public static LuaThread getRunning() { + return running_thread; + } + + public static boolean isMainThread(LuaThread r) { + return r == mainthread; + } + + /** get environment of the running thread, or defval if not defined */ + public static LuaValue getRunningEnv(LuaValue defval) { + return running_thread.env!=null? running_thread.env: defval; + } + + public static final void onCall(LuaFunction function) { + running_thread.callstack[running_thread.calls++] = function; + if (DebugLib.DEBUG_ENABLED) + DebugLib.debugOnCall(running_thread, running_thread.calls, function); + } + + public static final void onReturn() { + running_thread.callstack[--running_thread.calls] = null; + if (DebugLib.DEBUG_ENABLED) + DebugLib.debugOnReturn(running_thread, running_thread.calls); + } + + public static int getCallstackDepth() { + return running_thread.calls; + } + + public static final LuaFunction getCallstackFunction(int level) { + return level>=0 || level=1 && i<=v.length? v[i - 1]: r.arg(i-v.length); + } + public int narg() { + return v.length+r.narg(); + } + public LuaValue arg1() { return v.length>0? v[0]: r.arg1(); } + } + + // varargs from array part + static final class ArrayPartVarargs extends Varargs { + private final int offset; + private final LuaValue[] v; + private final int length; + private final Varargs more; + ArrayPartVarargs(LuaValue[] v, int offset, int length) { + this.v = v; + this.offset = offset; + this.length = length; + this.more = NONE; + } + public ArrayPartVarargs(LuaValue[] v, int offset, int length, Varargs more) { + this.v = v; + this.offset = offset; + this.length = length; + this.more = more; + } + public LuaValue arg(int i) { + return i>=1&&i<=length? v[i+offset-1]: more.arg(i-length); + } + public int narg() { + return length + more.narg(); + } + public LuaValue arg1() { + return length>0? v[offset]: more.arg1(); + } + } + + // varargs from two values + static final class PairVarargs extends Varargs { + private final LuaValue v1; + private final Varargs v2; + PairVarargs(LuaValue v1, Varargs v2) { + this.v1 = v1; + this.v2 = v2; + } + public LuaValue arg(int i) { + return i==1? v1: v2.arg(i-1); + } + public int narg() { + return 1+v2.narg(); + } + public LuaValue arg1() { + return v1; + } + public String toString() { + return "{"+v1+","+v2+"}"; + } + } + + +} diff --git a/src/core/org/luaj/vm2/Print.java b/src/core/org/luaj/vm2/Print.java new file mode 100644 index 00000000..12762483 --- /dev/null +++ b/src/core/org/luaj/vm2/Print.java @@ -0,0 +1,377 @@ +/******************************************************************************* +* Copyright (c) 2009 Luaj.org. All rights reserved. +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +* THE SOFTWARE. +******************************************************************************/ +package org.luaj.vm2; + +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; + +public class Print extends Lua { + + /** opcode names */ + private static final String STRING_FOR_NULL = "null"; + public static PrintStream ps = System.out; + + private static final String[] luaP_opnames = { + "MOVE", + "LOADK", + "LOADBOOL", + "LOADNIL", + "GETUPVAL", + "GETGLOBAL", + "GETTABLE", + "SETGLOBAL", + "SETUPVAL", + "SETTABLE", + "NEWTABLE", + "SELF", + "ADD", + "SUB", + "MUL", + "DIV", + "MOD", + "POW", + "UNM", + "NOT", + "LEN", + "CONCAT", + "JMP", + "EQ", + "LT", + "LE", + "TEST", + "TESTSET", + "CALL", + "TAILCALL", + "RETURN", + "FORLOOP", + "FORPREP", + "TFORLOOP", + "SETLIST", + "CLOSE", + "CLOSURE", + "VARARG", + null, + }; + + + static void printString(PrintStream ps, final LuaString s) { + + ps.print('"'); + for (int i = 0, n = s.m_length; i < n; i++) { + int c = s.m_bytes[s.m_offset+i]; + if ( c >= ' ' && c <= '~' && c != '\"' && c != '\\' ) + ps.print((char) c); + else { + switch (c) { + case '"': + ps.print("\\\""); + break; + case '\\': + ps.print("\\\\"); + break; + case 0x0007: /* bell */ + ps.print("\\a"); + break; + case '\b': /* backspace */ + ps.print("\\b"); + break; + case '\f': /* form feed */ + ps.print("\\f"); + break; + case '\t': /* tab */ + ps.print("\\t"); + break; + case '\r': /* carriage return */ + ps.print("\\r"); + break; + case '\n': /* newline */ + ps.print("\\n"); + break; + case 0x000B: /* vertical tab */ + ps.print("\\v"); + break; + default: + ps.print('\\'); + ps.print(Integer.toString(1000 + 0xff&c).substring(1)); + break; + } + } + } + ps.print('"'); + } + + static void printValue( PrintStream ps, LuaValue v ) { + switch ( v.type() ) { + case LuaValue.TSTRING: printString( ps, (LuaString) v ); break; + default: ps.print( v.toString() ); + + } + } + + static void printConstant(PrintStream ps, Prototype f, int i) { + printValue( ps, f.k[i] ); + } + + public static void printCode(Prototype f) { + int[] code = f.code; + int pc, n = code.length; + for (pc = 0; pc < n; pc++) { + printOpCode(f, pc); + ps.println(); + } + } + + public static void printOpCode(Prototype f, int pc) { + printOpCode(ps,f,pc); + } + + public static void printOpCode(PrintStream ps, Prototype f, int pc) { + int[] code = f.code; + int i = code[pc]; + int o = GET_OPCODE(i); + int a = GETARG_A(i); + int b = GETARG_B(i); + int c = GETARG_C(i); + int bx = GETARG_Bx(i); + int sbx = GETARG_sBx(i); + int line = getline(f, pc); + ps.print(" " + (pc + 1) + " "); + if (line > 0) + ps.print("[" + line + "] "); + else + ps.print("[-] "); + ps.print(luaP_opnames[o] + " "); + switch (getOpMode(o)) { + case iABC: + ps.print( a ); + if (getBMode(o) != OpArgN) + ps.print(" "+(ISK(b) ? (-1 - INDEXK(b)) : b)); + if (getCMode(o) != OpArgN) + ps.print(" "+(ISK(c) ? (-1 - INDEXK(c)) : c)); + break; + case iABx: + if (getBMode(o) == OpArgK) { + ps.print(a + " " + (-1 - bx)); + } else { + ps.print(a + " " + (bx)); + } + break; + case iAsBx: + if (o == OP_JMP) + ps.print( sbx ); + else + ps.print(a + " " + sbx); + break; + } + switch (o) { + case OP_LOADK: + ps.print(" ; "); + printConstant(ps, f, bx); + break; + case OP_GETUPVAL: + case OP_SETUPVAL: + ps.print(" ; "); + if ( f.upvalues.length > b ) + printValue(ps, f.upvalues[b]); + else + ps.print( "-" ); + break; + case OP_GETGLOBAL: + case OP_SETGLOBAL: + ps.print(" ; "); + printConstant( ps, f, bx ); + break; + case OP_GETTABLE: + case OP_SELF: + if (ISK(c)) { + ps.print(" ; "); + printConstant(ps, f, INDEXK(c)); + } + break; + case OP_SETTABLE: + case OP_ADD: + case OP_SUB: + case OP_MUL: + case OP_DIV: + case OP_POW: + case OP_EQ: + case OP_LT: + case OP_LE: + if (ISK(b) || ISK(c)) { + ps.print(" ; "); + if (ISK(b)) + printConstant(ps, f, INDEXK(b)); + else + ps.print("-"); + ps.print(" "); + if (ISK(c)) + printConstant(ps, f, INDEXK(c)); + else + ps.print("-"); + } + break; + case OP_JMP: + case OP_FORLOOP: + case OP_FORPREP: + ps.print(" ; to " + (sbx + pc + 2)); + break; + case OP_CLOSURE: + ps.print(" ; " + f.p[bx].getClass().getName()); + break; + case OP_SETLIST: + if (c == 0) + ps.print(" ; " + ((int) code[++pc])); + else + ps.print(" ; " + ((int) c)); + break; + case OP_VARARG: + ps.print( " ; is_vararg="+ f.is_vararg ); + break; + default: + break; + } + } + + private static int getline(Prototype f, int pc) { + return pc>0 && f.lineinfo!=null && pc (" + f.code.length + " instructions, " + + f.code.length * 4 + " bytes at " + id(f) + ")\n"); + ps.print(f.numparams + " param, " + f.maxstacksize + " slot, " + + f.upvalues.length + " upvalue, "); + ps.print(f.locvars.length + " local, " + f.k.length + + " constant, " + f.p.length + " function\n"); + } + + static void printConstants(Prototype f) { + int i, n = f.k.length; + ps.print("constants (" + n + ") for " + id(f) + ":\n"); + for (i = 0; i < n; i++) { + ps.print(" " + (i + 1) + " "); + printValue( ps, f.k[i] ); + ps.print( "\n"); + } + } + + static void printLocals(Prototype f) { + int i, n = f.locvars.length; + ps.print("locals (" + n + ") for " + id(f) + ":\n"); + for (i = 0; i < n; i++) { + ps.println(" "+i+" "+f.locvars[i].varname+" "+(f.locvars[i].startpc+1)+" "+(f.locvars[i].endpc+1)); + } + } + + static void printUpValues(Prototype f) { + int i, n = f.upvalues.length; + ps.print("upvalues (" + n + ") for " + id(f) + ":\n"); + for (i = 0; i < n; i++) { + ps.print(" " + i + " " + f.upvalues[i] + "\n"); + } + } + + public void printFunction(Prototype f, boolean full) { + int i, n = f.p.length; + printHeader(f); + printCode(f); + if (full) { + printConstants(f); + printLocals(f); + printUpValues(f); + } + for (i = 0; i < n; i++) + printFunction(f.p[i], full); + } + + private static void format( String s, int maxcols ) { + int n = s.length(); + if ( n > maxcols ) + ps.print( s.substring(0,maxcols) ); + else { + ps.print( s ); + for ( int i=maxcols-n; --i>=0; ) + ps.print( ' ' ); + } + } + + private static String id(Prototype f) { + return "Proto"; + } + + public static void printState(LuaClosure cl, int pc, LuaValue[] stack, int top, Varargs varargs) { + // print opcode into buffer + PrintStream previous = ps; + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + ps = new PrintStream( baos ); + printOpCode( cl.p, pc ); + ps.flush(); + ps.close(); + ps = previous; + format( baos.toString(), 50 ); + + // print stack + ps.print('['); + for ( int i=0; i=limit) ) { + array = new LuaValue[] { array[index] }; + index = 0; + return (closed = true); + } else { + return false; + } + } + + public boolean isClosed() { + return closed; + } +} diff --git a/src/core/org/luaj/vm2/Varargs.java b/src/core/org/luaj/vm2/Varargs.java new file mode 100644 index 00000000..7e981855 --- /dev/null +++ b/src/core/org/luaj/vm2/Varargs.java @@ -0,0 +1,135 @@ +/******************************************************************************* +* Copyright (c) 2009 Luaj.org. All rights reserved. +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +* THE SOFTWARE. +******************************************************************************/ +package org.luaj.vm2; + +/** + * Class to encapsulate varargs values. + */ +public abstract class Varargs { + + /** + * Get the n-th argument value (1-based) + * + * @param i 1 for the first argument, 2 for the second, etc. + * @return Value at position i, or Value.NIL if there is none. + */ + abstract public LuaValue arg( int i ); + + /** + * Get the number of arguments, or 0 if there are none. + * @return number of arguments. + */ + abstract public int narg(); + + /** + * Get the first argument + * @return Value + */ + abstract public LuaValue arg1(); + + // ----------------------------------------------------------------------- + // utilities to get specific arguments and type-check them. + // ----------------------------------------------------------------------- + + // argument types + public int type(int i) { return arg(i).type(); } + public boolean isnil(int i) { return arg(i).isnil(); } + public boolean isfunction(int i) { return arg(i).isfunction(); } + public boolean isnumber(int i) { return arg(i).isnumber(); } + public boolean isstring(int i) { return arg(i).isstring(); } + public boolean istable(int i) { return arg(i).istable(); } + public boolean isthread(int i) { return arg(i).isthread(); } + public boolean isuserdata(int i) { return arg(i).isuserdata(); } + public boolean isvalue(int i) { return i>0 && i<=narg(); } + + // optional argument types + public LuaFunction optfunction(int i, LuaFunction defval) { return arg(i).optfunction(defval); } + public int optint(int i, int defval) { return arg(i).optint(defval); } + public long optlong(int i, long defval) { return arg(i).optlong(defval); } + public LuaNumber optnumber(int i, LuaNumber defval) { return arg(i).optnumber(defval); } + public String optString(int i, String defval) { return arg(i).optString(defval); } + public LuaString optstring(int i, LuaString defval) { return arg(i).optstring(defval); } + public LuaValue optvalue(int i, LuaValue defval) { return i>0 && i<=narg()? arg(i): defval; } + + // required argument types + public boolean checkboolean(int i) { return arg(i).checkboolean(); } + public double checkdouble(int i) { return arg(i).checknumber().todouble(); } + public LuaValue checkfunction(int i) { return arg(i).checkfunction(); } + public int checkint(int i) { return arg(i).checknumber().toint(); } + public long checklong(int i) { return arg(i).checknumber().tolong(); } + public LuaValue checknumber(int i) { return arg(i).checknumber(); } + public String checkString(int i) { return arg(i).checkString(); } + public LuaString checkstring(int i) { return arg(i).checkstring(); } + public LuaTable checktable(int i) { return arg(i).checktable(); } + public LuaThread checkthread(int i) { return arg(i).checkthread(); } + public Object checkuserdata(int i,Class c) { return arg(i).checkuserdata(c); } + public LuaValue checkvalue(int i) { return i<=narg()? arg(i): LuaValue.error("value expected"); } + public LuaValue checknotnil(int i) { return arg(i).checknotnil(); } + + public void argcheck(boolean test, int i, String msg) { if (!test) LuaValue.argerror(i,msg); } + + public boolean isnoneornil(int i) { + return i>narg() || arg(i).isnil(); + } + + public String toString() { + Buffer sb = new Buffer(); + sb.append( "(" ); + for ( int i=1,n=narg(); i<=n; i++ ) { + if (i>1) sb.append( "," ); + sb.append( arg(i).toString() ); + } + sb.append( ")" ); + return sb.toString(); + } + + public Varargs subargs(final int start) { + int end = narg(); + switch ( end-start ) { + case 0: return arg(start); + case 1: return new LuaValue.PairVarargs(arg(start),arg(end)); + } + return end=start && i<=end? v.arg(i): LuaValue.NIL; + } + public LuaValue arg1() { + return v.arg(start); + } + public int narg() { + return end+1-start; + } + } + +} diff --git a/src/core/org/luaj/vm2/WeakTable.java b/src/core/org/luaj/vm2/WeakTable.java new file mode 100644 index 00000000..3b320650 --- /dev/null +++ b/src/core/org/luaj/vm2/WeakTable.java @@ -0,0 +1,179 @@ +/******************************************************************************* + * Copyright (c) 2009 Luaj.org. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + ******************************************************************************/ +package org.luaj.vm2; + +import java.lang.ref.WeakReference; + +public class WeakTable extends LuaTable { + + private final boolean weakKeys,weakValues; + + WeakTable( boolean weakKeys, boolean weakValues ) { + this.weakKeys = weakKeys; + this.weakValues = weakValues; + } + + private static class WeakValue extends LuaValue { + private final WeakReference ref; + public WeakValue(LuaValue val) { + ref = new WeakReference(val); + } + public int type() { + return LuaValue.TNIL; + } + public String typename() { + return "value"; + } + public LuaValue strongvalue() { + Object o = ref.get(); + return o!=null? (LuaValue)o: NIL; + } + } + + private static class WeakUserdata extends LuaValue { + private final WeakReference ref; + private LuaValue metatable; + public WeakUserdata(Object val, LuaValue metatable) { + ref = new WeakReference(val); + } + public int type() { + return LuaValue.TNIL; + } + public String typename() { + return "value"; + } + public LuaValue strongvalue() { + Object o = ref.get(); + if ( o == null ) { + metatable = null; + return NIL; + } + return userdataOf( o, metatable ); + } + } + + private static class WeakEntry extends LuaValue { + private LuaValue key; + private LuaValue val; + private WeakEntry(LuaValue key, LuaValue val) { + this.key = key; + this.val = val; + } + public int type() { + return LuaValue.TNIL; + } + public String typename() { + return "weakentry"; + } + public LuaValue strongkey() { + LuaValue k = key.strongvalue(); + LuaValue v = val.strongvalue(); + if ( k.isnil() || v.isnil() ) + return key = val = NIL; + return k; + } + public LuaValue strongvalue() { + LuaValue k = key.strongvalue(); + LuaValue v = val.strongvalue(); + if ( k.isnil() || v.isnil() ) + return key = val = NIL; + return v; + } + public boolean eq_b(LuaValue rhs) { + return strongkey().eq_b(rhs); + } + public int hashCode() { + return strongkey().hashCode(); + } + } + + + private boolean shouldWeaken( LuaValue value ) { + switch ( value.type() ) { + case LuaValue.TFUNCTION: + case LuaValue.TTHREAD: + case LuaValue.TTABLE: + case LuaValue.TUSERDATA: + return true; + } + return false; + } + + private LuaValue toWeak( LuaValue value ) { + switch ( value.type() ) { + case LuaValue.TFUNCTION: + case LuaValue.TTHREAD: + case LuaValue.TTABLE: return new WeakValue( value ); + case LuaValue.TUSERDATA: return new WeakUserdata( value.checkuserdata(), value.getmetatable() ); + default: return value; + } + } + + public LuaValue rawget(int key) { + LuaValue v = super.rawget(key); + if ( v.isnil() ) + return NIL; + v = v.strongvalue(); + if ( v.isnil() ) { + // TODO: mark table for culling? + rawset(key, NIL); + } + return v; + } + + public LuaValue rawget(LuaValue key) { + LuaValue v = super.rawget(key); + if ( v.isnil() ) + return NIL; + v = v.strongvalue(); + if ( v.isnil() ) { + // TODO: mark table for culling? + rawset(key, NIL); + } + return v; + } + + public void rawset(int key, LuaValue val) { + if ( val.isnil() || !weakValues || !shouldWeaken(val) ) { + super.rawset(key, val); + } else { + super.rawset(key, toWeak(val)); + } + } + + public void rawset(LuaValue key, LuaValue val) { + if ( val.isnil() ) { + super.rawset(key, val); + } else { + boolean weakenKey = weakKeys && shouldWeaken(key); + boolean weakenVal = weakValues && shouldWeaken(val); + if ( weakenKey ) { + WeakEntry e = new WeakEntry( toWeak(key), weakenVal? toWeak(val): val); + super.rawset(e, e); + } else if ( weakenVal ) { + super.rawset(key, toWeak(val)); + } else { + super.rawset(key, val); + } + } + } +} diff --git a/src/core/org/luaj/vm2/compiler/DumpState.java b/src/core/org/luaj/vm2/compiler/DumpState.java new file mode 100644 index 00000000..f02a944c --- /dev/null +++ b/src/core/org/luaj/vm2/compiler/DumpState.java @@ -0,0 +1,267 @@ +/******************************************************************************* +* Copyright (c) 2009 Luaj.org. All rights reserved. +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +* THE SOFTWARE. +******************************************************************************/ +package org.luaj.vm2.compiler; + +import java.io.DataOutputStream; +import java.io.IOException; +import java.io.OutputStream; + +import org.luaj.vm2.LocVars; +import org.luaj.vm2.Prototype; +import org.luaj.vm2.LuaString; +import org.luaj.vm2.LuaValue; + + +public class DumpState { + + /** mark for precompiled code (`Lua') */ + public static final String LUA_SIGNATURE = "\033Lua"; + + /** for header of binary files -- this is Lua 5.1 */ + public static final int LUAC_VERSION = 0x51; + + /** for header of binary files -- this is the official format */ + public static final int LUAC_FORMAT = 0; + + /** size of header of binary files */ + public static final int LUAC_HEADERSIZE = 12; + + /** expected lua header bytes */ + private static final byte[] LUAC_HEADER_SIGNATURE = { '\033', 'L', 'u', 'a' }; + + /** set true to allow integer compilation */ + public static boolean ALLOW_INTEGER_CASTING = false; + + /** format corresponding to non-number-patched lua, all numbers are floats or doubles */ + public static final int NUMBER_FORMAT_FLOATS_OR_DOUBLES = 0; + + /** format corresponding to non-number-patched lua, all numbers are ints */ + public static final int NUMBER_FORMAT_INTS_ONLY = 1; + + /** format corresponding to number-patched lua, all numbers are 32-bit (4 byte) ints */ + public static final int NUMBER_FORMAT_NUM_PATCH_INT32 = 4; + + /** default number format */ + public static final int NUMBER_FORMAT_DEFAULT = NUMBER_FORMAT_FLOATS_OR_DOUBLES; + + // header fields + private boolean IS_LITTLE_ENDIAN = false; + private int NUMBER_FORMAT = NUMBER_FORMAT_DEFAULT; + private int SIZEOF_LUA_NUMBER = 8; + private static final int SIZEOF_INT = 4; + private static final int SIZEOF_SIZET = 4; + private static final int SIZEOF_INSTRUCTION = 4; + + DataOutputStream writer; + boolean strip; + int status; + + public DumpState(OutputStream w, boolean strip) { + this.writer = new DataOutputStream( w ); + this.strip = strip; + this.status = 0; + } + + void dumpBlock(final byte[] b, int size) throws IOException { + writer.write(b, 0, size); + } + + void dumpChar(int b) throws IOException { + writer.write( b ); + } + + void dumpInt(int x) throws IOException { + if ( IS_LITTLE_ENDIAN ) { + writer.writeByte(x&0xff); + writer.writeByte((x>>8)&0xff); + writer.writeByte((x>>16)&0xff); + writer.writeByte((x>>24)&0xff); + } else { + writer.writeInt(x); + } + } + + void dumpString(LuaString s) throws IOException { + final int len = s.len().toint(); + dumpInt( len+1 ); + s.write( writer, 0, len ); + writer.write( 0 ); + } + + void dumpDouble(double d) throws IOException { + long l = Double.doubleToLongBits(d); + if ( IS_LITTLE_ENDIAN ) { + dumpInt( (int) l ); + dumpInt( (int) (l>>32) ); + } else { + writer.writeLong(l); + } + } + + void dumpCode( final Prototype f ) throws IOException { + final int[] code = f.code; + int n = code.length; + dumpInt( n ); + for ( int i=0; i l ) + errorlimit( l, msg ); + } + + void errorlimit (int limit, String what) { + String msg = (f.linedefined == 0) ? + L.pushfstring("main function has more than "+limit+" "+what) : + L.pushfstring("function at line "+f.linedefined+" has more than "+limit+" "+what); + ls.lexerror(msg, 0); + } + + + int indexupvalue(LuaString name, expdesc v) { + int i; + for (i = 0; i < f.nups; i++) { + if (upvalues[i].k == v.k && upvalues[i].info == v.u.s.info) { + _assert(f.upvalues[i] == name); + return i; + } + } + /* new one */ + checklimit(f.nups + 1, LUAI_MAXUPVALUES, "upvalues"); + if ( f.upvalues == null || f.nups + 1 > f.upvalues.length) + f.upvalues = realloc( f.upvalues, f.nups*2+1 ); + f.upvalues[f.nups] = name; + _assert (v.k == LexState.VLOCAL || v.k == LexState.VUPVAL); + upvalues[f.nups] = new upvaldesc(); + upvalues[f.nups].k = (byte) (v.k); + upvalues[f.nups].info = (byte) (v.u.s.info); + return f.nups++; + } + + int searchvar(LuaString n) { + int i; + for (i = nactvar - 1; i >= 0; i--) { + if (n == getlocvar(i).varname) + return i; + } + return -1; /* not found */ + } + + void markupval(int level) { + BlockCnt bl = this.bl; + while (bl != null && bl.nactvar > level) + bl = bl.previous; + if (bl != null) + bl.upval = true; + } + + int singlevaraux(LuaString n, expdesc var, int base) { + int v = searchvar(n); /* look up at current level */ + if (v >= 0) { + var.init(LexState.VLOCAL, v); + if (base == 0) + markupval(v); /* local will be used as an upval */ + return LexState.VLOCAL; + } else { /* not found at current level; try upper one */ + if (prev == null) { /* no more levels? */ + /* default is global variable */ + var.init(LexState.VGLOBAL, NO_REG); + return LexState.VGLOBAL; + } + if (prev.singlevaraux(n, var, 0) == LexState.VGLOBAL) + return LexState.VGLOBAL; + var.u.s.info = indexupvalue(n, var); /* else was LOCAL or UPVAL */ + var.k = LexState.VUPVAL; /* upvalue in this level */ + return LexState.VUPVAL; + } + } + + void enterblock (BlockCnt bl, boolean isbreakable) { + bl.breaklist.i = LexState.NO_JUMP; + bl.isbreakable = isbreakable; + bl.nactvar = this.nactvar; + bl.upval = false; + bl.previous = this.bl; + this.bl = bl; + _assert(this.freereg == this.nactvar); + } + + // +// void leaveblock (FuncState *fs) { +// BlockCnt *bl = this.bl; +// this.bl = bl.previous; +// removevars(this.ls, bl.nactvar); +// if (bl.upval) +// this.codeABC(OP_CLOSE, bl.nactvar, 0, 0); +// /* a block either controls scope or breaks (never both) */ +// assert(!bl.isbreakable || !bl.upval); +// assert(bl.nactvar == this.nactvar); +// this.freereg = this.nactvar; /* free registers */ +// this.patchtohere(bl.breaklist); +// } + + void leaveblock() { + BlockCnt bl = this.bl; + this.bl = bl.previous; + ls.removevars(bl.nactvar); + if (bl.upval) + this.codeABC(OP_CLOSE, bl.nactvar, 0, 0); + /* a block either controls scope or breaks (never both) */ + _assert (!bl.isbreakable || !bl.upval); + _assert (bl.nactvar == this.nactvar); + this.freereg = this.nactvar; /* free registers */ + this.patchtohere(bl.breaklist.i); + } + + void closelistfield(ConsControl cc) { + if (cc.v.k == LexState.VVOID) + return; /* there is no list item */ + this.exp2nextreg(cc.v); + cc.v.k = LexState.VVOID; + if (cc.tostore == LFIELDS_PER_FLUSH) { + this.setlist(cc.t.u.s.info, cc.na, cc.tostore); /* flush */ + cc.tostore = 0; /* no more items pending */ + } + } + + boolean hasmultret(int k) { + return ((k) == LexState.VCALL || (k) == LexState.VVARARG); + } + + void lastlistfield (ConsControl cc) { + if (cc.tostore == 0) return; + if (hasmultret(cc.v.k)) { + this.setmultret(cc.v); + this.setlist(cc.t.u.s.info, cc.na, LUA_MULTRET); + cc.na--; /** do not count last expression (unknown number of elements) */ + } + else { + if (cc.v.k != LexState.VVOID) + this.exp2nextreg(cc.v); + this.setlist(cc.t.u.s.info, cc.na, cc.tostore); + } + } + + + + // ============================================================= + // from lcode.c + // ============================================================= + + void nil(int from, int n) { + InstructionPtr previous; + if (this.pc > this.lasttarget) { /* no jumps to current position? */ + if (this.pc == 0) { /* function start? */ + if (from >= this.nactvar) + return; /* positions are already clean */ + } else { + previous = new InstructionPtr(this.f.code, this.pc - 1); + if (GET_OPCODE(previous.get()) == OP_LOADNIL) { + int pfrom = GETARG_A(previous.get()); + int pto = GETARG_B(previous.get()); + if (pfrom <= from && from <= pto + 1) { /* can connect both? */ + if (from + n - 1 > pto) + SETARG_B(previous, from + n - 1); + return; + } + } + } + } + /* else no optimization */ + this.codeABC(OP_LOADNIL, from, from + n - 1, 0); + } + + + int jump() { + int jpc = this.jpc.i; /* save list of jumps to here */ + this.jpc.i = LexState.NO_JUMP; + IntPtr j = new IntPtr(this.codeAsBx(OP_JMP, 0, LexState.NO_JUMP)); + this.concat(j, jpc); /* keep them on hold */ + return j.i; + } + + void ret(int first, int nret) { + this.codeABC(OP_RETURN, first, nret + 1, 0); + } + + int condjump(int /* OpCode */op, int A, int B, int C) { + this.codeABC(op, A, B, C); + return this.jump(); + } + + void fixjump(int pc, int dest) { + InstructionPtr jmp = new InstructionPtr(this.f.code, pc); + int offset = dest - (pc + 1); + _assert (dest != LexState.NO_JUMP); + if (Math.abs(offset) > MAXARG_sBx) + ls.syntaxerror("control structure too long"); + SETARG_sBx(jmp, offset); + } + + + /* + * * returns current `pc' and marks it as a jump target (to avoid wrong * + * optimizations with consecutive instructions not in the same basic block). + */ + int getlabel() { + this.lasttarget = this.pc; + return this.pc; + } + + + int getjump(int pc) { + int offset = GETARG_sBx(this.f.code[pc]); + /* point to itself represents end of list */ + if (offset == LexState.NO_JUMP) + /* end of list */ + return LexState.NO_JUMP; + else + /* turn offset into absolute position */ + return (pc + 1) + offset; + } + + + InstructionPtr getjumpcontrol(int pc) { + InstructionPtr pi = new InstructionPtr(this.f.code, pc); + if (pc >= 1 && testTMode(GET_OPCODE(pi.code[pi.idx - 1]))) + return new InstructionPtr(pi.code, pi.idx - 1); + else + return pi; + } + + + /* + * * check whether list has any jump that do not produce a value * (or + * produce an inverted value) + */ + boolean need_value(int list) { + for (; list != LexState.NO_JUMP; list = this.getjump(list)) { + int i = this.getjumpcontrol(list).get(); + if (GET_OPCODE(i) != OP_TESTSET) + return true; + } + return false; /* not found */ + } + + + boolean patchtestreg(int node, int reg) { + InstructionPtr i = this.getjumpcontrol(node); + if (GET_OPCODE(i.get()) != OP_TESTSET) + /* cannot patch other instructions */ + return false; + if (reg != NO_REG && reg != GETARG_B(i.get())) + SETARG_A(i, reg); + else + /* no register to put value or register already has the value */ + i.set(CREATE_ABC(OP_TEST, GETARG_B(i.get()), 0, Lua.GETARG_C(i.get()))); + + return true; + } + + + void removevalues(int list) { + for (; list != LexState.NO_JUMP; list = this.getjump(list)) + this.patchtestreg(list, NO_REG); + } + + void patchlistaux(int list, int vtarget, int reg, int dtarget) { + while (list != LexState.NO_JUMP) { + int next = this.getjump(list); + if (this.patchtestreg(list, reg)) + this.fixjump(list, vtarget); + else + this.fixjump(list, dtarget); /* jump to default target */ + list = next; + } + } + + void dischargejpc() { + this.patchlistaux(this.jpc.i, this.pc, NO_REG, this.pc); + this.jpc.i = LexState.NO_JUMP; + } + + void patchlist(int list, int target) { + if (target == this.pc) + this.patchtohere(list); + else { + _assert (target < this.pc); + this.patchlistaux(list, target, NO_REG, target); + } + } + + void patchtohere(int list) { + this.getlabel(); + this.concat(this.jpc, list); + } + + void concat(IntPtr l1, int l2) { + if (l2 == LexState.NO_JUMP) + return; + if (l1.i == LexState.NO_JUMP) + l1.i = l2; + else { + int list = l1.i; + int next; + while ((next = this.getjump(list)) != LexState.NO_JUMP) + /* find last element */ + list = next; + this.fixjump(list, l2); + } + } + + void checkstack(int n) { + int newstack = this.freereg + n; + if (newstack > this.f.maxstacksize) { + if (newstack >= MAXSTACK) + ls.syntaxerror("function or expression too complex"); + this.f.maxstacksize = newstack; + } + } + + void reserveregs(int n) { + this.checkstack(n); + this.freereg += n; + } + + void freereg(int reg) { + if (!ISK(reg) && reg >= this.nactvar) { + this.freereg--; + _assert (reg == this.freereg); + } + } + + void freeexp(expdesc e) { + if (e.k == LexState.VNONRELOC) + this.freereg(e.u.s.info); + } + + int addk(LuaValue v) { + int idx; + if (this.htable.containsKey(v)) { + idx = ((Integer) htable.get(v)).intValue(); + } else { + idx = this.nk; + this.htable.put(v, new Integer(idx)); + final Prototype f = this.f; + if (f.k == null || nk + 1 >= f.k.length) + f.k = realloc( f.k, nk*2 + 1 ); + f.k[this.nk++] = v; + } + return idx; + } + + int stringK(LuaString s) { + return this.addk(s); + } + + int numberK(LuaValue r) { + if ( r instanceof LuaDouble ) { + double d = r.todouble(); + int i = (int) d; + if ( d == (double) i ) + r = LuaInteger.valueOf(i); + } + return this.addk(r); + } + + int boolK(boolean b) { + return this.addk((b ? LuaValue.TRUE : LuaBoolean.FALSE)); + } + + int nilK() { + return this.addk(LuaNil.NIL); + } + + void setreturns(expdesc e, int nresults) { + if (e.k == LexState.VCALL) { /* expression is an open function call? */ + SETARG_C(this.getcodePtr(e), nresults + 1); + } else if (e.k == LexState.VVARARG) { + SETARG_B(this.getcodePtr(e), nresults + 1); + SETARG_A(this.getcodePtr(e), this.freereg); + this.reserveregs(1); + } + } + + void setoneret(expdesc e) { + if (e.k == LexState.VCALL) { /* expression is an open function call? */ + e.k = LexState.VNONRELOC; + e.u.s.info = GETARG_A(this.getcode(e)); + } else if (e.k == LexState.VVARARG) { + SETARG_B(this.getcodePtr(e), 2); + e.k = LexState.VRELOCABLE; /* can relocate its simple result */ + } + } + + void dischargevars(expdesc e) { + switch (e.k) { + case LexState.VLOCAL: { + e.k = LexState.VNONRELOC; + break; + } + case LexState.VUPVAL: { + e.u.s.info = this.codeABC(OP_GETUPVAL, 0, e.u.s.info, 0); + e.k = LexState.VRELOCABLE; + break; + } + case LexState.VGLOBAL: { + e.u.s.info = this.codeABx(OP_GETGLOBAL, 0, e.u.s.info); + e.k = LexState.VRELOCABLE; + break; + } + case LexState.VINDEXED: { + this.freereg(e.u.s.aux); + this.freereg(e.u.s.info); + e.u.s.info = this + .codeABC(OP_GETTABLE, 0, e.u.s.info, e.u.s.aux); + e.k = LexState.VRELOCABLE; + break; + } + case LexState.VVARARG: + case LexState.VCALL: { + this.setoneret(e); + break; + } + default: + break; /* there is one value available (somewhere) */ + } + } + + int code_label(int A, int b, int jump) { + this.getlabel(); /* those instructions may be jump targets */ + return this.codeABC(OP_LOADBOOL, A, b, jump); + } + + void discharge2reg(expdesc e, int reg) { + this.dischargevars(e); + switch (e.k) { + case LexState.VNIL: { + this.nil(reg, 1); + break; + } + case LexState.VFALSE: + case LexState.VTRUE: { + this.codeABC(OP_LOADBOOL, reg, (e.k == LexState.VTRUE ? 1 : 0), + 0); + break; + } + case LexState.VK: { + this.codeABx(OP_LOADK, reg, e.u.s.info); + break; + } + case LexState.VKNUM: { + this.codeABx(OP_LOADK, reg, this.numberK(e.u.nval())); + break; + } + case LexState.VRELOCABLE: { + InstructionPtr pc = this.getcodePtr(e); + SETARG_A(pc, reg); + break; + } + case LexState.VNONRELOC: { + if (reg != e.u.s.info) + this.codeABC(OP_MOVE, reg, e.u.s.info, 0); + break; + } + default: { + _assert (e.k == LexState.VVOID || e.k == LexState.VJMP); + return; /* nothing to do... */ + } + } + e.u.s.info = reg; + e.k = LexState.VNONRELOC; + } + + void discharge2anyreg(expdesc e) { + if (e.k != LexState.VNONRELOC) { + this.reserveregs(1); + this.discharge2reg(e, this.freereg - 1); + } + } + + void exp2reg(expdesc e, int reg) { + this.discharge2reg(e, reg); + if (e.k == LexState.VJMP) + this.concat(e.t, e.u.s.info); /* put this jump in `t' list */ + if (e.hasjumps()) { + int _final; /* position after whole expression */ + int p_f = LexState.NO_JUMP; /* position of an eventual LOAD false */ + int p_t = LexState.NO_JUMP; /* position of an eventual LOAD true */ + if (this.need_value(e.t.i) || this.need_value(e.f.i)) { + int fj = (e.k == LexState.VJMP) ? LexState.NO_JUMP : this + .jump(); + p_f = this.code_label(reg, 0, 1); + p_t = this.code_label(reg, 1, 0); + this.patchtohere(fj); + } + _final = this.getlabel(); + this.patchlistaux(e.f.i, _final, reg, p_f); + this.patchlistaux(e.t.i, _final, reg, p_t); + } + e.f.i = e.t.i = LexState.NO_JUMP; + e.u.s.info = reg; + e.k = LexState.VNONRELOC; + } + + void exp2nextreg(expdesc e) { + this.dischargevars(e); + this.freeexp(e); + this.reserveregs(1); + this.exp2reg(e, this.freereg - 1); + } + + int exp2anyreg(expdesc e) { + this.dischargevars(e); + if (e.k == LexState.VNONRELOC) { + if (!e.hasjumps()) + return e.u.s.info; /* exp is already in a register */ + if (e.u.s.info >= this.nactvar) { /* reg. is not a local? */ + this.exp2reg(e, e.u.s.info); /* put value on it */ + return e.u.s.info; + } + } + this.exp2nextreg(e); /* default */ + return e.u.s.info; + } + + void exp2val(expdesc e) { + if (e.hasjumps()) + this.exp2anyreg(e); + else + this.dischargevars(e); + } + + int exp2RK(expdesc e) { + this.exp2val(e); + switch (e.k) { + case LexState.VKNUM: + case LexState.VTRUE: + case LexState.VFALSE: + case LexState.VNIL: { + if (this.nk <= MAXINDEXRK) { /* constant fit in RK operand? */ + e.u.s.info = (e.k == LexState.VNIL) ? this.nilK() + : (e.k == LexState.VKNUM) ? this.numberK(e.u.nval()) + : this.boolK((e.k == LexState.VTRUE)); + e.k = LexState.VK; + return RKASK(e.u.s.info); + } else + break; + } + case LexState.VK: { + if (e.u.s.info <= MAXINDEXRK) /* constant fit in argC? */ + return RKASK(e.u.s.info); + else + break; + } + default: + break; + } + /* not a constant in the right range: put it in a register */ + return this.exp2anyreg(e); + } + + void storevar(expdesc var, expdesc ex) { + switch (var.k) { + case LexState.VLOCAL: { + this.freeexp(ex); + this.exp2reg(ex, var.u.s.info); + return; + } + case LexState.VUPVAL: { + int e = this.exp2anyreg(ex); + this.codeABC(OP_SETUPVAL, e, var.u.s.info, 0); + break; + } + case LexState.VGLOBAL: { + int e = this.exp2anyreg(ex); + this.codeABx(OP_SETGLOBAL, e, var.u.s.info); + break; + } + case LexState.VINDEXED: { + int e = this.exp2RK(ex); + this.codeABC(OP_SETTABLE, var.u.s.info, var.u.s.aux, e); + break; + } + default: { + _assert (false); /* invalid var kind to store */ + break; + } + } + this.freeexp(ex); + } + + void self(expdesc e, expdesc key) { + int func; + this.exp2anyreg(e); + this.freeexp(e); + func = this.freereg; + this.reserveregs(2); + this.codeABC(OP_SELF, func, e.u.s.info, this.exp2RK(key)); + this.freeexp(key); + e.u.s.info = func; + e.k = LexState.VNONRELOC; + } + + void invertjump(expdesc e) { + InstructionPtr pc = this.getjumpcontrol(e.u.s.info); + _assert (testTMode(GET_OPCODE(pc.get())) + && GET_OPCODE(pc.get()) != OP_TESTSET && Lua + .GET_OPCODE(pc.get()) != OP_TEST); + // SETARG_A(pc, !(GETARG_A(pc.get()))); + int a = GETARG_A(pc.get()); + int nota = (a!=0? 0: 1); + SETARG_A(pc, nota); + } + + int jumponcond(expdesc e, int cond) { + if (e.k == LexState.VRELOCABLE) { + int ie = this.getcode(e); + if (GET_OPCODE(ie) == OP_NOT) { + this.pc--; /* remove previous OP_NOT */ + return this.condjump(OP_TEST, GETARG_B(ie), 0, (cond!=0? 0: 1)); + } + /* else go through */ + } + this.discharge2anyreg(e); + this.freeexp(e); + return this.condjump(OP_TESTSET, NO_REG, e.u.s.info, cond); + } + + void goiftrue(expdesc e) { + int pc; /* pc of last jump */ + this.dischargevars(e); + switch (e.k) { + case LexState.VK: + case LexState.VKNUM: + case LexState.VTRUE: { + pc = LexState.NO_JUMP; /* always true; do nothing */ + break; + } + case LexState.VFALSE: { + pc = this.jump(); /* always jump */ + break; + } + case LexState.VJMP: { + this.invertjump(e); + pc = e.u.s.info; + break; + } + default: { + pc = this.jumponcond(e, 0); + break; + } + } + this.concat(e.f, pc); /* insert last jump in `f' list */ + this.patchtohere(e.t.i); + e.t.i = LexState.NO_JUMP; + } + + void goiffalse(expdesc e) { + int pc; /* pc of last jump */ + this.dischargevars(e); + switch (e.k) { + case LexState.VNIL: + case LexState.VFALSE: { + pc = LexState.NO_JUMP; /* always false; do nothing */ + break; + } + case LexState.VTRUE: { + pc = this.jump(); /* always jump */ + break; + } + case LexState.VJMP: { + pc = e.u.s.info; + break; + } + default: { + pc = this.jumponcond(e, 1); + break; + } + } + this.concat(e.t, pc); /* insert last jump in `t' list */ + this.patchtohere(e.f.i); + e.f.i = LexState.NO_JUMP; + } + + void codenot(expdesc e) { + this.dischargevars(e); + switch (e.k) { + case LexState.VNIL: + case LexState.VFALSE: { + e.k = LexState.VTRUE; + break; + } + case LexState.VK: + case LexState.VKNUM: + case LexState.VTRUE: { + e.k = LexState.VFALSE; + break; + } + case LexState.VJMP: { + this.invertjump(e); + break; + } + case LexState.VRELOCABLE: + case LexState.VNONRELOC: { + this.discharge2anyreg(e); + this.freeexp(e); + e.u.s.info = this.codeABC(OP_NOT, 0, e.u.s.info, 0); + e.k = LexState.VRELOCABLE; + break; + } + default: { + _assert (false); /* cannot happen */ + break; + } + } + /* interchange true and false lists */ + { + int temp = e.f.i; + e.f.i = e.t.i; + e.t.i = temp; + } + this.removevalues(e.f.i); + this.removevalues(e.t.i); + } + + void indexed(expdesc t, expdesc k) { + t.u.s.aux = this.exp2RK(k); + t.k = LexState.VINDEXED; + } + + boolean constfolding(int op, expdesc e1, expdesc e2) { + LuaValue v1, v2, r; + if (!e1.isnumeral() || !e2.isnumeral()) + return false; + v1 = e1.u.nval(); + v2 = e2.u.nval(); + switch (op) { + case OP_ADD: + r = v1.add(v2); + break; + case OP_SUB: + r = v1.sub(v2); + break; + case OP_MUL: + r = v1.mul(v2); + break; + case OP_DIV: + r = v1.div(v2); + break; + case OP_MOD: + r = v1.mod(v2); + break; + case OP_POW: + r = v1.pow(v2); + break; + case OP_UNM: + r = v1.neg(); + break; + case OP_LEN: + // r = v1.len(); + // break; + return false; /* no constant folding for 'len' */ + default: + _assert (false); + r = null; + break; + } + if (Double.NaN == r.todouble()) + return false; /* do not attempt to produce NaN */ + e1.u.setNval( r ); + return true; + } + + void codearith(int op, expdesc e1, expdesc e2) { + if (constfolding(op, e1, e2)) + return; + else { + int o2 = (op != OP_UNM && op != OP_LEN) ? this.exp2RK(e2) + : 0; + int o1 = this.exp2RK(e1); + if (o1 > o2) { + this.freeexp(e1); + this.freeexp(e2); + } else { + this.freeexp(e2); + this.freeexp(e1); + } + e1.u.s.info = this.codeABC(op, 0, o1, o2); + e1.k = LexState.VRELOCABLE; + } + } + + void codecomp(int /* OpCode */op, int cond, expdesc e1, expdesc e2) { + int o1 = this.exp2RK(e1); + int o2 = this.exp2RK(e2); + this.freeexp(e2); + this.freeexp(e1); + if (cond == 0 && op != OP_EQ) { + int temp; /* exchange args to replace by `<' or `<=' */ + temp = o1; + o1 = o2; + o2 = temp; /* o1 <==> o2 */ + cond = 1; + } + e1.u.s.info = this.condjump(op, cond, o1, o2); + e1.k = LexState.VJMP; + } + + void prefix(int /* UnOpr */op, expdesc e) { + expdesc e2 = new expdesc(); + e2.init(LexState.VKNUM, 0); + switch (op) { + case LexState.OPR_MINUS: { + if (e.k == LexState.VK) + this.exp2anyreg(e); /* cannot operate on non-numeric constants */ + this.codearith(OP_UNM, e, e2); + break; + } + case LexState.OPR_NOT: + this.codenot(e); + break; + case LexState.OPR_LEN: { + this.exp2anyreg(e); /* cannot operate on constants */ + this.codearith(OP_LEN, e, e2); + break; + } + default: + _assert (false); + } + } + + void infix(int /* BinOpr */op, expdesc v) { + switch (op) { + case LexState.OPR_AND: { + this.goiftrue(v); + break; + } + case LexState.OPR_OR: { + this.goiffalse(v); + break; + } + case LexState.OPR_CONCAT: { + this.exp2nextreg(v); /* operand must be on the `stack' */ + break; + } + case LexState.OPR_ADD: + case LexState.OPR_SUB: + case LexState.OPR_MUL: + case LexState.OPR_DIV: + case LexState.OPR_MOD: + case LexState.OPR_POW: { + if (!v.isnumeral()) + this.exp2RK(v); + break; + } + default: { + this.exp2RK(v); + break; + } + } + } + + + void posfix(int op, expdesc e1, expdesc e2) { + switch (op) { + case LexState.OPR_AND: { + _assert (e1.t.i == LexState.NO_JUMP); /* list must be closed */ + this.dischargevars(e2); + this.concat(e2.f, e1.f.i); + // *e1 = *e2; + e1.setvalue(e2); + break; + } + case LexState.OPR_OR: { + _assert (e1.f.i == LexState.NO_JUMP); /* list must be closed */ + this.dischargevars(e2); + this.concat(e2.t, e1.t.i); + // *e1 = *e2; + e1.setvalue(e2); + break; + } + case LexState.OPR_CONCAT: { + this.exp2val(e2); + if (e2.k == LexState.VRELOCABLE + && GET_OPCODE(this.getcode(e2)) == OP_CONCAT) { + _assert (e1.u.s.info == GETARG_B(this.getcode(e2)) - 1); + this.freeexp(e1); + SETARG_B(this.getcodePtr(e2), e1.u.s.info); + e1.k = LexState.VRELOCABLE; + e1.u.s.info = e2.u.s.info; + } else { + this.exp2nextreg(e2); /* operand must be on the 'stack' */ + this.codearith(OP_CONCAT, e1, e2); + } + break; + } + case LexState.OPR_ADD: + this.codearith(OP_ADD, e1, e2); + break; + case LexState.OPR_SUB: + this.codearith(OP_SUB, e1, e2); + break; + case LexState.OPR_MUL: + this.codearith(OP_MUL, e1, e2); + break; + case LexState.OPR_DIV: + this.codearith(OP_DIV, e1, e2); + break; + case LexState.OPR_MOD: + this.codearith(OP_MOD, e1, e2); + break; + case LexState.OPR_POW: + this.codearith(OP_POW, e1, e2); + break; + case LexState.OPR_EQ: + this.codecomp(OP_EQ, 1, e1, e2); + break; + case LexState.OPR_NE: + this.codecomp(OP_EQ, 0, e1, e2); + break; + case LexState.OPR_LT: + this.codecomp(OP_LT, 1, e1, e2); + break; + case LexState.OPR_LE: + this.codecomp(OP_LE, 1, e1, e2); + break; + case LexState.OPR_GT: + this.codecomp(OP_LT, 0, e1, e2); + break; + case LexState.OPR_GE: + this.codecomp(OP_LE, 0, e1, e2); + break; + default: + _assert (false); + } + } + + + void fixline(int line) { + this.f.lineinfo[this.pc - 1] = line; + } + + + int code(int instruction, int line) { + Prototype f = this.f; + this.dischargejpc(); /* `pc' will change */ + /* put new instruction in code array */ + if (f.code == null || this.pc + 1 > f.code.length) + f.code = LuaC.realloc(f.code, this.pc * 2 + 1); + f.code[this.pc] = instruction; + /* save corresponding line information */ + if (f.lineinfo == null || this.pc + 1 > f.lineinfo.length) + f.lineinfo = LuaC.realloc(f.lineinfo, + this.pc * 2 + 1); + f.lineinfo[this.pc] = line; + return this.pc++; + } + + + int codeABC(int o, int a, int b, int c) { + _assert (getOpMode(o) == iABC); + _assert (getBMode(o) != OpArgN || b == 0); + _assert (getCMode(o) != OpArgN || c == 0); + return this.code(CREATE_ABC(o, a, b, c), this.ls.lastline); + } + + + int codeABx(int o, int a, int bc) { + _assert (getOpMode(o) == iABx || getOpMode(o) == iAsBx); + _assert (getCMode(o) == OpArgN); + return this.code(CREATE_ABx(o, a, bc), this.ls.lastline); + } + + + void setlist(int base, int nelems, int tostore) { + int c = (nelems - 1) / LFIELDS_PER_FLUSH + 1; + int b = (tostore == LUA_MULTRET) ? 0 : tostore; + _assert (tostore != 0); + if (c <= MAXARG_C) + this.codeABC(OP_SETLIST, base, b, c); + else { + this.codeABC(OP_SETLIST, base, b, 0); + this.code(c, this.ls.lastline); + } + this.freereg = base + 1; /* free registers with list values */ + } + +} diff --git a/src/core/org/luaj/vm2/compiler/InstructionPtr.java b/src/core/org/luaj/vm2/compiler/InstructionPtr.java new file mode 100644 index 00000000..fbdf061a --- /dev/null +++ b/src/core/org/luaj/vm2/compiler/InstructionPtr.java @@ -0,0 +1,37 @@ +/******************************************************************************* +* Copyright (c) 2009 Luaj.org. All rights reserved. +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +* THE SOFTWARE. +******************************************************************************/ +package org.luaj.vm2.compiler; + +class InstructionPtr { + final int[] code; + final int idx; + InstructionPtr(int[] code, int idx ) { + this.code = code; + this.idx = idx; + } + int get() { + return code[idx]; + } + void set(int value) { + code[idx] = value; + } +} \ No newline at end of file diff --git a/src/core/org/luaj/vm2/compiler/IntPtr.java b/src/core/org/luaj/vm2/compiler/IntPtr.java new file mode 100644 index 00000000..0f42cb0e --- /dev/null +++ b/src/core/org/luaj/vm2/compiler/IntPtr.java @@ -0,0 +1,31 @@ +/******************************************************************************* +* Copyright (c) 2009 Luaj.org. All rights reserved. +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +* THE SOFTWARE. +******************************************************************************/ +package org.luaj.vm2.compiler; + +public class IntPtr { + int i; + IntPtr() { + } + IntPtr(int value) { + this.i = value; + } +} diff --git a/src/core/org/luaj/vm2/compiler/LexState.java b/src/core/org/luaj/vm2/compiler/LexState.java new file mode 100644 index 00000000..f09a1737 --- /dev/null +++ b/src/core/org/luaj/vm2/compiler/LexState.java @@ -0,0 +1,1905 @@ +/******************************************************************************* +* Copyright (c) 2009 Luaj.org. All rights reserved. +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +* THE SOFTWARE. +******************************************************************************/ +package org.luaj.vm2.compiler; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Hashtable; + +import org.luaj.vm2.LuaInteger; +import org.luaj.vm2.LocVars; +import org.luaj.vm2.Lua; +import org.luaj.vm2.LuaError; +import org.luaj.vm2.Prototype; +import org.luaj.vm2.LuaString; +import org.luaj.vm2.LuaValue; +import org.luaj.vm2.compiler.FuncState.BlockCnt; + + +public class LexState { + + protected static final String RESERVED_LOCAL_VAR_FOR_CONTROL = "(for control)"; + protected static final String RESERVED_LOCAL_VAR_FOR_STATE = "(for state)"; + protected static final String RESERVED_LOCAL_VAR_FOR_GENERATOR = "(for generator)"; + protected static final String RESERVED_LOCAL_VAR_FOR_STEP = "(for step)"; + protected static final String RESERVED_LOCAL_VAR_FOR_LIMIT = "(for limit)"; + protected static final String RESERVED_LOCAL_VAR_FOR_INDEX = "(for index)"; + + // keywords array + protected static final String[] RESERVED_LOCAL_VAR_KEYWORDS = new String[] { + RESERVED_LOCAL_VAR_FOR_CONTROL, + RESERVED_LOCAL_VAR_FOR_GENERATOR, + RESERVED_LOCAL_VAR_FOR_INDEX, + RESERVED_LOCAL_VAR_FOR_LIMIT, + RESERVED_LOCAL_VAR_FOR_STATE, + RESERVED_LOCAL_VAR_FOR_STEP + }; + private static final Hashtable RESERVED_LOCAL_VAR_KEYWORDS_TABLE = new Hashtable(); + static { + for ( int i=0; i=", "<=", "~=", + "", "", "", "", + }; + + final static int + /* terminal symbols denoted by reserved words */ + TK_AND=257, TK_BREAK=258, TK_DO=259, TK_ELSE=260, TK_ELSEIF=261, + TK_END=262, TK_FALSE=263, TK_FOR=264, TK_FUNCTION=265, TK_IF=266, + TK_IN=267, TK_LOCAL=268, TK_NIL=269, TK_NOT=270, TK_OR=271, TK_REPEAT=272, + TK_RETURN=273, TK_THEN=274, TK_TRUE=275, TK_UNTIL=276, TK_WHILE=277, + /* other terminal symbols */ + TK_CONCAT=278, TK_DOTS=279, TK_EQ=280, TK_GE=281, TK_LE=282, TK_NE=283, + TK_NUMBER=284, TK_NAME=285, TK_STRING=286, TK_EOS=287; + + final static int FIRST_RESERVED = TK_AND; + final static int NUM_RESERVED = TK_WHILE+1-FIRST_RESERVED; + + final static Hashtable RESERVED = new Hashtable(); + static { + for ( int i=0; i= '0' && c <= '9') + || (c >= 'a' && c <= 'z') + || (c >= 'A' && c <= 'Z') + || (c == '_'); + // return Character.isLetterOrDigit(c); + } + + private boolean isalpha(int c) { + return (c >= 'a' && c <= 'z') + || (c >= 'A' && c <= 'Z'); + } + + private boolean isdigit(int c) { + return (c >= '0' && c <= '9'); + } + + private boolean isspace(int c) { + return (c <= ' '); + } + + + public LexState(LuaC state, InputStream stream) { + this.z = stream; + this.buff = new byte[32]; + this.L = state; + } + + void nextChar() { + try { + current = z.read(); + } catch ( IOException e ) { + e.printStackTrace(); + current = EOZ; + } + } + + boolean currIsNewline() { + return current == '\n' || current == '\r'; + } + + void save_and_next() { + save( current ); + nextChar(); + } + + void save(int c) { + if ( buff == null || nbuff + 1 > buff.length ) + buff = LuaC.realloc( buff, nbuff*2+1 ); + buff[nbuff++] = (byte) c; + } + + + String token2str( int token ) { + if ( token < FIRST_RESERVED ) { + return iscntrl(token)? + L.pushfstring( "char("+((int)token)+")" ): + L.pushfstring( String.valueOf( (char) token ) ); + } else { + return luaX_tokens[token-FIRST_RESERVED]; + } + } + + private static boolean iscntrl(int token) { + return token < ' '; + } + + String txtToken(int token) { + switch ( token ) { + case TK_NAME: + case TK_STRING: + case TK_NUMBER: + return new String( buff, 0, nbuff ); + default: + return token2str( token ); + } + } + + void lexerror( String msg, int token ) { + String cid = chunkid( source.toString() ); // TODO: get source name from source + L.pushfstring( cid+":"+linenumber+": "+msg ); + if ( token != 0 ) + L.pushfstring( "syntax error: "+msg+" near "+txtToken(token) ); + throw new LuaError(cid+":"+linenumber+": "+msg); + } + + String chunkid( String source ) { + if ( source.startsWith("=") ) + return source.substring(1); + String end = ""; + if ( source.startsWith("@") ) { + source = source.substring(1); + } else { + source = "[string \""+source; + end = "\"]"; + } + int n = source.length() + end.length(); + if ( n > MAXSRC ) + source = source.substring(0,MAXSRC-end.length()-3) + "..."; + return source + end; + } + + void syntaxerror( String msg ) { + lexerror( msg, t.token ); + } + + // only called by new_localvarliteral() for var names. + LuaString newstring( String s ) { + byte[] b = s.getBytes(); + return L.newTString(b, 0, b.length); + } + + LuaString newstring( byte[] bytes, int offset, int len ) { + return L.newTString( bytes, offset, len ); + } + + void inclinenumber() { + int old = current; + LuaC._assert( currIsNewline() ); + nextChar(); /* skip '\n' or '\r' */ + if ( currIsNewline() && current != old ) + nextChar(); /* skip '\n\r' or '\r\n' */ + if ( ++linenumber >= MAX_INT ) + syntaxerror("chunk has too many lines"); + } + + void setinput( LuaC L, int firstByte, InputStream z, LuaString source ) { + this.decpoint = '.'; + this.L = L; + this.lookahead.token = TK_EOS; /* no look-ahead token */ + this.z = z; + this.fs = null; + this.linenumber = 1; + this.lastline = 1; + this.source = source; + this.nbuff = 0; /* initialize buffer */ + this.current = firstByte; /* read first char */ + this.skipShebang(); + } + + private void skipShebang() { + if ( current == '#' ) + while (!currIsNewline() && current != EOZ) + nextChar(); + } + + + + /* + ** ======================================================= + ** LEXICAL ANALYZER + ** ======================================================= + */ + + + boolean check_next(String set) { + if (set.indexOf(current) < 0) + return false; + save_and_next(); + return true; + } + + void buffreplace(byte from, byte to) { + int n = nbuff; + byte[] p = buff; + while ((--n) >= 0) + if (p[n] == from) + p[n] = to; + } + + boolean str2d(String str, SemInfo seminfo) { + double d; + str = str.trim(); // TODO: get rid of this + if ( str.startsWith("0x") ) { + d = Long.parseLong(str.substring(2), 16); + } + else + d = Double.parseDouble(str); + seminfo.r = LuaValue.valueOf(d); + return true; + } + + // + // TODO: reexamine this source and see if it should be ported differently + // + // static void trydecpoint (LexState *ls, SemInfo *seminfo) { + // /* format error: try to update decimal point separator */ + // struct lconv *cv = localeconv(); + // char old = this.decpoint; + // this.decpoint = (cv ? cv->decimal_point[0] : '.'); + // buffreplace(ls, old, this.decpoint); /* try updated decimal separator */ + // if (!luaO_str2d(luaZ_buffer(this.buff), &seminfo->r)) { + // /* format error with correct decimal point: no more options */ + // buffreplace(ls, this.decpoint, '.'); /* undo change (for error message) */ + // luaX_lexerror(ls, "malformed number", TK_NUMBER); + // } + // } + // + /* + void trydecpoint(String str, SemInfo seminfo) { + NumberFormat nf = NumberFormat.getInstance(); + try { + Number n = nf.parse(str); + double d = n.doubleValue(); + seminfo.r = new LDouble(d); + } catch (ParseException e) { + lexerror("malformed number", TK_NUMBER); + } + } + */ + + void read_numeral(SemInfo seminfo) { + LuaC._assert (isdigit(current)); + do { + save_and_next(); + } while (isdigit(current) || current == '.'); + if (check_next("Ee")) /* `E'? */ + check_next("+-"); /* optional exponent sign */ + while (isalnum(current) || current == '_') + save_and_next(); + save('\0'); + buffreplace((byte)'.', decpoint); /* follow locale for decimal point */ + String str = new String(buff, 0, nbuff); +// if (!str2d(str, seminfo)) /* format error? */ +// trydecpoint(str, seminfo); /* try to update decimal point separator */ + str2d(str, seminfo); + } + + int skip_sep() { + int count = 0; + int s = current; + LuaC._assert (s == '[' || s == ']'); + save_and_next(); + while (current == '=') { + save_and_next(); + count++; + } + return (current == s) ? count : (-count) - 1; + } + + void read_long_string(SemInfo seminfo, int sep) { + int cont = 0; + save_and_next(); /* skip 2nd `[' */ + if (currIsNewline()) /* string starts with a newline? */ + inclinenumber(); /* skip it */ + for (boolean endloop = false; !endloop;) { + switch (current) { + case EOZ: + lexerror((seminfo != null) ? "unfinished long string" + : "unfinished long comment", TK_EOS); + break; /* to avoid warnings */ + case '[': { + if (skip_sep() == sep) { + save_and_next(); /* skip 2nd `[' */ + cont++; + if (LUA_COMPAT_LSTR == 1) { + if (sep == 0) + lexerror("nesting of [[...]] is deprecated", '['); + } + } + break; + } + case ']': { + if (skip_sep() == sep) { + save_and_next(); /* skip 2nd `]' */ + if (LUA_COMPAT_LSTR == 2) { + cont--; + if (sep == 0 && cont >= 0) + break; + } + endloop = true; + } + break; + } + case '\n': + case '\r': { + save('\n'); + inclinenumber(); + if (seminfo == null) + nbuff = 0; /* avoid wasting space */ + break; + } + default: { + if (seminfo != null) + save_and_next(); + else + nextChar(); + } + } + } + if (seminfo != null) + seminfo.ts = newstring(buff, 2 + sep, nbuff - 2 * (2 + sep)); + } + + void read_string(int del, SemInfo seminfo) { + save_and_next(); + while (current != del) { + switch (current) { + case EOZ: + lexerror("unfinished string", TK_EOS); + continue; /* to avoid warnings */ + case '\n': + case '\r': + lexerror("unfinished string", TK_STRING); + continue; /* to avoid warnings */ + case '\\': { + int c; + nextChar(); /* do not save the `\' */ + switch (current) { + case 'a': /* bell */ + c = '\u0007'; + break; + case 'b': /* backspace */ + c = '\b'; + break; + case 'f': /* form feed */ + c = '\f'; + break; + case 'n': /* newline */ + c = '\n'; + break; + case 'r': /* carriage return */ + c = '\r'; + break; + case 't': /* tab */ + c = '\t'; + break; + case 'v': /* vertical tab */ + c = '\u000B'; + break; + case '\n': /* go through */ + case '\r': + save('\n'); + inclinenumber(); + continue; + case EOZ: + continue; /* will raise an error next loop */ + default: { + if (!isdigit(current)) + save_and_next(); /* handles \\, \", \', and \? */ + else { /* \xxx */ + int i = 0; + c = 0; + do { + c = 10 * c + (current - '0'); + nextChar(); + } while (++i < 3 && isdigit(current)); + if (c > UCHAR_MAX) + lexerror("escape sequence too large", TK_STRING); + save(c); + } + continue; + } + } + save(c); + nextChar(); + continue; + } + default: + save_and_next(); + } + } + save_and_next(); /* skip delimiter */ + seminfo.ts = newstring(buff, 1, nbuff - 2); + } + + int llex(SemInfo seminfo) { + nbuff = 0; + while (true) { + switch (current) { + case '\n': + case '\r': { + inclinenumber(); + continue; + } + case '-': { + nextChar(); + if (current != '-') + return '-'; + /* else is a comment */ + nextChar(); + if (current == '[') { + int sep = skip_sep(); + nbuff = 0; /* `skip_sep' may dirty the buffer */ + if (sep >= 0) { + read_long_string(null, sep); /* long comment */ + nbuff = 0; + continue; + } + } + /* else short comment */ + while (!currIsNewline() && current != EOZ) + nextChar(); + continue; + } + case '[': { + int sep = skip_sep(); + if (sep >= 0) { + read_long_string(seminfo, sep); + return TK_STRING; + } else if (sep == -1) + return '['; + else + lexerror("invalid long string delimiter", TK_STRING); + } + case '=': { + nextChar(); + if (current != '=') + return '='; + else { + nextChar(); + return TK_EQ; + } + } + case '<': { + nextChar(); + if (current != '=') + return '<'; + else { + nextChar(); + return TK_LE; + } + } + case '>': { + nextChar(); + if (current != '=') + return '>'; + else { + nextChar(); + return TK_GE; + } + } + case '~': { + nextChar(); + if (current != '=') + return '~'; + else { + nextChar(); + return TK_NE; + } + } + case '"': + case '\'': { + read_string(current, seminfo); + return TK_STRING; + } + case '.': { + save_and_next(); + if (check_next(".")) { + if (check_next(".")) + return TK_DOTS; /* ... */ + else + return TK_CONCAT; /* .. */ + } else if (!isdigit(current)) + return '.'; + else { + read_numeral(seminfo); + return TK_NUMBER; + } + } + case EOZ: { + return TK_EOS; + } + default: { + if (isspace(current)) { + LuaC._assert (!currIsNewline()); + nextChar(); + continue; + } else if (isdigit(current)) { + read_numeral(seminfo); + return TK_NUMBER; + } else if (isalpha(current) || current == '_') { + /* identifier or reserved word */ + LuaString ts; + do { + save_and_next(); + } while (isalnum(current) || current == '_'); + ts = newstring(buff, 0, nbuff); + if ( RESERVED.containsKey(ts) ) + return ((Integer)RESERVED.get(ts)).intValue(); + else { + seminfo.ts = ts; + return TK_NAME; + } + } else { + int c = current; + nextChar(); + return c; /* single-char tokens (+ - / ...) */ + } + } + } + } + } + + void next() { + lastline = linenumber; + if (lookahead.token != TK_EOS) { /* is there a look-ahead token? */ + t.set( lookahead ); /* use this one */ + lookahead.token = TK_EOS; /* and discharge it */ + } else + t.token = llex(t.seminfo); /* read next token */ + } + + void lookahead() { + LuaC._assert (lookahead.token == TK_EOS); + lookahead.token = llex(lookahead.seminfo); + } + + // ============================================================= + // from lcode.h + // ============================================================= + + + // ============================================================= + // from lparser.c + // ============================================================= + + static class expdesc { + int k; // expkind, from enumerated list, above + static class U { // originally a union + static class S { + int info, aux; + } + final S s = new S(); + private LuaValue _nval; + public void setNval(LuaValue r) { + _nval = r; + } + public LuaValue nval() { + return (_nval == null? LuaInteger.valueOf(s.info): _nval); + } + }; + final U u = new U(); + final IntPtr t = new IntPtr(); /* patch list of `exit when true' */ + final IntPtr f = new IntPtr(); /* patch list of `exit when false' */ + void init( int k, int i ) { + this.f.i = NO_JUMP; + this.t.i = NO_JUMP; + this.k = k; + this.u.s.info = i; + } + + boolean hasjumps() { + return (t.i != f.i); + } + + boolean isnumeral() { + return (k == VKNUM && t.i == NO_JUMP && f.i == NO_JUMP); + } + + public void setvalue(expdesc other) { + this.k = other.k; + this.u._nval = other.u._nval; + this.u.s.info = other.u.s.info; + this.u.s.aux = other.u.s.aux; + this.t.i = other.t.i; + this.f.i = other.f.i; + } + } + + boolean hasmultret(int k) { + return ((k) == VCALL || (k) == VVARARG); + } + + /*---------------------------------------------------------------------- + name args description + ------------------------------------------------------------------------*/ + + /* + * * prototypes for recursive non-terminal functions + */ + + void error_expected(int token) { + syntaxerror(L.pushfstring(LUA_QS(token2str(token)) + " expected")); + } + + boolean testnext(int c) { + if (t.token == c) { + next(); + return true; + } else + return false; + } + + void check(int c) { + if (t.token != c) + error_expected(c); + } + + void checknext (int c) { + check(c); + next(); + } + + void check_condition(boolean c, String msg) { + if (!(c)) + syntaxerror(msg); + } + + + void check_match(int what, int who, int where) { + if (!testnext(what)) { + if (where == linenumber) + error_expected(what); + else { + syntaxerror(L.pushfstring(LUA_QS(token2str(what)) + + " expected " + "(to close " + LUA_QS(token2str(who)) + + " at line " + where + ")")); + } + } + } + + LuaString str_checkname() { + LuaString ts; + check(TK_NAME); + ts = t.seminfo.ts; + next(); + return ts; + } + + void codestring(expdesc e, LuaString s) { + e.init(VK, fs.stringK(s)); + } + + void checkname(expdesc e) { + codestring(e, str_checkname()); + } + + + int registerlocalvar(LuaString varname) { + FuncState fs = this.fs; + Prototype f = fs.f; + if (f.locvars == null || fs.nlocvars + 1 > f.locvars.length) + f.locvars = LuaC.realloc( f.locvars, fs.nlocvars*2+1 ); + f.locvars[fs.nlocvars] = new LocVars(varname,0,0); + return fs.nlocvars++; + } + + +// +// #define new_localvarliteral(ls,v,n) \ +// this.new_localvar(luaX_newstring(ls, "" v, (sizeof(v)/sizeof(char))-1), n) +// + void new_localvarliteral(String v, int n) { + LuaString ts = newstring(v); + new_localvar(ts, n); + } + + void new_localvar(LuaString name, int n) { + FuncState fs = this.fs; + fs.checklimit(fs.nactvar + n + 1, FuncState.LUAI_MAXVARS, "local variables"); + fs.actvar[fs.nactvar + n] = (short) registerlocalvar(name); + } + + void adjustlocalvars(int nvars) { + FuncState fs = this.fs; + fs.nactvar = (short) (fs.nactvar + nvars); + for (; nvars > 0; nvars--) { + fs.getlocvar(fs.nactvar - nvars).startpc = fs.pc; + } + } + + void removevars(int tolevel) { + FuncState fs = this.fs; + while (fs.nactvar > tolevel) + fs.getlocvar(--fs.nactvar).endpc = fs.pc; + } + + void singlevar(expdesc var) { + LuaString varname = this.str_checkname(); + FuncState fs = this.fs; + if (fs.singlevaraux(varname, var, 1) == VGLOBAL) + var.u.s.info = fs.stringK(varname); /* info points to global name */ + } + + void adjust_assign(int nvars, int nexps, expdesc e) { + FuncState fs = this.fs; + int extra = nvars - nexps; + if (hasmultret(e.k)) { + /* includes call itself */ + extra++; + if (extra < 0) + extra = 0; + /* last exp. provides the difference */ + fs.setreturns(e, extra); + if (extra > 1) + fs.reserveregs(extra - 1); + } else { + /* close last expression */ + if (e.k != VVOID) + fs.exp2nextreg(e); + if (extra > 0) { + int reg = fs.freereg; + fs.reserveregs(extra); + fs.nil(reg, extra); + } + } + } + + void enterlevel() { + if (++L.nCcalls > LUAI_MAXCCALLS) + lexerror("chunk has too many syntax levels", 0); + } + + void leavelevel() { + L.nCcalls--; + } + + void pushclosure(FuncState func, expdesc v) { + FuncState fs = this.fs; + Prototype f = fs.f; + if (f.p == null || fs.np + 1 > f.p.length) + f.p = LuaC.realloc( f.p, fs.np*2 + 1 ); + f.p[fs.np++] = func.f; + v.init(VRELOCABLE, fs.codeABx(Lua.OP_CLOSURE, 0, fs.np - 1)); + for (int i = 0; i < func.f.nups; i++) { + int o = (func.upvalues[i].k == VLOCAL) ? Lua.OP_MOVE + : Lua.OP_GETUPVAL; + fs.codeABC(o, 0, func.upvalues[i].info, 0); + } + } + + void open_func (FuncState fs) { + LuaC L = this.L; + Prototype f = new Prototype(); + if ( this.fs!=null ) + f.source = this.fs.f.source; + fs.f = f; + fs.prev = this.fs; /* linked list of funcstates */ + fs.ls = this; + fs.L = L; + this.fs = fs; + fs.pc = 0; + fs.lasttarget = -1; + fs.jpc = new IntPtr( NO_JUMP ); + fs.freereg = 0; + fs.nk = 0; + fs.np = 0; + fs.nlocvars = 0; + fs.nactvar = 0; + fs.bl = null; + f.maxstacksize = 2; /* registers 0/1 are always valid */ + //fs.h = new LTable(); + fs.htable = new Hashtable(); + } + + void close_func() { + FuncState fs = this.fs; + Prototype f = fs.f; + this.removevars(0); + fs.ret(0, 0); /* final return */ + f.code = LuaC.realloc(f.code, fs.pc); + f.lineinfo = LuaC.realloc(f.lineinfo, fs.pc); + // f.sizelineinfo = fs.pc; + f.k = LuaC.realloc(f.k, fs.nk); + f.p = LuaC.realloc(f.p, fs.np); + f.locvars = LuaC.realloc(f.locvars, fs.nlocvars); + // f.sizelocvars = fs.nlocvars; + f.upvalues = LuaC.realloc(f.upvalues, f.nups); + // LuaC._assert (CheckCode.checkcode(f)); + LuaC._assert (fs.bl == null); + this.fs = fs.prev; +// L.top -= 2; /* remove table and prototype from the stack */ + // /* last token read was anchored in defunct function; must reanchor it + // */ + // if (fs!=null) ls.anchor_token(); + } + + /*============================================================*/ + /* GRAMMAR RULES */ + /*============================================================*/ + + void field(expdesc v) { + /* field -> ['.' | ':'] NAME */ + FuncState fs = this.fs; + expdesc key = new expdesc(); + fs.exp2anyreg(v); + this.next(); /* skip the dot or colon */ + this.checkname(key); + fs.indexed(v, key); + } + + void yindex(expdesc v) { + /* index -> '[' expr ']' */ + this.next(); /* skip the '[' */ + this.expr(v); + this.fs.exp2val(v); + this.checknext(']'); + } + + + /* + ** {====================================================================== + ** Rules for Constructors + ** ======================================================================= + */ + + + static class ConsControl { + expdesc v = new expdesc(); /* last list item read */ + expdesc t; /* table descriptor */ + int nh; /* total number of `record' elements */ + int na; /* total number of array elements */ + int tostore; /* number of array elements pending to be stored */ + }; + + + void recfield(ConsControl cc) { + /* recfield -> (NAME | `['exp1`]') = exp1 */ + FuncState fs = this.fs; + int reg = this.fs.freereg; + expdesc key = new expdesc(); + expdesc val = new expdesc(); + int rkkey; + if (this.t.token == TK_NAME) { + fs.checklimit(cc.nh, MAX_INT, "items in a constructor"); + this.checkname(key); + } else + /* this.t.token == '[' */ + this.yindex(key); + cc.nh++; + this.checknext('='); + rkkey = fs.exp2RK(key); + this.expr(val); + fs.codeABC(Lua.OP_SETTABLE, cc.t.u.s.info, rkkey, fs.exp2RK(val)); + fs.freereg = reg; /* free registers */ + } + + void listfield (ConsControl cc) { + this.expr(cc.v); + fs.checklimit(cc.na, MAX_INT, "items in a constructor"); + cc.na++; + cc.tostore++; + } + + + void constructor(expdesc t) { + /* constructor -> ?? */ + FuncState fs = this.fs; + int line = this.linenumber; + int pc = fs.codeABC(Lua.OP_NEWTABLE, 0, 0, 0); + ConsControl cc = new ConsControl(); + cc.na = cc.nh = cc.tostore = 0; + cc.t = t; + t.init(VRELOCABLE, pc); + cc.v.init(VVOID, 0); /* no value (yet) */ + fs.exp2nextreg(t); /* fix it at stack top (for gc) */ + this.checknext('{'); + do { + LuaC._assert (cc.v.k == VVOID || cc.tostore > 0); + if (this.t.token == '}') + break; + fs.closelistfield(cc); + switch (this.t.token) { + case TK_NAME: { /* may be listfields or recfields */ + this.lookahead(); + if (this.lookahead.token != '=') /* expression? */ + this.listfield(cc); + else + this.recfield(cc); + break; + } + case '[': { /* constructor_item -> recfield */ + this.recfield(cc); + break; + } + default: { /* constructor_part -> listfield */ + this.listfield(cc); + break; + } + } + } while (this.testnext(',') || this.testnext(';')); + this.check_match('}', '{', line); + fs.lastlistfield(cc); + InstructionPtr i = new InstructionPtr(fs.f.code, pc); + LuaC.SETARG_B(i, luaO_int2fb(cc.na)); /* set initial array size */ + LuaC.SETARG_C(i, luaO_int2fb(cc.nh)); /* set initial table size */ + } + + /* + ** converts an integer to a "floating point byte", represented as + ** (eeeeexxx), where the real value is (1xxx) * 2^(eeeee - 1) if + ** eeeee != 0 and (xxx) otherwise. + */ + static int luaO_int2fb (int x) { + int e = 0; /* expoent */ + while (x >= 16) { + x = (x+1) >> 1; + e++; + } + if (x < 8) return x; + else return ((e+1) << 3) | (((int)x) - 8); + } + + + /* }====================================================================== */ + + void parlist () { + /* parlist -> [ param { `,' param } ] */ + FuncState fs = this.fs; + Prototype f = fs.f; + int nparams = 0; + f.is_vararg = 0; + if (this.t.token != ')') { /* is `parlist' not empty? */ + do { + switch (this.t.token) { + case TK_NAME: { /* param . NAME */ + this.new_localvar(this.str_checkname(), nparams++); + break; + } + case TK_DOTS: { /* param . `...' */ + this.next(); + if (LUA_COMPAT_VARARG) { + /* use `arg' as default name */ + this.new_localvarliteral("arg", nparams++); + f.is_vararg = LuaC.VARARG_HASARG | LuaC.VARARG_NEEDSARG; + } + f.is_vararg |= LuaC.VARARG_ISVARARG; + break; + } + default: this.syntaxerror(" or " + LUA_QL("...") + " expected"); + } + } while ((f.is_vararg==0) && this.testnext(',')); + } + this.adjustlocalvars(nparams); + f.numparams = (fs.nactvar - (f.is_vararg & LuaC.VARARG_HASARG)); + fs.reserveregs(fs.nactvar); /* reserve register for parameters */ + } + + + void body(expdesc e, boolean needself, int line) { + /* body -> `(' parlist `)' chunk END */ + FuncState new_fs = new FuncState(); + open_func(new_fs); + new_fs.f.linedefined = line; + this.checknext('('); + if (needself) { + new_localvarliteral("self", 0); + adjustlocalvars(1); + } + this.parlist(); + this.checknext(')'); + this.chunk(); + new_fs.f.lastlinedefined = this.linenumber; + this.check_match(TK_END, TK_FUNCTION, line); + this.close_func(); + this.pushclosure(new_fs, e); + } + + int explist1(expdesc v) { + /* explist1 -> expr { `,' expr } */ + int n = 1; /* at least one expression */ + this.expr(v); + while (this.testnext(',')) { + fs.exp2nextreg(v); + this.expr(v); + n++; + } + return n; + } + + + void funcargs(expdesc f) { + FuncState fs = this.fs; + expdesc args = new expdesc(); + int base, nparams; + int line = this.linenumber; + switch (this.t.token) { + case '(': { /* funcargs -> `(' [ explist1 ] `)' */ + if (line != this.lastline) + this.syntaxerror("ambiguous syntax (function call x new statement)"); + this.next(); + if (this.t.token == ')') /* arg list is empty? */ + args.k = VVOID; + else { + this.explist1(args); + fs.setmultret(args); + } + this.check_match(')', '(', line); + break; + } + case '{': { /* funcargs -> constructor */ + this.constructor(args); + break; + } + case TK_STRING: { /* funcargs -> STRING */ + this.codestring(args, this.t.seminfo.ts); + this.next(); /* must use `seminfo' before `next' */ + break; + } + default: { + this.syntaxerror("function arguments expected"); + return; + } + } + LuaC._assert (f.k == VNONRELOC); + base = f.u.s.info; /* base register for call */ + if (hasmultret(args.k)) + nparams = Lua.LUA_MULTRET; /* open call */ + else { + if (args.k != VVOID) + fs.exp2nextreg(args); /* close last argument */ + nparams = fs.freereg - (base + 1); + } + f.init(VCALL, fs.codeABC(Lua.OP_CALL, base, nparams + 1, 2)); + fs.fixline(line); + fs.freereg = base+1; /* call remove function and arguments and leaves + * (unless changed) one result */ + } + + + /* + ** {====================================================================== + ** Expression parsing + ** ======================================================================= + */ + + void prefixexp(expdesc v) { + /* prefixexp -> NAME | '(' expr ')' */ + switch (this.t.token) { + case '(': { + int line = this.linenumber; + this.next(); + this.expr(v); + this.check_match(')', '(', line); + fs.dischargevars(v); + return; + } + case TK_NAME: { + this.singlevar(v); + return; + } + default: { + this.syntaxerror("unexpected symbol"); + return; + } + } + } + + + void primaryexp(expdesc v) { + /* + * primaryexp -> prefixexp { `.' NAME | `[' exp `]' | `:' NAME funcargs | + * funcargs } + */ + FuncState fs = this.fs; + this.prefixexp(v); + for (;;) { + switch (this.t.token) { + case '.': { /* field */ + this.field(v); + break; + } + case '[': { /* `[' exp1 `]' */ + expdesc key = new expdesc(); + fs.exp2anyreg(v); + this.yindex(key); + fs.indexed(v, key); + break; + } + case ':': { /* `:' NAME funcargs */ + expdesc key = new expdesc(); + this.next(); + this.checkname(key); + fs.self(v, key); + this.funcargs(v); + break; + } + case '(': + case TK_STRING: + case '{': { /* funcargs */ + fs.exp2nextreg(v); + this.funcargs(v); + break; + } + default: + return; + } + } + } + + + void simpleexp(expdesc v) { + /* + * simpleexp -> NUMBER | STRING | NIL | true | false | ... | constructor | + * FUNCTION body | primaryexp + */ + switch (this.t.token) { + case TK_NUMBER: { + v.init(VKNUM, 0); + v.u.setNval(this.t.seminfo.r); + break; + } + case TK_STRING: { + this.codestring(v, this.t.seminfo.ts); + break; + } + case TK_NIL: { + v.init(VNIL, 0); + break; + } + case TK_TRUE: { + v.init(VTRUE, 0); + break; + } + case TK_FALSE: { + v.init(VFALSE, 0); + break; + } + case TK_DOTS: { /* vararg */ + FuncState fs = this.fs; + this.check_condition(fs.f.is_vararg!=0, "cannot use " + LUA_QL("...") + + " outside a vararg function"); + fs.f.is_vararg &= ~LuaC.VARARG_NEEDSARG; /* don't need 'arg' */ + v.init(VVARARG, fs.codeABC(Lua.OP_VARARG, 0, 1, 0)); + break; + } + case '{': { /* constructor */ + this.constructor(v); + return; + } + case TK_FUNCTION: { + this.next(); + this.body(v, false, this.linenumber); + return; + } + default: { + this.primaryexp(v); + return; + } + } + this.next(); + } + + + int getunopr(int op) { + switch (op) { + case TK_NOT: + return OPR_NOT; + case '-': + return OPR_MINUS; + case '#': + return OPR_LEN; + default: + return OPR_NOUNOPR; + } + } + + + int getbinopr(int op) { + switch (op) { + case '+': + return OPR_ADD; + case '-': + return OPR_SUB; + case '*': + return OPR_MUL; + case '/': + return OPR_DIV; + case '%': + return OPR_MOD; + case '^': + return OPR_POW; + case TK_CONCAT: + return OPR_CONCAT; + case TK_NE: + return OPR_NE; + case TK_EQ: + return OPR_EQ; + case '<': + return OPR_LT; + case TK_LE: + return OPR_LE; + case '>': + return OPR_GT; + case TK_GE: + return OPR_GE; + case TK_AND: + return OPR_AND; + case TK_OR: + return OPR_OR; + default: + return OPR_NOBINOPR; + } + } + + static class Priority { + final byte left; /* left priority for each binary operator */ + + final byte right; /* right priority */ + + public Priority(int i, int j) { + left = (byte) i; + right = (byte) j; + } + }; + + static Priority[] priority = { /* ORDER OPR */ + new Priority(6, 6), new Priority(6, 6), new Priority(7, 7), new Priority(7, 7), new Priority(7, 7), /* `+' `-' `/' `%' */ + new Priority(10, 9), new Priority(5, 4), /* power and concat (right associative) */ + new Priority(3, 3), new Priority(3, 3), /* equality and inequality */ + new Priority(3, 3), new Priority(3, 3), new Priority(3, 3), new Priority(3, 3), /* order */ + new Priority(2, 2), new Priority(1, 1) /* logical (and/or) */ + }; + + static final int UNARY_PRIORITY = 8; /* priority for unary operators */ + + + /* + ** subexpr -> (simpleexp | unop subexpr) { binop subexpr } + ** where `binop' is any binary operator with a priority higher than `limit' + */ + int subexpr(expdesc v, int limit) { + int op; + int uop; + this.enterlevel(); + uop = getunopr(this.t.token); + if (uop != OPR_NOUNOPR) { + this.next(); + this.subexpr(v, UNARY_PRIORITY); + fs.prefix(uop, v); + } else + this.simpleexp(v); + /* expand while operators have priorities higher than `limit' */ + op = getbinopr(this.t.token); + while (op != OPR_NOBINOPR && priority[op].left > limit) { + expdesc v2 = new expdesc(); + int nextop; + this.next(); + fs.infix(op, v); + /* read sub-expression with higher priority */ + nextop = this.subexpr(v2, priority[op].right); + fs.posfix(op, v, v2); + op = nextop; + } + this.leavelevel(); + return op; /* return first untreated operator */ + } + + void expr(expdesc v) { + this.subexpr(v, 0); + } + + /* }==================================================================== */ + + + + /* + ** {====================================================================== + ** Rules for Statements + ** ======================================================================= + */ + + + boolean block_follow (int token) { + switch (token) { + case TK_ELSE: case TK_ELSEIF: case TK_END: + case TK_UNTIL: case TK_EOS: + return true; + default: return false; + } + } + + + void block () { + /* block -> chunk */ + FuncState fs = this.fs; + BlockCnt bl = new BlockCnt(); + fs.enterblock(bl, false); + this.chunk(); + LuaC._assert(bl.breaklist.i == NO_JUMP); + fs.leaveblock(); + } + + + /* + ** structure to chain all variables in the left-hand side of an + ** assignment + */ + static class LHS_assign { + LHS_assign prev; + /* variable (global, local, upvalue, or indexed) */ + expdesc v = new expdesc(); + }; + + + /* + ** check whether, in an assignment to a local variable, the local variable + ** is needed in a previous assignment (to a table). If so, save original + ** local value in a safe place and use this safe copy in the previous + ** assignment. + */ + void check_conflict (LHS_assign lh, expdesc v) { + FuncState fs = this.fs; + int extra = fs.freereg; /* eventual position to save local variable */ + boolean conflict = false; + for (; lh!=null; lh = lh.prev) { + if (lh.v.k == VINDEXED) { + if (lh.v.u.s.info == v.u.s.info) { /* conflict? */ + conflict = true; + lh.v.u.s.info = extra; /* previous assignment will use safe copy */ + } + if (lh.v.u.s.aux == v.u.s.info) { /* conflict? */ + conflict = true; + lh.v.u.s.aux = extra; /* previous assignment will use safe copy */ + } + } + } + if (conflict) { + fs.codeABC(Lua.OP_MOVE, fs.freereg, v.u.s.info, 0); /* make copy */ + fs.reserveregs(1); + } + } + + + void assignment (LHS_assign lh, int nvars) { + expdesc e = new expdesc(); + this.check_condition(VLOCAL <= lh.v.k && lh.v.k <= VINDEXED, + "syntax error"); + if (this.testnext(',')) { /* assignment -> `,' primaryexp assignment */ + LHS_assign nv = new LHS_assign(); + nv.prev = lh; + this.primaryexp(nv.v); + if (nv.v.k == VLOCAL) + this.check_conflict(lh, nv.v); + this.assignment(nv, nvars+1); + } + else { /* assignment . `=' explist1 */ + int nexps; + this.checknext('='); + nexps = this.explist1(e); + if (nexps != nvars) { + this.adjust_assign(nvars, nexps, e); + if (nexps > nvars) + this.fs.freereg -= nexps - nvars; /* remove extra values */ + } + else { + fs.setoneret(e); /* close last expression */ + fs.storevar(lh.v, e); + return; /* avoid default */ + } + } + e.init(VNONRELOC, this.fs.freereg-1); /* default assignment */ + fs.storevar(lh.v, e); + } + + + int cond() { + /* cond -> exp */ + expdesc v = new expdesc(); + /* read condition */ + this.expr(v); + /* `falses' are all equal here */ + if (v.k == VNIL) + v.k = VFALSE; + fs.goiftrue(v); + return v.f.i; + } + + + void breakstat() { + FuncState fs = this.fs; + BlockCnt bl = fs.bl; + boolean upval = false; + while (bl != null && !bl.isbreakable) { + upval |= bl.upval; + bl = bl.previous; + } + if (bl == null) + this.syntaxerror("no loop to break"); + if (upval) + fs.codeABC(Lua.OP_CLOSE, bl.nactvar, 0, 0); + fs.concat(bl.breaklist, fs.jump()); + } + + + void whilestat (int line) { + /* whilestat -> WHILE cond DO block END */ + FuncState fs = this.fs; + int whileinit; + int condexit; + BlockCnt bl = new BlockCnt(); + this.next(); /* skip WHILE */ + whileinit = fs.getlabel(); + condexit = this.cond(); + fs.enterblock(bl, true); + this.checknext(TK_DO); + this.block(); + fs.patchlist(fs.jump(), whileinit); + this.check_match(TK_END, TK_WHILE, line); + fs.leaveblock(); + fs.patchtohere(condexit); /* false conditions finish the loop */ + } + + void repeatstat(int line) { + /* repeatstat -> REPEAT block UNTIL cond */ + int condexit; + FuncState fs = this.fs; + int repeat_init = fs.getlabel(); + BlockCnt bl1 = new BlockCnt(); + BlockCnt bl2 = new BlockCnt(); + fs.enterblock(bl1, true); /* loop block */ + fs.enterblock(bl2, false); /* scope block */ + this.next(); /* skip REPEAT */ + this.chunk(); + this.check_match(TK_UNTIL, TK_REPEAT, line); + condexit = this.cond(); /* read condition (inside scope block) */ + if (!bl2.upval) { /* no upvalues? */ + fs.leaveblock(); /* finish scope */ + fs.patchlist(condexit, repeat_init); /* close the loop */ + } else { /* complete semantics when there are upvalues */ + this.breakstat(); /* if condition then break */ + fs.patchtohere(condexit); /* else... */ + fs.leaveblock(); /* finish scope... */ + fs.patchlist(fs.jump(), repeat_init); /* and repeat */ + } + fs.leaveblock(); /* finish loop */ + } + + + int exp1() { + expdesc e = new expdesc(); + int k; + this.expr(e); + k = e.k; + fs.exp2nextreg(e); + return k; + } + + + void forbody(int base, int line, int nvars, boolean isnum) { + /* forbody -> DO block */ + BlockCnt bl = new BlockCnt(); + FuncState fs = this.fs; + int prep, endfor; + this.adjustlocalvars(3); /* control variables */ + this.checknext(TK_DO); + prep = isnum ? fs.codeAsBx(Lua.OP_FORPREP, base, NO_JUMP) : fs.jump(); + fs.enterblock(bl, false); /* scope for declared variables */ + this.adjustlocalvars(nvars); + fs.reserveregs(nvars); + this.block(); + fs.leaveblock(); /* end of scope for declared variables */ + fs.patchtohere(prep); + endfor = (isnum) ? fs.codeAsBx(Lua.OP_FORLOOP, base, NO_JUMP) : fs + .codeABC(Lua.OP_TFORLOOP, base, 0, nvars); + fs.fixline(line); /* pretend that `Lua.OP_FOR' starts the loop */ + fs.patchlist((isnum ? endfor : fs.jump()), prep + 1); + } + + + void fornum(LuaString varname, int line) { + /* fornum -> NAME = exp1,exp1[,exp1] forbody */ + FuncState fs = this.fs; + int base = fs.freereg; + this.new_localvarliteral(RESERVED_LOCAL_VAR_FOR_INDEX, 0); + this.new_localvarliteral(RESERVED_LOCAL_VAR_FOR_LIMIT, 1); + this.new_localvarliteral(RESERVED_LOCAL_VAR_FOR_STEP, 2); + this.new_localvar(varname, 3); + this.checknext('='); + this.exp1(); /* initial value */ + this.checknext(','); + this.exp1(); /* limit */ + if (this.testnext(',')) + this.exp1(); /* optional step */ + else { /* default step = 1 */ + fs.codeABx(Lua.OP_LOADK, fs.freereg, fs.numberK(LuaInteger.valueOf(1))); + fs.reserveregs(1); + } + this.forbody(base, line, 1, true); + } + + + void forlist(LuaString indexname) { + /* forlist -> NAME {,NAME} IN explist1 forbody */ + FuncState fs = this.fs; + expdesc e = new expdesc(); + int nvars = 0; + int line; + int base = fs.freereg; + /* create control variables */ + this.new_localvarliteral(RESERVED_LOCAL_VAR_FOR_GENERATOR, nvars++); + this.new_localvarliteral(RESERVED_LOCAL_VAR_FOR_STATE, nvars++); + this.new_localvarliteral(RESERVED_LOCAL_VAR_FOR_CONTROL, nvars++); + /* create declared variables */ + this.new_localvar(indexname, nvars++); + while (this.testnext(',')) + this.new_localvar(this.str_checkname(), nvars++); + this.checknext(TK_IN); + line = this.linenumber; + this.adjust_assign(3, this.explist1(e), e); + fs.checkstack(3); /* extra space to call generator */ + this.forbody(base, line, nvars - 3, false); + } + + + void forstat(int line) { + /* forstat -> FOR (fornum | forlist) END */ + FuncState fs = this.fs; + LuaString varname; + BlockCnt bl = new BlockCnt(); + fs.enterblock(bl, true); /* scope for loop and control variables */ + this.next(); /* skip `for' */ + varname = this.str_checkname(); /* first variable name */ + switch (this.t.token) { + case '=': + this.fornum(varname, line); + break; + case ',': + case TK_IN: + this.forlist(varname); + break; + default: + this.syntaxerror(LUA_QL("=") + " or " + LUA_QL("in") + " expected"); + } + this.check_match(TK_END, TK_FOR, line); + fs.leaveblock(); /* loop scope (`break' jumps to this point) */ + } + + + int test_then_block() { + /* test_then_block -> [IF | ELSEIF] cond THEN block */ + int condexit; + this.next(); /* skip IF or ELSEIF */ + condexit = this.cond(); + this.checknext(TK_THEN); + this.block(); /* `then' part */ + return condexit; + } + + + void ifstat(int line) { + /* ifstat -> IF cond THEN block {ELSEIF cond THEN block} [ELSE block] + * END */ + FuncState fs = this.fs; + int flist; + IntPtr escapelist = new IntPtr(NO_JUMP); + flist = test_then_block(); /* IF cond THEN block */ + while (this.t.token == TK_ELSEIF) { + fs.concat(escapelist, fs.jump()); + fs.patchtohere(flist); + flist = test_then_block(); /* ELSEIF cond THEN block */ + } + if (this.t.token == TK_ELSE) { + fs.concat(escapelist, fs.jump()); + fs.patchtohere(flist); + this.next(); /* skip ELSE (after patch, for correct line info) */ + this.block(); /* `else' part */ + } else + fs.concat(escapelist, flist); + fs.patchtohere(escapelist.i); + this.check_match(TK_END, TK_IF, line); + } + + void localfunc() { + expdesc v = new expdesc(); + expdesc b = new expdesc(); + FuncState fs = this.fs; + this.new_localvar(this.str_checkname(), 0); + v.init(VLOCAL, fs.freereg); + fs.reserveregs(1); + this.adjustlocalvars(1); + this.body(b, false, this.linenumber); + fs.storevar(v, b); + /* debug information will only see the variable after this point! */ + fs.getlocvar(fs.nactvar - 1).startpc = fs.pc; + } + + + void localstat() { + /* stat -> LOCAL NAME {`,' NAME} [`=' explist1] */ + int nvars = 0; + int nexps; + expdesc e = new expdesc(); + do { + this.new_localvar(this.str_checkname(), nvars++); + } while (this.testnext(',')); + if (this.testnext('=')) + nexps = this.explist1(e); + else { + e.k = VVOID; + nexps = 0; + } + this.adjust_assign(nvars, nexps, e); + this.adjustlocalvars(nvars); + } + + + boolean funcname(expdesc v) { + /* funcname -> NAME {field} [`:' NAME] */ + boolean needself = false; + this.singlevar(v); + while (this.t.token == '.') + this.field(v); + if (this.t.token == ':') { + needself = true; + this.field(v); + } + return needself; + } + + + void funcstat(int line) { + /* funcstat -> FUNCTION funcname body */ + boolean needself; + expdesc v = new expdesc(); + expdesc b = new expdesc(); + this.next(); /* skip FUNCTION */ + needself = this.funcname(v); + this.body(b, needself, line); + fs.storevar(v, b); + fs.fixline(line); /* definition `happens' in the first line */ + } + + + void exprstat() { + /* stat -> func | assignment */ + FuncState fs = this.fs; + LHS_assign v = new LHS_assign(); + this.primaryexp(v.v); + if (v.v.k == VCALL) /* stat -> func */ + LuaC.SETARG_C(fs.getcodePtr(v.v), 1); /* call statement uses no results */ + else { /* stat -> assignment */ + v.prev = null; + this.assignment(v, 1); + } + } + + void retstat() { + /* stat -> RETURN explist */ + FuncState fs = this.fs; + expdesc e = new expdesc(); + int first, nret; /* registers with returned values */ + this.next(); /* skip RETURN */ + if (block_follow(this.t.token) || this.t.token == ';') + first = nret = 0; /* return no values */ + else { + nret = this.explist1(e); /* optional return values */ + if (hasmultret(e.k)) { + fs.setmultret(e); + if (e.k == VCALL && nret == 1) { /* tail call? */ + LuaC.SET_OPCODE(fs.getcodePtr(e), Lua.OP_TAILCALL); + LuaC._assert (Lua.GETARG_A(fs.getcode(e)) == fs.nactvar); + } + first = fs.nactvar; + nret = Lua.LUA_MULTRET; /* return all values */ + } else { + if (nret == 1) /* only one single value? */ + first = fs.exp2anyreg(e); + else { + fs.exp2nextreg(e); /* values must go to the `stack' */ + first = fs.nactvar; /* return all `active' values */ + LuaC._assert (nret == fs.freereg - first); + } + } + } + fs.ret(first, nret); + } + + + boolean statement() { + int line = this.linenumber; /* may be needed for error messages */ + switch (this.t.token) { + case TK_IF: { /* stat -> ifstat */ + this.ifstat(line); + return false; + } + case TK_WHILE: { /* stat -> whilestat */ + this.whilestat(line); + return false; + } + case TK_DO: { /* stat -> DO block END */ + this.next(); /* skip DO */ + this.block(); + this.check_match(TK_END, TK_DO, line); + return false; + } + case TK_FOR: { /* stat -> forstat */ + this.forstat(line); + return false; + } + case TK_REPEAT: { /* stat -> repeatstat */ + this.repeatstat(line); + return false; + } + case TK_FUNCTION: { + this.funcstat(line); /* stat -> funcstat */ + return false; + } + case TK_LOCAL: { /* stat -> localstat */ + this.next(); /* skip LOCAL */ + if (this.testnext(TK_FUNCTION)) /* local function? */ + this.localfunc(); + else + this.localstat(); + return false; + } + case TK_RETURN: { /* stat -> retstat */ + this.retstat(); + return true; /* must be last statement */ + } + case TK_BREAK: { /* stat -> breakstat */ + this.next(); /* skip BREAK */ + this.breakstat(); + return true; /* must be last statement */ + } + default: { + this.exprstat(); + return false; /* to avoid warnings */ + } + } + } + + void chunk() { + /* chunk -> { stat [`;'] } */ + boolean islast = false; + this.enterlevel(); + while (!islast && !block_follow(this.t.token)) { + islast = this.statement(); + this.testnext(';'); + LuaC._assert (this.fs.f.maxstacksize >= this.fs.freereg + && this.fs.freereg >= this.fs.nactvar); + this.fs.freereg = this.fs.nactvar; /* free registers */ + } + this.leavelevel(); + } + + /* }====================================================================== */ + +} diff --git a/src/core/org/luaj/vm2/compiler/LuaC.java b/src/core/org/luaj/vm2/compiler/LuaC.java new file mode 100644 index 00000000..4e9dbb64 --- /dev/null +++ b/src/core/org/luaj/vm2/compiler/LuaC.java @@ -0,0 +1,223 @@ +/******************************************************************************* +* Copyright (c) 2009 Luaj.org. All rights reserved. +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +* THE SOFTWARE. +******************************************************************************/ +package org.luaj.vm2.compiler; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Hashtable; + +import org.luaj.vm2.LocVars; +import org.luaj.vm2.Lua; +import org.luaj.vm2.LuaError; +import org.luaj.vm2.Prototype; +import org.luaj.vm2.LuaString; +import org.luaj.vm2.LuaValue; +import org.luaj.vm2.LoadState.LuaCompiler; + + +/** + * Compiler for Lua + */ +public class LuaC extends Lua implements LuaCompiler { + + /** Install the compiler so that LoadState will first + * try to use it when handed bytes that are + * not already a compiled lua chunk. + */ + public static void install() { + org.luaj.vm2.LoadState.compiler = new LuaC(); + } + + protected static void _assert(boolean b) { + if (!b) + throw new LuaError("compiler assert failed"); + } + + public static final int MAXSTACK = 250; + static final int LUAI_MAXUPVALUES = 60; + static final int LUAI_MAXVARS = 200; + static final int NO_REG = MAXARG_A; + + + /* OpMode - basic instruction format */ + static final int + iABC = 0, + iABx = 1, + iAsBx = 2; + + /* OpArgMask */ + static final int + OpArgN = 0, /* argument is not used */ + OpArgU = 1, /* argument is used */ + OpArgR = 2, /* argument is a register or a jump offset */ + OpArgK = 3; /* argument is a constant or register/constant */ + + + static void SET_OPCODE(InstructionPtr i,int o) { + i.set( ( i.get() & (MASK_NOT_OP)) | ((o << POS_OP) & MASK_OP) ); + } + + static void SETARG_A(InstructionPtr i,int u) { + i.set( ( i.get() & (MASK_NOT_A)) | ((u << POS_A) & MASK_A) ); + } + + static void SETARG_B(InstructionPtr i,int u) { + i.set( ( i.get() & (MASK_NOT_B)) | ((u << POS_B) & MASK_B) ); + } + + static void SETARG_C(InstructionPtr i,int u) { + i.set( ( i.get() & (MASK_NOT_C)) | ((u << POS_C) & MASK_C) ); + } + + static void SETARG_Bx(InstructionPtr i,int u) { + i.set( ( i.get() & (MASK_NOT_Bx)) | ((u << POS_Bx) & MASK_Bx) ); + } + + static void SETARG_sBx(InstructionPtr i,int u) { + SETARG_Bx( i, u + MAXARG_sBx ); + } + + static int CREATE_ABC(int o, int a, int b, int c) { + return ((o << POS_OP) & MASK_OP) | + ((a << POS_A) & MASK_A) | + ((b << POS_B) & MASK_B) | + ((c << POS_C) & MASK_C) ; + } + + static int CREATE_ABx(int o, int a, int bc) { + return ((o << POS_OP) & MASK_OP) | + ((a << POS_A) & MASK_A) | + ((bc << POS_Bx) & MASK_Bx) ; + } + + // vector reallocation + + static LuaValue[] realloc(LuaValue[] v, int n) { + LuaValue[] a = new LuaValue[n]; + if ( v != null ) + System.arraycopy(v, 0, a, 0, Math.min(v.length,n)); + return a; + } + + static Prototype[] realloc(Prototype[] v, int n) { + Prototype[] a = new Prototype[n]; + if ( v != null ) + System.arraycopy(v, 0, a, 0, Math.min(v.length,n)); + return a; + } + + static LuaString[] realloc(LuaString[] v, int n) { + LuaString[] a = new LuaString[n]; + if ( v != null ) + System.arraycopy(v, 0, a, 0, Math.min(v.length,n)); + return a; + } + + static LocVars[] realloc(LocVars[] v, int n) { + LocVars[] a = new LocVars[n]; + if ( v != null ) + System.arraycopy(v, 0, a, 0, Math.min(v.length,n)); + return a; + } + + static int[] realloc(int[] v, int n) { + int[] a = new int[n]; + if ( v != null ) + System.arraycopy(v, 0, a, 0, Math.min(v.length,n)); + return a; + } + + static byte[] realloc(byte[] v, int n) { + byte[] a = new byte[n]; + if ( v != null ) + System.arraycopy(v, 0, a, 0, Math.min(v.length,n)); + return a; + } + + public int nCcalls; + Hashtable strings = new Hashtable(); + + /** Utility method to invoke the compiler for an input stream + */ + public static Prototype compile(InputStream is, String string) throws IOException { + return new LuaC().compile(is.read(), is, string); + } + + /** Compile source bytes into a LPrototype. + * + * Try to compile the file, and return the Prototype on success, + * or throw LuaErrorException on syntax error or I/O Exception + * + * @param firstByte the first byte from the InputStream. + * This can be read by the client and tested to see if it is already a binary chunk. + * @param stream InputStream to read from. + * @param name Name of the chunk + * @return null if the first byte indicates it is a binary chunk, + * a LPrototype instance if it can be compiled, + * or an exception is thrown if there is an error. + * @throws IOException if an I/O exception occurs + * @throws LuaError if there is a syntax error. + */ + public Prototype compile(int firstByte, InputStream stream, String name) throws IOException { + LuaC compiler = new LuaC(); + return compiler.luaY_parser(firstByte, stream, name); + } + + /** Parse the input */ + private Prototype luaY_parser(int firstByte, InputStream z, String name) { + LexState lexstate = new LexState(this, z); + FuncState funcstate = new FuncState(); + // lexstate.buff = buff; + lexstate.setinput( this, firstByte, z, (LuaString) LuaValue.valueOf(name) ); + lexstate.open_func(funcstate); + /* main func. is always vararg */ + funcstate.f.is_vararg = LuaC.VARARG_ISVARARG; + funcstate.f.source = (LuaString) LuaValue.valueOf("@"+name); + lexstate.next(); /* read first token */ + lexstate.chunk(); + lexstate.check(LexState.TK_EOS); + lexstate.close_func(); + LuaC._assert (funcstate.prev == null); + LuaC._assert (funcstate.f.nups == 0); + LuaC._assert (lexstate.fs == null); + return funcstate.f; + } + + // look up and keep at most one copy of each string + public LuaString newTString(byte[] bytes, int offset, int len) { + LuaString tmp = LuaString.valueOf(bytes, offset, len); + LuaString v = (LuaString) strings.get(tmp); + if ( v == null ) { + // must copy bytes, since bytes could be from reusable buffer + byte[] copy = new byte[len]; + System.arraycopy(bytes, offset, copy, 0, len); + v = LuaString.valueOf(copy); + strings.put(v, v); + } + return v; + } + + public String pushfstring(String string) { + return string; + } + +} diff --git a/src/core/org/luaj/vm2/lib/BaseLib.java b/src/core/org/luaj/vm2/lib/BaseLib.java new file mode 100644 index 00000000..0edf66e4 --- /dev/null +++ b/src/core/org/luaj/vm2/lib/BaseLib.java @@ -0,0 +1,403 @@ +/******************************************************************************* +* Copyright (c) 2009 Luaj.org. All rights reserved. +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +* THE SOFTWARE. +******************************************************************************/package org.luaj.vm2.lib; + +import java.io.IOException; +import java.io.InputStream; +import java.io.PrintStream; + +import org.luaj.vm2.LoadState; +import org.luaj.vm2.LuaClosure; +import org.luaj.vm2.LuaError; +import org.luaj.vm2.LuaString; +import org.luaj.vm2.LuaTable; +import org.luaj.vm2.LuaThread; +import org.luaj.vm2.LuaValue; +import org.luaj.vm2.Prototype; +import org.luaj.vm2.Varargs; + +/** + * Base library implementation, targeted for JME platforms. + * + * BaseLib instances are typically used as the initial globals table + * when creating a new uniqued runtime context. + * + * Since JME has no file system by default, dofile and loadfile use the + * FINDER instance to find resource files. The default loader chain + * in PackageLib will use these as well. + * + * For an implementation that looks in the current directory on JSE, + * use org.luaj.lib.j2se.BaseLib instead. + * + * @see org.luaj.vm2.lib.jse.JseBaseLib + */ +public class BaseLib extends LuaTable implements ResourceFinder { + public static final String VERSION = "Luaj 2.0"; + + public static InputStream STDIN = null; + public static PrintStream STDOUT = System.out; + public static PrintStream STDERR = System.err; + + /** + * Singleton file opener for this Java ClassLoader realm. + * + * Unless set or changed elsewhere, will be set by the BaseLib that is created. + */ + public static ResourceFinder FINDER; + + /** + * Construct a base libarary instance and initialize the functions in it. + */ + public BaseLib() { + this.set( "_G", this ); + this.set( "_VERSION", VERSION ); + LibFunction.bind( this, new BaseFunc1().getClass(), new String[] { + "getfenv", // ( [f] ) -> env + "getmetatable", // ( object ) -> table + "tostring", // (e) -> value + } ); + LibFunction.bind( this, new BaseFunc2().getClass(), new String[] { + "collectgarbage", // ( opt [,arg] ) -> value + "error", // ( message [,level] ) -> ERR + "rawequal", // (v1, v2) -> boolean + "setfenv", // (f, table) -> void + "tonumber", // (e [,base]) -> value + } ); + LibFunction.bind( this, new BaseFuncV().getClass(), new String[] { + "assert", // ( v [,message] ) -> v, message | ERR + "dofile", // ( filename ) -> result1, ... + "load", // ( func [,chunkname] ) -> chunk | nil, msg + "loadfile", // ( [filename] ) -> chunk | nil, msg + "loadstring", // ( string [,chunkname] ) -> chunk | nil, msg + "pcall", // (f, arg1, ...) -> status, result1, ... + "xpcall", // (f, err) -> result1, ... + "print", // (...) -> void + "select", // (f, ...) -> value1, ... + "unpack", // (list [,i [,j]]) -> result1, ... + "type", // (v) -> value + "rawget", // (table, index) -> value + "rawset", // (table, index, value) -> table + "setmetatable", // (table, metatable) -> table + } ); + + // pairs and ipars need iterator functions + // "next", // ( table, [index] ) -> next-index, next-value + // "inext", // not public ( table, [int-index] ) -> next-index, next-value + // "ipairs", // (t) -> iter-func, t, 0 + // "pairs", // (t) -> iter-func, t, nil + LuaValue next = new BaseIter(0,"next",null); + LuaValue inext = new BaseIter(1,"inext",null); + this.set( "pairs", new BaseIter(2,"pairs",next) ); + this.set( "ipairs", new BaseIter(3,"ipairs",inext) ); + this.set( "next", next ); + + // set the default resource finder if not set already + if ( FINDER == null ) + FINDER = this; + } + + /** ResourceFinder implementation + * + * Tries to open the file as a resource, which can work for . + */ + public InputStream findResource(String filename) { + Class c = getClass(); + return c.getResourceAsStream(filename.startsWith("/")? filename: "/"+filename); + } + + public static class BaseFunc1 extends OneArgFunction { + public LuaValue call(LuaValue arg) { + switch ( opcode ) { + case 0: { // "getfenv", // ( [f] ) -> env + if ( ! arg.isfunction() ) { + int i = arg.checkint(); + arg = (i==0)? (LuaValue) LuaThread.getRunning(): (LuaValue) LuaThread.getCallstackFunction(i-1); + if ( arg == null ) + LuaValue.argerror(1, "invalid level"); + } + return arg.getfenv(); + } + case 1: // "getmetatable", // ( object ) -> table + LuaValue mt = arg.getmetatable(); + return mt!=null? mt: NIL; + case 2: // "tostring", // (e) -> value + return arg.type() == LuaValue.TSTRING? arg: valueOf(arg.toString()); + } + return NIL; + } + } + + public static class BaseFunc2 extends TwoArgFunction { + public LuaValue call(LuaValue arg1,LuaValue arg2) { + switch ( opcode ) { + case 0: // "collectgarbage", // ( opt [,arg] ) -> value + String s = arg1.optString("collect"); + int result = 0; + if ( "collect".equals(s) ) { + System.gc(); + return ZERO; + } + else if ( "count".equals(s) ) { + Runtime rt = Runtime.getRuntime(); + long used = rt.totalMemory() - rt.freeMemory(); + return valueOf(used/1024.); + } else if ( "step".equals(s) ) { + System.gc(); + return LuaValue.TRUE; + } + return NIL; + case 1: // "error", // ( message [,level] ) -> ERR + throw new LuaError( arg1.isnil()? null: arg1.toString() ); + case 2: // "rawequal", // (v1, v2) -> boolean + return valueOf(arg1 == arg2); + case 3: { // "setfenv", // (f, table) -> void + LuaValue f = arg1; + if ( ! f.isfunction() ) { + int i = arg1.checkint(); + f = (i==0)? (LuaValue) LuaThread.getRunning(): (LuaValue) LuaThread.getCallstackFunction(i-1); + if ( f == null ) + LuaValue.argerror(1, "invalid level"); + } + f.setfenv(arg2); + return f; + } + case 4: // "tonumber", // (e [,base]) -> value + final int base = arg2.optint(10); + if (base == 10) { /* standard conversion */ + return arg1.tonumber(); + } else { + if ( base < 2 || base > 36 ) + argerror(2, "base out of range"); + final LuaString str = arg1.optstring(null); + return str!=null? str.tonumber(base): NIL; + } + } + return NIL; + } + } + + public static class BaseFuncV extends VarArgFunction { + private final BaseLib lib; + public BaseFuncV() { + this.lib = null; + } + public BaseFuncV(BaseLib lib) { + this.lib = lib; + } + public Varargs invoke(Varargs args) { + switch ( opcode ) { + case 0: // "assert", // ( v [,message] ) -> v, message | ERR + if ( !args.arg1().toboolean() ) error("assertion failed!"); + return args; + case 1: // "dofile", // ( filename ) -> result1, ... + { + LuaValue chunk; + try { + String filename = args.checkString(1); + chunk = loadFile(filename).arg1(); + } catch ( IOException e ) { + return error(e.getMessage()); + } + return chunk.invoke(); + } + case 2: // "load", // ( func [,chunkname] ) -> chunk | nil, msg + try { + LuaValue func = args.checkfunction(1); + String chunkname = args.optString(2, "function"); + Prototype p = LoadState.undump(new StringInputStream(func), chunkname); + return new LuaClosure(p,LuaThread.getRunningEnv(env)); + } catch ( Exception e ) { + return varargsOf(NIL, valueOf(e.getMessage())); + } + case 3: // "loadfile", // ( [filename] ) -> chunk | nil, msg + { + try { + String filename = args.checkString(1); + return loadFile(filename); + } catch ( Exception e ) { + return varargsOf(NIL, valueOf(e.getMessage())); + } + } + case 4: // "loadstring", // ( string [,chunkname] ) -> chunk | nil, msg + try { + LuaString script = args.checkstring(1); + LuaString chunkname = args.optstring(2, script); + Prototype p = LoadState.undump(script.toInputStream(), chunkname.toString()); + return new LuaClosure(p,LuaThread.getRunningEnv(env)); + } catch ( Exception e ) { + return varargsOf(NIL, valueOf(e.getMessage())); + } + case 5: // "pcall", // (f, arg1, ...) -> status, result1, ... + try { + LuaThread.onCall(this); + try { + return varargsOf(LuaValue.TRUE, args.arg1().invoke(args.subargs(2))); + } finally { + LuaThread.onReturn(); + } + } catch ( LuaError le ) { + String m = le.getMessage(); + return varargsOf(FALSE, m!=null? valueOf(m): NIL); + } catch ( Exception e ) { + String m = e.getMessage(); + return varargsOf(FALSE, valueOf(m!=null? m: e.toString())); + } + case 6: // "xpcall", // (f, err) -> result1, ... + try { + LuaThread.onCall(this); + try { + LuaThread thread = LuaThread.getRunning(); + LuaValue olderr = thread.err; + try { + thread.err = args.arg(2); + return varargsOf(LuaValue.TRUE, args.arg1().invoke(args.subargs(2))); + } finally { + thread.err = olderr; + } + } finally { + LuaThread.onReturn(); + } + } catch ( Exception e ) { + try { + return args.arg(2).invoke(valueOf(e.getMessage())); + } catch ( Exception f ) { + return varargsOf(FALSE, valueOf(f.getMessage())); + } + } + case 7: // "print", // (...) -> void + { + LuaValue tostring = env.get("tostring"); + for ( int i=1, n=args.narg(); i<=n; i++ ) { + if ( i>1 ) STDOUT.write( '\t' ); + LuaString s = tostring.call( args.arg(i) ).strvalue(); + int z = s.indexOf((byte)0, 0); + STDOUT.write( s.m_bytes, s.m_offset, z>=0? z: s.m_length ); + } + STDOUT.println(); + return NONE; + } + case 8: // "select", // (f, ...) -> value1, ... + { + int n = args.narg()-1; + if ( args.arg1().equals(valueOf("#")) ) + return valueOf(n); + int i = args.checkint(1); + if ( i == 0 || i < -n ) + typerror(1,"index out of range"); + return args.subargs(i<0? n+i+2: i+1); + } + case 9: // "unpack", // (list [,i [,j]]) -> result1, ... + { + int na = args.narg(); + LuaTable t = args.checktable(1); + int n = t.length(); + int i = na>=2? args.checkint(2): 1; + int j = na>=3? args.checkint(3): n; + n = j-i+1; + if ( n<0 ) return NONE; + if ( n==1 ) return t.get(i); + if ( n==2 ) return varargsOf(t.get(i),t.get(j)); + LuaValue[] v = new LuaValue[n]; + for ( int k=0; k value + return valueOf(args.checkvalue(1).typename()); + case 11: // "rawget", // (table, index) -> value + return args.checktable(1).rawget(args.checkvalue(2)); + case 12: { // "rawset", // (table, index, value) -> table + LuaTable t = args.checktable(1); + t.rawset(args.checknotnil(2), args.checkvalue(3)); + return t; + } + case 13: { // "setmetatable", // (table, metatable) -> table + final LuaValue t = args.arg1(); + final LuaValue mt = args.checkvalue(2); + t.setmetatable(mt.isnil()? null: mt.checktable()); + return t; + } + } + return NONE; + } + + private Varargs loadFile(String filename) throws IOException { + InputStream is = FINDER.findResource(filename); + if ( is == null ) + return varargsOf(NIL, valueOf("not found: "+filename)); + try { + Prototype p = LoadState.undump(is, filename); + return new LuaClosure(p,LuaThread.getRunningEnv(env)); + } finally { + is.close(); + } + } + } + + public static class BaseIter extends VarArgFunction { + final LuaValue aux; + BaseIter(int opcode, String name, LuaValue aux) { + this.name = name; + this.opcode = opcode; + this.aux = aux; + } + public Varargs invoke(Varargs args) { + switch ( opcode ) { + case 0: // "next" ( table, [index] ) -> next-index, next-value + return args.arg1().next(args.arg(2)); + case 1: // "inext" ( table, [int-index] ) -> next-index, next-value + return args.arg1().inext(args.arg(2)); + case 2: // "pairs" (t) -> iter-func, t, nil + return varargsOf( aux, args.checktable(1), NIL ); + case 3: // "ipairs", // (t) -> iter-func, t, 0 + return varargsOf( aux, args.checktable(1), ZERO ); + } + return NONE; + } + } + + + private static class StringInputStream extends InputStream { + LuaValue func; + byte[] bytes; + int offset; + StringInputStream(LuaValue func) { + this.func = func; + } + public int read() throws IOException { + if ( func == null ) return -1; + if ( bytes == null ) { + LuaValue s = func.call(); + if ( s.isnil() ) { + func = null; + bytes = null; + return -1; + } + bytes = s.toString().getBytes(); + offset = 0; + } + if ( offset >= bytes.length ) + return -1; + return bytes[offset++]; + + } + } +} diff --git a/src/core/org/luaj/vm2/lib/CoroutineLib.java b/src/core/org/luaj/vm2/lib/CoroutineLib.java new file mode 100644 index 00000000..a4ed5adb --- /dev/null +++ b/src/core/org/luaj/vm2/lib/CoroutineLib.java @@ -0,0 +1,111 @@ +/******************************************************************************* +* Copyright (c) 2007 LuaJ. All rights reserved. +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +* THE SOFTWARE. +******************************************************************************/ +package org.luaj.vm2.lib; + +import org.luaj.vm2.LuaThread; +import org.luaj.vm2.LuaTable; +import org.luaj.vm2.LuaValue; +import org.luaj.vm2.Varargs; + +public class CoroutineLib extends VarArgFunction { + + private static final String[] NAMES = { + "", + "create", + "resume", + "running", + "status", + "yield", + "wrap", + "wrapped" + }; + + private static final int INSTALL = 0; + private static final int CREATE = 1; + private static final int RESUME = 2; + private static final int RUNNING = 3; + private static final int STATUS = 4; + private static final int YIELD = 5; + private static final int WRAP = 6; + private static final int WRAPPED = 7; + + public static void install(LuaValue globals) { + globals.set("coroutine", createInstance()); + } + + public static final LuaValue createInstance() { + LuaTable t = new LuaTable(); + CoroutineLib f = new CoroutineLib(); + LibFunction.bind(t, f.getClass(), NAMES); + return t; + } + + public CoroutineLib() {} + + private CoroutineLib(String name, int opcode, LuaThread thread) { + super(name, opcode, thread); + } + + public Varargs invoke(Varargs args) { + switch ( opcode ) { + case INSTALL: + return createInstance(); + case CREATE: { + final LuaValue func = args.checkfunction(1); + return new LuaThread(func, func.getfenv() ); + } + case RESUME: { + final LuaThread t = args.checkthread(1); + return t.resume( args.subargs(2) ); + } + case RUNNING: { + final LuaThread r = LuaThread.getRunning(); + return LuaThread.isMainThread(r)? NIL: r; + } + case STATUS: { + return valueOf( args.checkthread(1).getStatus() ); + } + case YIELD: { + final LuaThread r = LuaThread.getRunning(); + if ( LuaThread.isMainThread( r ) ) + error("main thread can't yield"); + return r.yield( args ); + } + case WRAP: { + final LuaValue func = args.checkfunction(1); + final LuaThread thread = new LuaThread(func, func.getfenv()); + return new CoroutineLib("wrapped",WRAPPED,thread); + } + case WRAPPED: { + final LuaThread t = (LuaThread) env; + final Varargs result = t.resume( args ); + if ( result.arg1().toboolean() ) { + return result.subargs(2); + } else { + error( result.arg(2).toString() ); + } + } + default: + return NONE; + } + } +} diff --git a/src/core/org/luaj/vm2/lib/DebugLib.java b/src/core/org/luaj/vm2/lib/DebugLib.java new file mode 100644 index 00000000..a8325077 --- /dev/null +++ b/src/core/org/luaj/vm2/lib/DebugLib.java @@ -0,0 +1,905 @@ +/******************************************************************************* +* Copyright (c) 2009 Luaj.org. All rights reserved. +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +* THE SOFTWARE. +******************************************************************************/ +package org.luaj.vm2.lib; + + +import org.luaj.vm2.LuaClosure; +import org.luaj.vm2.Lua; +import org.luaj.vm2.LuaError; +import org.luaj.vm2.LuaFunction; +import org.luaj.vm2.LuaThread; +import org.luaj.vm2.Print; +import org.luaj.vm2.Prototype; +import org.luaj.vm2.LuaString; +import org.luaj.vm2.LuaTable; +import org.luaj.vm2.LuaValue; +import org.luaj.vm2.Varargs; + +public class DebugLib extends VarArgFunction { + public static final boolean CALLS = (null != System.getProperty("CALLS")); + public static final boolean TRACE = (null != System.getProperty("TRACE")); + + // leave this unset to allow obfuscators to remove it in production builds + public static boolean DEBUG_ENABLED = false; + + private static final String[] NAMES = { + "", + "debug", + "getfenv", + "gethook", + "getinfo", + "getlocal", + "getmetatable", + "getregistry", + "getupvalue", + "setfenv", + "sethook", + "setlocal", + "setmetatable", + "setupvalue", + "traceback", + }; + + private static final int INSTALL = 0; + private static final int DEBUG = 1; + private static final int GETFENV = 2; + private static final int GETHOOK = 3; + private static final int GETINFO = 4; + private static final int GETLOCAL = 5; + private static final int GETMETATABLE = 6; + private static final int GETREGISTRY = 7; + private static final int GETUPVALUE = 8; + private static final int SETFENV = 9; + private static final int SETHOOK = 10; + private static final int SETLOCAL = 11; + private static final int SETMETATABLE = 12; + private static final int SETUPVALUE = 13; + private static final int TRACEBACK = 14; + + /* maximum stack for a Lua function */ + private static final int MAXSTACK = 250; + + private static final LuaString LUA = LuaString.valueOf("Lua"); + private static final LuaString JAVA = LuaString.valueOf("Java"); + private static final LuaString QMARK = LuaString.valueOf("?"); + private static final LuaString GLOBAL = LuaString.valueOf("global"); + private static final LuaString LOCAL = LuaString.valueOf("local"); + private static final LuaString METHOD = LuaString.valueOf("method"); + private static final LuaString UPVALUE = LuaString.valueOf("upvalue"); + private static final LuaString FIELD = LuaString.valueOf("field"); + private static final LuaString CALL = LuaString.valueOf("call"); + private static final LuaString LINE = LuaString.valueOf("line"); + private static final LuaString RETURN = LuaString.valueOf("return"); + private static final LuaString TAILRETURN = LuaString.valueOf("tail return"); + + private static final LuaString FUNC = LuaString.valueOf("func"); + private static final LuaString NUPS = LuaString.valueOf("nups"); + private static final LuaString NAME = LuaString.valueOf("name"); + private static final LuaString NAMEWHAT = LuaString.valueOf("namewhat"); + private static final LuaString WHAT = LuaString.valueOf("what"); + private static final LuaString SOURCE = LuaString.valueOf("source"); + private static final LuaString SHORT_SRC = LuaString.valueOf("short_src"); + private static final LuaString LINEDEFINED = LuaString.valueOf("linedefined"); + private static final LuaString LASTLINEDEFINED = LuaString.valueOf("lastlinedefined"); + private static final LuaString CURRENTLINE = LuaString.valueOf("currentline"); + private static final LuaString ACTIVELINES = LuaString.valueOf("activelines"); + + public static void install(LuaValue globals) { + globals.set("debug", DebugLib.createInstance() ); + } + + public static final LuaValue createInstance() { + LuaTable t = new LuaTable(); + DebugLib f = new DebugLib(); + LibFunction.bind(t, f.getClass(), NAMES); + if ( ! DEBUG_ENABLED ) { + DEBUG_ENABLED = true; + } + return t; + } + + public DebugLib() {} + + public Varargs invoke(Varargs args) { + switch ( opcode ) { + case INSTALL: + return createInstance(); + case DEBUG: + return _debug(args); + case GETFENV: + return _getfenv(args); + case GETHOOK: + return _gethook(args); + case GETINFO: + return _getinfo(args); + case GETLOCAL: + return _getlocal(args); + case GETMETATABLE: + return _getmetatable(args); + case GETREGISTRY: + return _getregistry(args); + case GETUPVALUE: + return _getupvalue(args); + case SETFENV: + return _setfenv(args); + case SETHOOK: + return _sethook(args); + case SETLOCAL: + return _setlocal(args); + case SETMETATABLE: + return _setmetatable(args); + case SETUPVALUE: + return _setupvalue(args); + case TRACEBACK: + return _traceback(args); + default: + return NONE; + } + } + + // ------------------------ Debug Info management -------------------------- + // + // when DEBUG_ENABLED is set to true, these functions will be called + // by Closure instances as they process bytecodes. + // + // Each thread will get a DebugState attached to it by the debug library + // which will track function calls, hook functions, etc. + // + private static class DebugInfo { + LuaValue func; + LuaClosure closure; + LuaValue[] stack; + Varargs varargs, extras; + int pc, top; + + private DebugInfo() { + func = NIL; + } + private DebugInfo(LuaValue func) { + pc = -1; + setfunction( func ); + } + void setargs(Varargs varargs, LuaValue[] stack) { + this.varargs = varargs; + this.stack = stack; + } + void setfunction( LuaValue func ) { + this.func = func; + this.closure = (func instanceof LuaClosure? (LuaClosure) func: null); + } + void clear() { + func = NIL; + closure = null; + stack = null; + varargs = extras = null; + pc = top = 0; + } + public void bytecode(int pc, Varargs extras, int top) { + this.pc = pc; + this.top = top; + this.extras = extras; + } + public int currentline() { + if ( closure == null ) return -1; + int[] li = closure.p.lineinfo; + return li==null || pc<0 || pc>=li.length? -1: li[pc]; + } + public LuaString[] getfunckind() { + if ( closure == null || pc<0 ) return null; + int stackpos = (closure.p.code[pc] >> 6) & 0xff; + return getobjname(this, stackpos); + } + public Object sourceline() { + if ( closure == null ) return func.toString(); + String s = closure.p.source.toString(); + int line = currentline(); + return (s.startsWith("@")||s.startsWith("=")? s.substring(1): s) + ":" + line; + } + public String tracename() { + // if ( func != null ) + // return func.toString(); + LuaString[] kind = getfunckind(); + if ( kind == null ) + return "function ?"; + return "function "+kind[0].toString(); + } + public LuaString getlocalname(int index) { + if ( closure == null ) return null; + return closure.p.getlocalname(index, pc); + } + public String toString() { + return sourceline()+": in "+tracename(); + } + } + + /** DebugState is associated with a Thread */ + private static class DebugState { + private final LuaThread thread; + private int debugCalls = 0; + private DebugInfo[] debugInfo = new DebugInfo[LuaThread.MAX_CALLSTACK+1]; + private LuaValue hookfunc; + private boolean hookcall,hookline,hookrtrn,inhook; + private int hookcount; + private int line; + private DebugState(LuaThread thread) { + this.thread = thread; + } + public DebugInfo nextInfo() { + DebugInfo di = debugInfo[debugCalls]; + if ( di == null ) + debugInfo[debugCalls] = di = new DebugInfo(); + return di; + } + public DebugInfo pushInfo( int calls ) { + while ( debugCalls < calls ) { + nextInfo(); + ++debugCalls; + } + return debugInfo[debugCalls-1]; + } + public void popInfo(int calls) { + while ( debugCalls > calls ) + debugInfo[--debugCalls].clear(); + } + private void callHookFunc(DebugState ds, LuaString type, LuaValue arg) { + if ( inhook || hookfunc == null ) + return; + inhook = true; + try { + int n = debugCalls; + ds.nextInfo().setargs( arg, null ); + ds.pushInfo(n+1).setfunction(hookfunc); + try { + hookfunc.call(type,arg); + } finally { + ds.popInfo(n); + } + } catch ( Throwable t ) { + t.printStackTrace(); + } finally { + inhook = false; + } + } + public void sethook(LuaValue func, boolean call, boolean line, boolean rtrn, int count) { + this.hookcount = count; + this.hookcall = call; + this.hookline = line; + this.hookrtrn = rtrn; + this.hookfunc = func; + } + private DebugInfo getDebugInfo() { + return debugInfo[debugCalls-1]; + } + private DebugInfo getDebugInfo(int level) { + return level < 0 || level >= debugCalls? null: debugInfo[debugCalls-level-1]; + } + public DebugInfo findDebugInfo(LuaValue func) { + for ( int i=debugCalls; --i>=0; ) { + if ( debugInfo[i].func == func ) { + return debugInfo[i]; + } + } + return new DebugInfo(func); + } + public String toString() { + return DebugLib.traceback(thread, 0); + } +} + + private static DebugState getDebugState( LuaThread thread ) { + if ( thread.debugState == null ) + thread.debugState = new DebugState(thread); + return (DebugState) thread.debugState; + } + + private static DebugState getDebugState() { + return getDebugState( LuaThread.getRunning() ); + } + + /** Called by Closures to set up stack and arguments to next call */ + public static void debugSetupCall(Varargs args, LuaValue[] stack) { + DebugState ds = getDebugState(); + if ( ds.inhook ) + return; + ds.nextInfo().setargs( args, stack ); + } + + /** Called by Closures and recursing java functions on entry + * @param calls */ + public static void debugOnCall(LuaThread thread, int calls, LuaFunction func) { + DebugState ds = getDebugState(); + if ( ds.inhook ) + return; + DebugInfo di = ds.pushInfo(calls); + di.setfunction( func ); + if(CALLS)System.out.println("calling "+func); + if ( ds.hookcall ) + ds.callHookFunc( ds, CALL, LuaValue.NIL ); + } + + /** Called by Closures and recursing java functions on return + * @param running_calls + * @param thread */ + public static void debugOnReturn(LuaThread thread, int calls) { + DebugState ds = getDebugState(thread); + if ( ds.inhook ) + return; + if(CALLS)System.out.println("returning"); + try { + if ( ds.hookrtrn ) + ds.callHookFunc( ds, RETURN, LuaValue.NIL ); + } finally { + getDebugState().popInfo(calls); + } + } + + /** Called by Closures on bytecode execution */ + public static void debugBytecode( int pc, Varargs extras, int top ) { + DebugState ds = getDebugState(); + if ( ds.inhook ) + return; + DebugInfo di = ds.getDebugInfo(); + if(TRACE)Print.printState(di.closure, pc, di.stack, top, di.varargs); + ds.getDebugInfo().bytecode( pc, extras, top ); + if ( ds.hookline ) { + int newline = di.currentline(); + if ( newline != ds.line ) { + ds.line = newline; + ds.callHookFunc( ds, LINE, LuaValue.valueOf(newline) ); + } + } + } + + // ------------------- library function implementations ----------------- + + // j2se subclass may wish to override and provide actual console here. + // j2me platform has not System.in to provide console. + private static Varargs _debug(Varargs args) { + return NONE; + } + + private static Varargs _gethook(Varargs args) { + int a=1; + LuaThread thread = args.isthread(a)? args.checkthread(a++): LuaThread.getRunning(); + DebugState ds = getDebugState(thread); + return varargsOf( + ds.hookfunc, + valueOf((ds.hookcall?"c":"")+(ds.hookline?"l":"")+(ds.hookrtrn?"r":"")), + valueOf(ds.hookcount)); + } + + private static Varargs _sethook(Varargs args) { + int a=1; + LuaThread thread = args.isthread(a)? args.checkthread(a++): LuaThread.getRunning(); + LuaValue func = args.optfunction(a++, null); + String str = args.optString(a++,""); + int count = args.optint(a++,0); + boolean call=false,line=false,rtrn=false; + for ( int i=0; i0? + ds.getDebugInfo(level-1): + new DebugInfo( this ); + } else { + di = ds.findDebugInfo( func.checkfunction() ); + } + if ( di == null ) + return NIL; + + // start a table + LuaTable info = new LuaTable(); + LuaClosure c = di.closure; + for (int i = 0, j = what.length(); i < j; i++) { + switch (what.charAt(i)) { + case 'S': { + if ( c != null ) { + Prototype p = c.p; + info.set(WHAT, LUA); + info.set(SOURCE, p.source); + info.set(SHORT_SRC, valueOf(sourceshort(p))); + info.set(LINEDEFINED, valueOf(p.linedefined)); + info.set(LASTLINEDEFINED, valueOf(p.lastlinedefined)); + } else { + String shortName = di.func.toString(); + LuaString name = LuaString.valueOf("[Java] "+shortName); + info.set(WHAT, JAVA); + info.set(SOURCE, name); + info.set(SHORT_SRC, valueOf(shortName)); + info.set(LINEDEFINED, LuaValue.MINUSONE); + info.set(LASTLINEDEFINED, LuaValue.MINUSONE); + } + break; + } + case 'l': { + int line = di.currentline(); + info.set( CURRENTLINE, valueOf(line) ); + break; + } + case 'u': { + info.set(NUPS, valueOf(c!=null? c.p.nups: 0)); + break; + } + case 'n': { + LuaString[] kind = di.getfunckind(); + info.set(NAME, kind!=null? kind[0]: QMARK); + info.set(NAMEWHAT, kind!=null? kind[1]: EMPTYSTRING); + break; + } + case 'f': { + info.set( FUNC, di.func ); + break; + } + case 'L': { + LuaTable lines = new LuaTable(); + info.set(ACTIVELINES, lines); +// if ( di.luainfo != null ) { +// int line = di.luainfo.currentline(); +// if ( line >= 0 ) +// lines.set(1, IntValue.valueOf(line)); +// } + break; + } + } + } + return info; + } + + public static String sourceshort(Prototype p) { + String name = p.source.toString(); + if ( name.startsWith("@") || name.startsWith("=") ) + name = name.substring(1); + else if ( name.startsWith("\033") ) + name = "binary string"; + return name; + } + + private static Varargs _getlocal(Varargs args) { + int a=1; + LuaThread thread = args.isthread(a)? args.checkthread(a++): LuaThread.getRunning(); + int level = args.checkint(a++); + int local = args.checkint(a++); + + DebugState ds = getDebugState(thread); + DebugInfo di = ds.getDebugInfo(level-1); + LuaString name = (di!=null? di.getlocalname(local): null); + if ( name != null ) { + LuaValue value = di.stack[local-1]; + return varargsOf( name, value ); + } else { + return NIL; + } + } + + private static Varargs _setlocal(Varargs args) { + int a=1; + LuaThread thread = args.isthread(a)? args.checkthread(a++): LuaThread.getRunning(); + int level = args.checkint(a++); + int local = args.checkint(a++); + LuaValue value = args.arg(a++); + + DebugState ds = getDebugState(thread); + DebugInfo di = ds.getDebugInfo(level-1); + LuaString name = (di!=null? di.getlocalname(local): null); + if ( name != null ) { + di.stack[local-1] = value; + return name; + } else { + return NIL; + } + } + + private static LuaValue _getmetatable(Varargs args) { + LuaValue object = args.arg(1); + LuaValue mt = object.getmetatable(); + return mt!=null? mt: NIL; + } + + private static Varargs _setmetatable(Varargs args) { + LuaValue object = args.arg(1); + try { + if ( ! args.isnoneornil(2) ) + object.setmetatable(args.checktable(2)); + else + object.setmetatable(null); + return LuaValue.TRUE; + } catch ( LuaError e ) { + return varargsOf(FALSE, valueOf(e.toString())); + } + } + + private static Varargs _getregistry(Varargs args) { + return new LuaTable(); + } + + private static LuaString findupvalue(LuaClosure c, int up) { + if ( c.upValues != null && up > 0 && up <= c.upValues.length ) { + if ( c.p.upvalues != null && up <= c.p.upvalues.length ) + return c.p.upvalues[up-1]; + else + return LuaString.valueOf( "."+up ); + } + return null; + } + + private static Varargs _getupvalue(Varargs args) { + LuaValue func = args.checkfunction(1); + int up = args.checkint(2); + if ( func instanceof LuaClosure ) { + LuaClosure c = (LuaClosure) func; + LuaString name = findupvalue(c, up); + if ( name != null ) { + return varargsOf(name, c.upValues[up-1].getValue() ); + } + } + return NIL; + } + + private static LuaValue _setupvalue(Varargs args) { + LuaValue func = args.checkfunction(1); + int up = args.checkint(2); + LuaValue value = args.arg(3); + if ( func instanceof LuaClosure ) { + LuaClosure c = (LuaClosure) func; + LuaString name = findupvalue(c, up); + if ( name != null ) { + c.upValues[up-1].setValue(value); + return name; + } + } + return NIL; + } + + private static LuaValue _traceback(Varargs args) { + int a=1; + LuaThread thread = args.isthread(a)? args.checkthread(a++): LuaThread.getRunning(); + String message = args.optString(a++, "stack traceback:"); + int level = args.optint(a++,1); + String tb = DebugLib.traceback(thread, level); + return valueOf(message+"\n"+tb); + } + + // =================== public utilities ==================== + + /** + * Get a traceback as a string for the current thread + */ + public static String traceback(int level) { + return traceback(LuaThread.getRunning(), level); + } + + /** + * Get a traceback for a particular thread. + * @param thread + * @param level + * @return + */ + public static String traceback(LuaThread thread, int level) { + StringBuffer sb = new StringBuffer(); + DebugState ds = getDebugState(thread); + DebugInfo di; + for ( int i=level, n=ds.debugCalls; i 0) { + /* cannot jump to a setlist count */ + int d = pt.code[dest - 1]; + if ((Lua.GET_OPCODE(d) == Lua.OP_SETLIST && Lua.GETARG_C(d) == 0)) return 0; + } + } + break; + } + } + if (Lua.testAMode(op)) { + if (a == reg) + last = pc; /* change register `a' */ + } + if (Lua.testTMode(op)) { + if (!(pc + 2 < pt.code.length)) return 0; /* check skip */ + if (!(Lua.GET_OPCODE(pt.code[pc + 1]) == Lua.OP_JMP)) return 0; + } + switch (op) { + case Lua.OP_LOADBOOL: { + if (!(c == 0 || pc + 2 < pt.code.length)) return 0; /* check its jump */ + break; + } + case Lua.OP_LOADNIL: { + if (a <= reg && reg <= b) + last = pc; /* set registers from `a' to `b' */ + break; + } + case Lua.OP_GETUPVAL: + case Lua.OP_SETUPVAL: { + if (!(b < pt.nups)) return 0; + break; + } + case Lua.OP_GETGLOBAL: + case Lua.OP_SETGLOBAL: { + if (!(pt.k[b].isstring())) return 0; + break; + } + case Lua.OP_SELF: { + if (!checkreg(pt, a + 1)) return 0; + if (reg == a + 1) + last = pc; + break; + } + case Lua.OP_CONCAT: { + if (!(b < c)) return 0; /* at least two operands */ + break; + } + case Lua.OP_TFORLOOP: { + if (!(c >= 1)) return 0; /* at least one result (control variable) */ + if (!checkreg(pt, a + 2 + c)) return 0; /* space for results */ + if (reg >= a + 2) + last = pc; /* affect all regs above its base */ + break; + } + case Lua.OP_FORLOOP: + case Lua.OP_FORPREP: + if (!checkreg(pt, a + 3)) return 0; + /* go through */ + case Lua.OP_JMP: { + int dest = pc + 1 + b; + /* not full check and jump is forward and do not skip `lastpc'? */ + if (reg != Lua.NO_REG && pc < dest && dest <= lastpc) + pc += b; /* do the jump */ + break; + } + case Lua.OP_CALL: + case Lua.OP_TAILCALL: { + if (b != 0) { + if (!checkreg(pt, a + b - 1)) return 0; + } + c--; /* c = num. returns */ + if (c == Lua.LUA_MULTRET) { + if (!(checkopenop(pt, pc))) return 0; + } else if (c != 0) + if (!checkreg(pt, a + c - 1)) return 0; + if (reg >= a) + last = pc; /* affect all registers above base */ + break; + } + case Lua.OP_RETURN: { + b--; /* b = num. returns */ + if (b > 0) + if (!checkreg(pt, a + b - 1)) return 0; + break; + } + case Lua.OP_SETLIST: { + if (b > 0) + if (!checkreg(pt, a + b)) return 0; + if (c == 0) + pc++; + break; + } + case Lua.OP_CLOSURE: { + int nup, j; + if (!(b < pt.p.length)) return 0; + nup = pt.p[b].nups; + if (!(pc + nup < pt.code.length)) return 0; + for (j = 1; j <= nup; j++) { + int op1 = Lua.GET_OPCODE(pt.code[pc + j]); + if (!(op1 == Lua.OP_GETUPVAL || op1 == Lua.OP_MOVE)) return 0; + } + if (reg != Lua.NO_REG) /* tracing? */ + pc += nup; /* do not 'execute' these pseudo-instructions */ + break; + } + case Lua.OP_VARARG: { + if (!((pt.is_vararg & Lua.VARARG_ISVARARG) != 0 + && (pt.is_vararg & Lua.VARARG_NEEDSARG) == 0)) return 0; + b--; + if (b == Lua.LUA_MULTRET) + if (!(checkopenop(pt, pc))) return 0; + if (!checkreg(pt, a + b - 1)) return 0; + break; + } + default: + break; + } + } + return pt.code[last]; + } + +} diff --git a/src/core/org/luaj/vm2/lib/IoLib.java b/src/core/org/luaj/vm2/lib/IoLib.java new file mode 100644 index 00000000..fbac3e3c --- /dev/null +++ b/src/core/org/luaj/vm2/lib/IoLib.java @@ -0,0 +1,431 @@ +/******************************************************************************* +* Copyright (c) 2009 Luaj.org. All rights reserved. +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +* THE SOFTWARE. +******************************************************************************/ +package org.luaj.vm2.lib; + +import java.io.ByteArrayOutputStream; +import java.io.EOFException; +import java.io.IOException; + +import org.luaj.vm2.LuaString; +import org.luaj.vm2.LuaTable; +import org.luaj.vm2.LuaValue; +import org.luaj.vm2.Varargs; + + + +abstract +public class IoLib extends LuaTable { + + abstract + protected class File extends LuaValue{ + abstract public void write( LuaString string ) throws IOException; + abstract public void flush() throws IOException; + abstract public boolean isstdfile(); + abstract public void close() throws IOException; + abstract public boolean isclosed(); + // returns new position + abstract public int seek(String option, int bytecount) throws IOException; + abstract public void setvbuf(String mode, int size); + // get length remaining to read + abstract public int remaining() throws IOException; + // peek ahead one character + abstract public int peek() throws IOException, EOFException; + // return char if read, -1 if eof, throw IOException on other exception + abstract public int read() throws IOException, EOFException; + // return number of bytes read if positive, false if eof, throw IOException on other exception + abstract public int read(byte[] bytes, int offset, int length) throws IOException; + + // delegate method access to file methods table + public LuaValue get( LuaValue key ) { + return filemethods.get(key); + } + + // essentially a userdata instance + public int type() { + return LuaValue.TUSERDATA; + } + public String typename() { + return "userdata"; + } + + // displays as "file" type + public String toString() { + return "file: " + Integer.toHexString(hashCode()); + } + } + + + /** + * Wrap the standard input. + * @return File + * @throws IOException + */ + abstract protected File wrapStdin() throws IOException; + + /** + * Wrap the standard output. + * @return File + * @throws IOException + */ + abstract protected File wrapStdout() throws IOException; + + /** + * Open a file in a particular mode. + * @param filename + * @param mode + * @return File object if successful + * @throws IOException if could not be opened + */ + abstract protected File openFile( String filename, boolean readMode, boolean appendMode, boolean updateMode, boolean binaryMode ) throws IOException; + + /** + * Open a temporary file. + * @return File object if successful + * @throws IOException if could not be opened + */ + abstract protected File tmpFile() throws IOException; + + /** + * Start a new process and return a file for input or output + * @param prog the program to execute + * @param mode "r" to read, "w" to write + * @return File to read to or write from + * @throws IOException if an i/o exception occurs + */ + abstract protected File openProgram(String prog, String mode) throws IOException; + + + //protected final Table filemt; + protected final LuaTable filemethods; + private final LuaValue linesiter; + + private File infile = null; + private File outfile = null; + private File errfile = null; + + private static final LuaValue STDIN = valueOf("stdin"); + private static final LuaValue STDOUT = valueOf("stdout"); + private static final LuaValue STDERR = valueOf("stderr"); + private static final LuaValue FILE = valueOf("file"); + private static final LuaValue CLOSED_FILE = valueOf("closed file"); + + public IoLib() { + + // io lib functions + set("flush", new IoFuncV("flush",0)); + set("tmpfile", new IoFuncV("tmpfile",1)); + set("close", new IoFuncV("close",2)); + set("input", new IoFuncV("input",3)); + set("output", new IoFuncV("output",4)); + set("type", new IoFuncV("type",5)); + set("popen", new IoFuncV("popen",6)); + set("open", new IoFuncV("open",7)); + set("lines", new IoFuncV("lines",8)); + set("read", new IoFuncV("read",9)); + set("write", new IoFuncV("write",10)); + setmetatable( tableOf(new LuaValue[] { + valueOf("__index"),new IoFuncV("__index",11), + }) ); + + // create file metatable + filemethods = tableOf(new LuaValue[] { + valueOf("close"), new IoFuncV("close",12), + valueOf("flush"), new IoFuncV("flush",13), + valueOf("setvbuf"), new IoFuncV("setvbuf",14), + valueOf("lines"), new IoFuncV("lines",15), + valueOf("read"), new IoFuncV("read",16), + valueOf("seek"), new IoFuncV("seek",17), + valueOf("write"), new IoFuncV("write",18), + }); + //filemt = tableOf(new Value[]{valueOf("__index"),filemethods}); + + // lines iterator + linesiter = new IoFuncV("linesiter",19); + } + + public class IoFuncV extends VarArgFunction { + public IoFuncV(String name, int opcode) { + super(name,opcode,IoLib.this); + } + public Varargs invoke(Varargs args) { + File f; + int n; + LuaValue v; + try { + switch ( opcode ) { + case 0: // io.flush() -> bool + checkopen(output()); + outfile.flush(); + return LuaValue.TRUE; + case 1: // io.tmpfile() -> file + return tmpFile(); + case 2: // io.close([file]) -> void + f = args.arg1().isnil()? output(): checkfile(args.arg1()); + checkopen(f); + return ioclose(f); + case 3: // io.input([file]) -> file + infile = args.arg1().isnil()? input(): args.arg1().isstring()? + ioopenfile(args.checkString(1),"r"): + checkfile(args.arg1()); + return infile; + + case 4: // io.output(filename) -> file + outfile = args.arg1().isnil()? output(): args.arg1().isstring()? + ioopenfile(args.checkString(1),"w"): + checkfile(args.arg1()); + return outfile; + case 5: // io.type(obj) -> "file" | "closed file" | nil + if ( (f=optfile(args.arg1())) != null ) + return f.isclosed()? CLOSED_FILE: FILE; + return NIL; + case 6: // io.popen(prog, [mode]) -> file + return openProgram(args.checkString(1),args.optString(2,"r")); + case 7: // io.open(filename, [mode]) -> file | nil,err + return rawopenfile(args.checkString(1), args.optString(2,"r")); + case 8: // io.lines(filename) -> iterator + infile = args.arg1().isnil()? input(): ioopenfile(args.checkString(1),"r"); + checkopen(infile); + return lines(infile); + case 9: // io.read(...) -> (...) + checkopen(infile); + return ioread(infile,args); + case 10: // io.write(...) -> void + checkopen(output()); + return iowrite(outfile,args); + case 11: // __index, returns a field + v = args.arg(2); + return v.equals(STDOUT)?output(): + v.equals(STDIN)? input(): + v.equals(STDERR)? errput(): NIL; + + // ------------ file metatable operations + + case 12: // file:close() -> void + return ioclose(checkfile(args.arg1())); + case 13: // file:flush() -> void + checkfile(args.arg1()).flush(); + return LuaValue.TRUE; + case 14: // file:setvbuf(mode,[size]) -> void + f = checkfile(args.arg1()); + f.setvbuf(args.checkString(2),args.optint(3, 1024)); + return LuaValue.TRUE; + case 15: // file:lines() -> iterator + return lines(checkfile(args.arg1())); + case 16: // file:read(...) -> (...) + f = checkfile(args.arg1()); + return ioread(f,args.subargs(2)); + case 17: // file:seek([whence][,offset]) -> pos | nil,error + f = checkfile(args.arg1()); + n = f.seek(args.optString(2,"cur"),args.optint(3,0)); + return valueOf(n); + case 18: // file:write(...) -> void + f = checkfile(args.arg1()); + return iowrite(f,args.subargs(2)); + case 19: // lines iterator(s,var) -> var' + f = checkfile(args.arg1()); + return freadline(f); + } + } catch ( IOException ioe ) { + return errorresult(ioe); + } + return NONE; + } + } + + private File input() { + return infile!=null? infile: (infile=ioopenfile("-","r")); + } + + private File output() { + return outfile!=null? outfile: (outfile=ioopenfile("-","w")); + } + + private File errput() { + return errfile!=null? errfile: (errfile=ioopenfile("-","w")); + } + + private File ioopenfile(String filename, String mode) { + try { + return rawopenfile(filename, mode); + } catch ( Exception e ) { + error("io error: "+e.getMessage()); + return null; + } + } + + private static Varargs ioclose(File f) throws IOException { + if ( f.isstdfile() ) + return errorresult("cannot close standard file"); + else { + f.close(); + return successresult(); + } + } + + private static Varargs successresult() { + return LuaValue.TRUE; + } + + private static Varargs errorresult(IOException ioe) { + String s = ioe.getMessage(); + return errorresult("io error: "+(s!=null? s: ioe.toString())); + } + + private static Varargs errorresult(String errortext) { + return varargsOf(NIL, valueOf(errortext)); + } + + // TODO: how to close on finalization + private Varargs lines(final File f) throws IOException { + return varargsOf( linesiter, f ); + } + + private static Varargs iowrite(File f, Varargs args) throws IOException { + for ( int i=1, n=args.narg(); i<=n; i++ ) + f.write( args.checkstring(i) ); + return LuaValue.TRUE; + } + + private Varargs ioread(File f, Varargs args) throws IOException { + int i,n=args.narg(); + LuaValue[] v = new LuaValue[n]; + for ( i=0; i 0; + boolean isbinary = mode.endsWith("b"); + return openFile( filename, isreadmode, isappend, isupdate, isbinary ); + } + + + // ------------- file reading utilitied ------------------ + + public static LuaValue freadbytes(File f, int count) throws IOException { + byte[] b = new byte[count]; + int r; + if ( ( r = f.read(b,0,b.length) ) < 0 ) + return NIL; + return valueOf(b, 0, r); + } + public static LuaValue freaduntil(File f,int delim) throws IOException { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + int c; + try { + while ( true ) { + c = f.read(); + if ( c < 0 || c == delim ) + break; + baos.write(c); + } + } catch ( EOFException e ) { + c = -1; + } + return ( c < 0 && baos.size() == 0 )? + (LuaValue) NIL: + (LuaValue) valueOf(baos.toByteArray()); + } + public static LuaValue freadline(File f) throws IOException { + return freaduntil(f,'\n'); + } + public static LuaValue freadall(File f) throws IOException { + int n = f.remaining(); + if ( n >= 0 ) { + return freadbytes(f, n); + } else { + return freaduntil(f,-1); + } + } + public static double freadnumber(File f) throws IOException { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + freadchars(f," \t\r\n",null); + freadchars(f,"-+",baos); + //freadchars(f,"0",baos); + //freadchars(f,"xX",baos); + freadchars(f,"0123456789",baos); + freadchars(f,".",baos); + freadchars(f,"0123456789",baos); + //freadchars(f,"eEfFgG",baos); + // freadchars(f,"+-",baos); + //freadchars(f,"0123456789",baos); + String s = baos.toString(); + return s.length()>0? Double.parseDouble(s): 0; + } + private static void freadchars(File f, String chars, ByteArrayOutputStream baos) throws IOException { + int c; + while ( true ) { + c = f.peek(); + if ( chars.indexOf(c) < 0 ) { + return; + } + f.read(); + if ( baos != null ) + baos.write( c ); + } + } + + + +} diff --git a/src/core/org/luaj/vm2/lib/LibFunction.java b/src/core/org/luaj/vm2/lib/LibFunction.java new file mode 100644 index 00000000..b6b42b78 --- /dev/null +++ b/src/core/org/luaj/vm2/lib/LibFunction.java @@ -0,0 +1,76 @@ +/******************************************************************************* +* Copyright (c) 2009 Luaj.org. All rights reserved. +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +* THE SOFTWARE. +******************************************************************************/ +package org.luaj.vm2.lib; + +import org.luaj.vm2.LuaFunction; +import org.luaj.vm2.LuaTable; +import org.luaj.vm2.LuaValue; +; + + +abstract public class LibFunction extends LuaFunction { + + protected int opcode; + protected String name; + + public LibFunction() { + } + + public LibFunction(String name, int opcode, LuaValue env) { + super(env); + this.name = name; + this.opcode = opcode; + } + + public String toString() { + return name!=null? name: super.toString(); + } + + /** Bind a set of names to class instances, put values into the table. */ + public static LuaTable bind( LuaTable table, Class libFuncClass, String[] names ) { + try { + for ( int i=0, n=names.length; i=0? Math.floor(q): Math.ceil(q)); + return valueOf( f ); + } + case 1: { // ldexp + double x = arg1.checkdouble(); + double y = arg2.checkdouble()+1023.5; + long e = (long) ((0!=(1&((int)y)))? Math.floor(y): Math.ceil(y-1)); + return valueOf(x * Double.longBitsToDouble(e << 52)); + } + case 2: { // pow + return dpow(arg1.todouble(), arg2.todouble()); + } + case 3: { // random + MathLib lib = (MathLib) env; + if ( lib.random == null ) + lib.random = new Random(); + if ( arg1.isnil() ) + return valueOf( lib.random.nextDouble() ); + int m = arg1.toint(); + if ( arg2.isnil() ) + return valueOf( 1 + lib.random.nextInt(m) ); + else + return valueOf( m + lib.random.nextInt(arg2.toint()-m) ); + } + } + return NIL; + } + + } + + /** compute power using installed math library, or default if there is no math library installed */ + public static LuaValue dpow(double a, double b) { + return LuaDouble.valueOf( + MATHLIB!=null? + MATHLIB.dpow_d(a,b): + dpow_default(a,b) ); + } + + /** + * Hook to override default dpow behavior with faster implementation. + */ + public double dpow_d(double a, double b) { + return dpow_default(a,b); + } + + /** + * Default JME version computes using longhand heuristics. + */ + protected static double dpow_default(double a, double b) { + if ( b < 0 ) + return 1 / dpow_default( a, -b ); + double p = 1; + int whole = (int) b; + for ( double v=a; whole > 0; whole>>=1, v*=v ) + if ( (whole & 1) != 0 ) + p *= v; + if ( (b -= whole) > 0 ) { + int frac = (int) (0x10000 * b); + for ( ; (frac&0xffff)!=0; frac<<=1 ) { + a = Math.sqrt(a); + if ( (frac & 0x8000) != 0 ) + p *= a; + } + } + return p; + } + + public static class MathFuncV extends VarArgFunction { + public Varargs invoke(Varargs args) { + switch ( opcode ) { + case 0: { // frexp + double x = args.checkdouble(1); + if ( x == 0 ) return varargsOf(ZERO,ZERO); + long bits = Double.doubleToLongBits( x ); + double m = ((bits & (~(-1L<<52))) + (1L<<52)) * ((bits >= 0)? (.5 / (1L<<52)): (-.5 / (1L<<52))); + double e = (((int) (bits >> 52)) & 0x7ff) - 1022; + return varargsOf( valueOf(m), valueOf(e) ); + } + case 1: { // max + double m = args.checkdouble(1); + for ( int i=2,n=args.narg(); i<=n; ++i ) + m = Math.max(m,args.checkdouble(i)); + return valueOf(m); + } + case 2: { // min + double m = args.checkdouble(1); + for ( int i=2,n=args.narg(); i<=n; ++i ) + m = Math.min(m,args.checkdouble(i)); + return valueOf(m); + } + case 3: { // modf + double x = args.checkdouble(1); + double intPart = ( x > 0 ) ? Math.floor( x ) : Math.ceil( x ); + double fracPart = x - intPart; + return varargsOf( valueOf(intPart), valueOf(fracPart) ); + } + case 4: { // randomseed + long seed = args.checklong(1); + ((MathLib) env).random = new Random(seed); + return NONE; + } + } + return NONE; + } + } +} diff --git a/src/core/org/luaj/vm2/lib/OneArgFunction.java b/src/core/org/luaj/vm2/lib/OneArgFunction.java new file mode 100644 index 00000000..8a351573 --- /dev/null +++ b/src/core/org/luaj/vm2/lib/OneArgFunction.java @@ -0,0 +1,57 @@ +/******************************************************************************* +* Copyright (c) 2009 Luaj.org. All rights reserved. +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +* THE SOFTWARE. +******************************************************************************/ +package org.luaj.vm2.lib; + +import org.luaj.vm2.LuaValue; +import org.luaj.vm2.Varargs; + +abstract public class OneArgFunction extends LibFunction { + + abstract public LuaValue call(LuaValue arg); + + public OneArgFunction() { + } + + public OneArgFunction( LuaValue env ) { + this.env = env; + } + + public OneArgFunction( String name, int opcode, LuaValue env ) { + super(name, opcode, env); + } + + public final LuaValue call() { + return call(NIL); + } + + public final LuaValue call(LuaValue arg1, LuaValue arg2) { + return call(arg1); + } + + public LuaValue call(LuaValue arg1, LuaValue arg2, LuaValue arg3) { + return call(arg1); + } + + public Varargs invoke(Varargs varargs) { + return call(varargs.arg1()); + } +} diff --git a/src/core/org/luaj/vm2/lib/OsLib.java b/src/core/org/luaj/vm2/lib/OsLib.java new file mode 100644 index 00000000..a400fcda --- /dev/null +++ b/src/core/org/luaj/vm2/lib/OsLib.java @@ -0,0 +1,292 @@ +/******************************************************************************* +* Copyright (c) 2009 Luaj.org. All rights reserved. +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +* THE SOFTWARE. +******************************************************************************/ +package org.luaj.vm2.lib; + +import java.io.IOException; + +import org.luaj.vm2.LuaTable; +import org.luaj.vm2.LuaValue; +import org.luaj.vm2.Varargs; + +/** + * Base implementation of OsLib, with simplified stub functions + * for library functions that cannot be implemented uniformly + * on J2se and J2me. + * + *

+ * This can be installed as-is on either platform, or extended + * and refined to be used in a complete J2se implementation. + * + *

Contains limited implementations of features not supported well on J2me: + * + *

  • execute()
  • + *
  • remove()
  • + *
  • rename()
  • + *
  • tmpname()
  • + * + * + * @see org.luaj.vm2.lib.jse.JseOsLib + */ +public class OsLib extends LuaTable { + public static String TMP_PREFIX = ".luaj"; + public static String TMP_SUFFIX = "tmp"; + + private static final int CLOCK = 0; + private static final int DATE = 1; + private static final int DIFFTIME = 2; + private static final int EXECUTE = 3; + private static final int EXIT = 4; + private static final int GETENV = 5; + private static final int REMOVE = 6; + private static final int RENAME = 7; + private static final int SETLOCALE = 8; + private static final int TIME = 9; + private static final int TMPNAME = 10; + + private static final long t0 = System.currentTimeMillis(); + private static long tmpnames = t0; + + /** + * Create and OsLib instance. + */ + public OsLib() { + String[] NAMES = { + "clock", + "date", + "difftime", + "execute", + "exit", + "getenv", + "remove", + "rename", + "setlocale", + "time", + "tmpname", + }; + for ( int i=NAMES.length; --i>=0; ) + set(NAMES[i], new OsFuncV(NAMES[i], i, this)); + } + + public String toString() { + return "os"; + } + + private class OsFuncV extends VarArgFunction { + public OsFuncV(String name, int opcode, OsLib lib) { + super(name, opcode, lib); + } + public Varargs invoke(Varargs args) { + try { + switch ( opcode ) { + case CLOCK: + return valueOf(clock()); + case DATE: { + String s = args.optString(1, null); + long t = args.optlong(2,-1); + return valueOf( date(s, t==-1? System.currentTimeMillis(): t) ); + } + case DIFFTIME: + return valueOf(difftime(args.checklong(1),args.checklong(2))); + case EXECUTE: + return valueOf(execute(args.optString(1, null))); + case EXIT: + exit(args.optint(1, 0)); + return NONE; + case GETENV: { + final String val = getenv(args.checkString(1)); + return val!=null? valueOf(val): NIL; + } + case REMOVE: + remove(args.checkString(1)); + return LuaValue.TRUE; + case RENAME: + rename(args.checkString(1), args.checkString(2)); + return LuaValue.TRUE; + case SETLOCALE: { + String s = setlocale(args.optString(1,null), args.optString(2, "all")); + return s!=null? valueOf(s): NIL; + } + case TIME: + return valueOf(time(args.arg1().isnil()? null: args.checktable(1))); + case TMPNAME: + return valueOf(tmpname()); + } + return NONE; + } catch ( IOException e ) { + return varargsOf(NIL, valueOf(e.getMessage())); + } + } + } + + /** + * @return an approximation of the amount in seconds of CPU time used by + * the program. + */ + protected double clock() { + return (System.currentTimeMillis()-t0) / 1000.; + } + + /** + * Returns the number of seconds from time t1 to time t2. + * In POSIX, Windows, and some other systems, this value is exactly t2-t1. + * @param t2 + * @param t1 + * @return diffeence in time values, in seconds + */ + protected double difftime(long t2, long t1) { + return (t2 - t1) / 1000.; + } + + /** + * If the time argument is present, this is the time to be formatted + * (see the os.time function for a description of this value). + * Otherwise, date formats the current time. + * + * If format starts with '!', then the date is formatted in Coordinated + * Universal Time. After this optional character, if format is the string + * "*t", then date returns a table with the following fields: year + * (four digits), month (1--12), day (1--31), hour (0--23), min (0--59), + * sec (0--61), wday (weekday, Sunday is 1), yday (day of the year), + * and isdst (daylight saving flag, a boolean). + * + * If format is not "*t", then date returns the date as a string, + * formatted according to the same rules as the C function strftime. + * + * When called without arguments, date returns a reasonable date and + * time representation that depends on the host system and on the + * current locale (that is, os.date() is equivalent to os.date("%c")). + * + * @param format + * @param time time since epoch, or -1 if not supplied + * @return a LString or a LTable containing date and time, + * formatted according to the given string format. + */ + protected String date(String format, long time) { + return new java.util.Date(time).toString(); + } + + /** + * This function is equivalent to the C function system. + * It passes command to be executed by an operating system shell. + * It returns a status code, which is system-dependent. + * If command is absent, then it returns nonzero if a shell + * is available and zero otherwise. + * @param command command to pass to the system + */ + protected int execute(String command) { + return 0; + } + + /** + * Calls the C function exit, with an optional code, to terminate the host program. + * @param code + */ + protected void exit(int code) { + System.exit(code); + } + + /** + * Returns the value of the process environment variable varname, + * or null if the variable is not defined. + * @param varname + * @return String value, or null if not defined + */ + protected String getenv(String varname) { + return System.getProperty(varname); + } + + /** + * Deletes the file or directory with the given name. + * Directories must be empty to be removed. + * If this function fails, it throws and IOException + * + * @param filename + * @throws IOException if it fails + */ + protected void remove(String filename) throws IOException { + throw new IOException( "not implemented" ); + } + + /** + * Renames file or directory named oldname to newname. + * If this function fails,it throws and IOException + * + * @param oldname old file name + * @param newname new file name + * @throws IOException if it fails + */ + protected void rename(String oldname, String newname) throws IOException { + throw new IOException( "not implemented" ); + } + + /** + * Sets the current locale of the program. locale is a string specifying + * a locale; category is an optional string describing which category to change: + * "all", "collate", "ctype", "monetary", "numeric", or "time"; the default category + * is "all". + * + * If locale is the empty string, the current locale is set to an implementation- + * defined native locale. If locale is the string "C", the current locale is set + * to the standard C locale. + * + * When called with null as the first argument, this function only returns the + * name of the current locale for the given category. + * + * @param locale + * @param category + * @return the name of the new locale, or null if the request + * cannot be honored. + */ + protected String setlocale(String locale, String category) { + return null; + } + + /** + * Returns the current time when called without arguments, + * or a time representing the date and time specified by the given table. + * This table must have fields year, month, and day, + * and may have fields hour, min, sec, and isdst + * (for a description of these fields, see the os.date function). + * @param table + * @return long value for the time + */ + protected long time(LuaTable table) { + return System.currentTimeMillis(); + } + + /** + * Returns a string with a file name that can be used for a temporary file. + * The file must be explicitly opened before its use and explicitly removed + * when no longer needed. + * + * On some systems (POSIX), this function also creates a file with that name, + * to avoid security risks. (Someone else might create the file with wrong + * permissions in the time between getting the name and creating the file.) + * You still have to open the file to use it and to remove it (even if you + * do not use it). + * + * @return String filename to use + */ + protected String tmpname() { + return TMP_PREFIX+(tmpnames++)+TMP_SUFFIX; + } +} diff --git a/src/core/org/luaj/vm2/lib/PackageLib.java b/src/core/org/luaj/vm2/lib/PackageLib.java new file mode 100644 index 00000000..ff14c4e6 --- /dev/null +++ b/src/core/org/luaj/vm2/lib/PackageLib.java @@ -0,0 +1,366 @@ +/******************************************************************************* +* Copyright (c) 200 Luaj.org. All rights reserved. +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +* THE SOFTWARE. +******************************************************************************/ +package org.luaj.vm2.lib; + +import java.io.InputStream; +import java.io.PrintStream; + +import org.luaj.vm2.LuaFunction; +import org.luaj.vm2.LuaThread; +import org.luaj.vm2.LuaString; +import org.luaj.vm2.LuaTable; +import org.luaj.vm2.LuaValue; +import org.luaj.vm2.Varargs; + + +public class PackageLib extends LuaTable { + + public static String DEFAULT_LUA_PATH = "?.lua"; + + public LuaValue _G = null; + public InputStream STDIN = null; + public PrintStream STDOUT = System.out; + public LuaTable LOADED = null; + public LuaTable PACKAGE = null; + + private static final LuaString _M = LuaString.valueOf("_M"); + private static final LuaString _NAME = LuaString.valueOf("_NAME"); + private static final LuaString _PACKAGE = LuaString.valueOf("_PACKAGE"); + private static final LuaString _DOT = LuaString.valueOf("."); + private static final LuaString _LOADERS = LuaString.valueOf("loaders"); + private static final LuaString _LOADED = LuaString.valueOf("loaded"); + private static final LuaString _LOADLIB = LuaString.valueOf("loadlib"); + private static final LuaString _PRELOAD = LuaString.valueOf("preload"); + private static final LuaString _PATH = LuaString.valueOf("path"); + private static final LuaString _SEEALL = LuaString.valueOf("seeall"); + private static final LuaValue _SENTINEL = EMPTYSTRING; + + private static final int MODULE = 1; + private static final int REQUIRE = 2; + private static final int LOADLIB = 3; + private static final int SEEALL = 4; + private static final int PRELOAD_LOADER = 5; + private static final int LUA_LOADER = 6; + private static final int JAVA_LOADER = 7; + + + public PackageLib(LuaValue _G) { + this._G = _G; + _G.set( "module", new PackageFuncV(MODULE, "module", _G) ); + _G.set( "require", new PackageFuncV(REQUIRE, "require",_G) ); + _G.set( "package", PACKAGE = tableOf( new LuaValue[] { + _LOADED, LOADED=tableOf(), + _PRELOAD, tableOf(), + _PATH, valueOf(DEFAULT_LUA_PATH), + _LOADLIB, new PackageFuncV(LOADLIB, "loadlib",_G), + _SEEALL, new PackageFuncV(SEEALL, "seeall",_G), + _LOADERS, listOf(new LuaValue[] { + new PackageFuncV(PRELOAD_LOADER,"preload_loader",_G), + new PackageFuncV(LUA_LOADER, "lua_loader",_G), + new PackageFuncV(JAVA_LOADER, "java_loader",_G), + }), + } ) ); + } + + private class PackageFuncV extends VarArgFunction { + public PackageFuncV(int opcode, String name, LuaValue env) { + super(name, opcode, env); + } + public Varargs invoke(Varargs args) { + switch ( opcode ) { + case MODULE: + return module(args); + case REQUIRE: + return require(args); + case LOADLIB: + return loadlib(args); + case SEEALL: { + LuaTable t = args.checktable(1); + LuaValue m = t.getmetatable(); + if ( m == null ) + t.setmetatable(m=tableOf()); + m.set( INDEX, env ); + return NONE; + } + case PRELOAD_LOADER: { + return loader_preload(args); + } + case LUA_LOADER: { + return loader_Lua(args); + } + case JAVA_LOADER: { + return loader_Java(args); + } + } + return NONE; + } + } + + /** Allow packages to mark themselves as loaded */ + public void setIsLoaded(String name, LuaTable value) { + LOADED.set(name, value); + } + + public void setLuaPath( String newLuaPath ) { + PACKAGE.set( _PATH, valueOf(newLuaPath) ); + } + + public String toString() { + return "package"; + } + + + // ======================== Module, Package loading ============================= + /** + * module (name [, ...]) + * + * Creates a module. If there is a table in package.loaded[name], this table + * is the module. Otherwise, if there is a global table t with the given + * name, this table is the module. Otherwise creates a new table t and sets + * it as the value of the global name and the value of package.loaded[name]. + * This function also initializes t._NAME with the given name, t._M with the + * module (t itself), and t._PACKAGE with the package name (the full module + * name minus last component; see below). Finally, module sets t as the new + * environment of the current function and the new value of + * package.loaded[name], so that require returns t. + * + * If name is a compound name (that is, one with components separated by + * dots), module creates (or reuses, if they already exist) tables for each + * component. For instance, if name is a.b.c, then module stores the module + * table in field c of field b of global a. + * + * This function may receive optional options after the module name, where + * each option is a function to be applied over the module. + */ + public Varargs module(Varargs args) { + LuaString modname = args.checkstring(1); + int n = args.narg(); + LuaValue value = LOADED.get(modname); + LuaValue module; + if ( ! value.istable() ) { /* not found? */ + + /* try global variable (and create one if it does not exist) */ + module = findtable( _G, modname ); + if ( module == null ) + error( "name conflict for module '"+modname+"'" ); + LOADED.set(modname, module); + } else { + module = (LuaTable) value; + } + + + /* check whether table already has a _NAME field */ + LuaValue name = module.get(_NAME); + if ( name.isnil() ) { + modinit( module, modname ); + } + + // set the environment of the current function + LuaFunction f = LuaThread.getCallstackFunction(0); + if ( f == null ) + error(1, "no calling function"); + f.setfenv(module); + + // apply the functions + for ( int i=2; i<=n; i++ ) + args.arg(i).call( module ); + + // returns no results + return NONE; + } + + /** + * + * @param table the table at which to start the search + * @param fname the name to look up or create, such as "abc.def.ghi" + * @return the table for that name, possible a new one, or null if a non-table has that name already. + */ + private static LuaValue findtable(LuaValue table, LuaString fname) { + int b, e=(-1); + do { + e = fname.indexOf(_DOT, b=e+1 ); + if ( e < 0 ) + e = fname.m_length; + LuaString key = fname.substring(b, e); + LuaValue val = table.get(key); + if ( val.isnil() ) { /* no such field? */ + LuaTable field = new LuaTable(); /* new table for field */ + table.set(key, field); + table = field; + } else if ( ! val.istable() ) { /* field has a non-table value? */ + return NIL; + } else { + table = val; + } + } while ( e < fname.m_length ); + return table; + } + + private static void modinit(LuaValue module, LuaString modname) { + /* module._M = module */ + module.set(_M, module); + int e = modname.lastIndexOf(_DOT); + module.set(_NAME, modname ); + module.set(_PACKAGE, (e<0? EMPTYSTRING: modname.substring(0,e+1)) ); + } + + /** + * require (modname) + * + * Loads the given module. The function starts by looking into the package.loaded table to + * determine whether modname is already loaded. If it is, then require returns the value + * stored at package.loaded[modname]. Otherwise, it tries to find a loader for the module. + * + * To find a loader, require is guided by the package.loaders array. By changing this array, + * we can change how require looks for a module. The following explanation is based on the + * default configuration for package.loaders. + * + * First require queries package.preload[modname]. If it has a value, this value + * (which should be a function) is the loader. Otherwise require searches for a Lua loader + * using the path stored in package.path. If that also fails, it searches for a C loader + * using the path stored in package.cpath. If that also fails, it tries an all-in-one loader + * (see package.loaders). + * + * Once a loader is found, require calls the loader with a single argument, modname. + * If the loader returns any value, require assigns the returned value to package.loaded[modname]. + * If the loader returns no value and has not assigned any value to package.loaded[modname], + * then require assigns true to this entry. In any case, require returns the final value of + * package.loaded[modname]. + * + * If there is any error loading or running the module, or if it cannot find any loader for + * the module, then require signals an error. + */ + public Varargs require( Varargs args ) { + LuaString name = args.checkstring(1); + LuaValue loaded = LOADED.get(name); + if ( loaded.toboolean() ) { + if ( loaded == _SENTINEL ) + error("loop or previous error loading module '"+name+"'"); + return loaded; + } + + /* else must load it; iterate over available loaders */ + LuaTable tbl = PACKAGE.get(_LOADERS).checktable(); + StringBuffer sb = new StringBuffer(); + LuaValue chunk = null; + for ( int i=1; true; i++ ) { + LuaValue loader = tbl.get(i); + if ( loader.isnil() ) { + error( "module '"+name+"' not found: "+name+"\n"+sb ); + } + + /* call loader with module name as argument */ + chunk = loader.call(name); + if ( chunk.isfunction() ) + break; + if ( chunk.isstring() ) + sb.append( chunk.toString() ); + } + + // load the module using the loader + LOADED.set(name, _SENTINEL); + LuaValue result = chunk.call(name); + if ( ! result.isnil() ) + LOADED.set( name, result ); + else if ( LOADED.get(name) == _SENTINEL ) + LOADED.set( name, result = LuaValue.TRUE ); + return result; + } + + public static Varargs loadlib( Varargs args ) { + return varargsOf(NIL, valueOf("dynamic libraries not enabled"), valueOf("absent")); + } + + private LuaValue loader_preload( Varargs args ) { + LuaString name = args.checkstring(1); + LuaValue preload = PACKAGE.get(_PRELOAD).checktable(); + LuaValue val = preload.get(name); + return val.isnil()? + valueOf("\n\tno field package.preload['"+name+"']"): + val; + } + + private LuaValue loader_Lua( Varargs args ) { + String name = args.checkString(1); + InputStream is = null; + + // try to use loadfile for the file + LuaValue loadfile = _G.get("loadfile"); + if ( ! loadfile.isfunction() ) + return valueOf("loadfile is not a function" ); + + + // get package path + LuaValue pp = PACKAGE.get(_PATH); + if ( ! pp.isstring() ) + return valueOf("package.path is not a string"); + String path = pp.toString(); + + // check the path elements + int e = -1; + int n = path.length(); + StringBuffer sb = null; + name = name.replace('.','/'); + while ( e < n ) { + + // find next template + int b = e+1; + e = path.indexOf(';',b); + if ( e < 0 ) + e = path.length(); + String template = path.substring(b,e); + + // create filename + int q = template.indexOf('?'); + String filename = template; + if ( q >= 0 ) { + filename = template.substring(0,q) + name + template.substring(q+1); + } + + // try loading the file + Varargs v = loadfile.invoke(valueOf(filename)); + if ( v.arg1().isfunction() ) + return v.arg1(); + + // report error + if ( sb == null ) + sb = new StringBuffer(); + sb.append( "\n\t'"+filename+"': "+v.arg(2) ); + } + return valueOf(sb.toString()); + } + + private LuaValue loader_Java( Varargs args ) { + String name = args.checkString(1); + Class c = null; + LuaValue v = null; + try { + c = Class.forName(name); + v = (LuaValue) c.newInstance(); + return v; + } catch ( ClassNotFoundException cnfe ) { + return valueOf("\n\tno class '"+name+"'" ); + } catch ( Exception e ) { + return valueOf("\n\tjava load failed on '"+name+"', "+e ); + } + } +} diff --git a/src/core/org/luaj/vm2/lib/ResourceFinder.java b/src/core/org/luaj/vm2/lib/ResourceFinder.java new file mode 100644 index 00000000..72d6cb57 --- /dev/null +++ b/src/core/org/luaj/vm2/lib/ResourceFinder.java @@ -0,0 +1,50 @@ +/******************************************************************************* + * Copyright (c) 2009 Luaj.org. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + ******************************************************************************/ +package org.luaj.vm2.lib; + +import java.io.InputStream; + +/** + * Interface for opening application resource files such as scripts sources. + * + * This is used by required to load files that are part of + * the application, and implemented by BaseLib + * for both the Jme and Jse platforms. + * + * The Jme version implements FileOpener via getResourceAsStream(), + * while the Jse version implements it using new File(). + * + * The io library does not use this API for file manipulation. + */ +public interface ResourceFinder { + + /** + * Try to open a file, or return null if not found. + * + * @see org.luaj.vm2.lib.BaseLib + * @see org.luaj.vm2.lib.jse.JseBaseLib + * + * @param filename + * @return InputStream, or null if not found. + */ + public InputStream findResource( String filename ); +} \ No newline at end of file diff --git a/src/core/org/luaj/vm2/lib/StringLib.java b/src/core/org/luaj/vm2/lib/StringLib.java new file mode 100644 index 00000000..f1c41657 --- /dev/null +++ b/src/core/org/luaj/vm2/lib/StringLib.java @@ -0,0 +1,1139 @@ +/******************************************************************************* +* Copyright (c) 2009 Luaj.org. All rights reserved. +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +* THE SOFTWARE. +******************************************************************************/ +package org.luaj.vm2.lib; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; + +import org.luaj.vm2.LuaClosure; +import org.luaj.vm2.Buffer; +import org.luaj.vm2.LuaString; +import org.luaj.vm2.LuaTable; +import org.luaj.vm2.LuaValue; +import org.luaj.vm2.Varargs; +import org.luaj.vm2.compiler.DumpState; + +public class StringLib extends LuaTable { + + public static StringLib instance; + + public StringLib() { + LibFunction.bind( this, new StringFunc1().getClass(), new String[] { + "dump", "len", "lower", "reverse", + "upper", } ); + LibFunction.bind( this, new StringFuncV().getClass(), new String[] { + "byte", "char", "find", "format", + "gmatch", "gsub", "match", "rep", + "sub"} ); + instance = this; + } + + public static class StringFunc1 extends OneArgFunction { + public LuaValue call(LuaValue arg) { + switch ( opcode ) { + case 0: return StringLib.dump(arg); // dump (function) + case 1: return StringLib.len(arg); // len (function) + case 2: return StringLib.lower(arg); // lower (function) + case 3: return StringLib.reverse(arg); // reverse (function) + case 4: return StringLib.upper(arg); // upper (function) + } + return NIL; + } + } + + public static class StringFuncV extends VarArgFunction { + public Varargs invoke(Varargs args) { + switch ( opcode ) { + case 0: return StringLib.byte_( args ); + case 1: return StringLib.char_( args ); + case 2: return StringLib.find( args ); + case 3: return StringLib.format( args ); + case 4: return StringLib.gmatch( args ); + case 5: return StringLib.gsub( args ); + case 6: return StringLib.match( args ); + case 7: return StringLib.rep( args ); + case 8: return StringLib.sub( args ); + } + return NONE; + } + } + + /** + * string.byte (s [, i [, j]]) + * + * Returns the internal numerical codes of the + * characters s[i], s[i+1], ..., s[j]. The default value for i is 1; the + * default value for j is i. + * + * Note that numerical codes are not necessarily portable across platforms. + * + * @param args the calling args + */ + static Varargs byte_( Varargs args ) { + LuaString s = args.checkstring(1); + int l = s.m_length; + int posi = posrelat( args.optint(2,1), l ); + int pose = posrelat( args.optint(3,posi), l ); + int n,i; + if (posi <= 0) posi = 1; + if (pose > l) pose = l; + if (posi > pose) return NONE; /* empty interval; return no values */ + n = (int)(pose - posi + 1); + if (posi + n <= pose) /* overflow? */ + error("string slice too long"); + LuaValue[] v = new LuaValue[n]; + for (i=0; i=256) error(a, "invalid value"); + bytes[i] = (byte) c; + } + return valueOf( bytes ); + } + + /** + * string.dump (function) + * + * Returns a string containing a binary representation of the given function, + * so that a later loadstring on this string returns a copy of the function. + * function must be a Lua function without upvalues. + * + * TODO: port dumping code as optional add-on + */ + static LuaValue dump( LuaValue arg ) { + LuaValue f = arg.checkfunction(); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + try { + DumpState.dump( ((LuaClosure)f).p, baos, true ); + return valueOf(baos.toByteArray()); + } catch (IOException e) { + return error( e.getMessage() ); + } + } + + /** + * string.find (s, pattern [, init [, plain]]) + * + * Looks for the first match of pattern in the string s. + * If it finds a match, then find returns the indices of s + * where this occurrence starts and ends; otherwise, it returns nil. + * A third, optional numerical argument init specifies where to start the search; + * its default value is 1 and may be negative. A value of true as a fourth, + * optional argument plain turns off the pattern matching facilities, + * so the function does a plain "find substring" operation, + * with no characters in pattern being considered "magic". + * Note that if plain is given, then init must be given as well. + * + * If the pattern has captures, then in a successful match the captured values + * are also returned, after the two indices. + */ + static Varargs find( Varargs args ) { + return str_find_aux( args, true ); + } + + /** + * string.format (formatstring, ...) + * + * Returns a formatted version of its variable number of arguments following + * the description given in its first argument (which must be a string). + * The format string follows the same rules as the printf family of standard C functions. + * The only differences are that the options/modifiers *, l, L, n, p, and h are not supported + * and that there is an extra option, q. The q option formats a string in a form suitable + * to be safely read back by the Lua interpreter: the string is written between double quotes, + * and all double quotes, newlines, embedded zeros, and backslashes in the string are correctly + * escaped when written. For instance, the call + * string.format('%q', 'a string with "quotes" and \n new line') + * + * will produce the string: + * "a string with \"quotes\" and \ + * new line" + * + * The options c, d, E, e, f, g, G, i, o, u, X, and x all expect a number as argument, + * whereas q and s expect a string. + * + * This function does not accept string values containing embedded zeros, + * except as arguments to the q option. + */ + static Varargs format( Varargs args ) { + LuaString fmt = args.checkstring( 1 ); + final int n = fmt.length(); + Buffer result = new Buffer(n); + int arg = 1; + int c; + + for ( int i = 0; i < n; ) { + switch ( c = fmt.luaByte( i++ ) ) { + case '\n': + // TODO: dos only? + result.append( "\r\n" ); + break; + default: + result.append( (byte) c ); + break; + case L_ESC: + if ( i < n ) { + if ( ( c = fmt.luaByte( i ) ) == L_ESC ) { + ++i; + result.append( (byte)L_ESC ); + } else { + arg++; + FormatDesc fdsc = new FormatDesc(args, fmt, i ); + i += fdsc.length; + switch ( fdsc.conversion ) { + case 'c': + fdsc.format( result, (byte)args.checkint( arg ) ); + break; + case 'i': + case 'd': + fdsc.format( result, args.checkint( arg ) ); + break; + case 'o': + case 'u': + case 'x': + case 'X': + fdsc.format( result, args.checklong( arg ) ); + break; + case 'e': + case 'E': + case 'f': + case 'g': + case 'G': + fdsc.format( result, args.checkdouble( arg ) ); + break; + case 'q': + addquoted( result, args.checkstring( arg ) ); + break; + case 's': { + LuaString s = args.checkstring( arg ); + if ( fdsc.precision == -1 && s.length() >= 100 ) { + result.append( s ); + } else { + fdsc.format( result, s ); + } + } break; + default: + error("invalid option '%"+(char)fdsc.conversion+"' to 'format'"); + break; + } + } + } + } + } + + return result.tostrvalue(); + } + + private static void addquoted(Buffer buf, LuaString s) { + int c; + buf.append( (byte) '"' ); + for ( int i = 0, n = s.length(); i < n; i++ ) { + switch ( c = s.luaByte( i ) ) { + case '"': case '\\': + buf.append( (byte)'\\' ); + buf.append( (byte)c ); + break; + case '\n': + // TODO: dos only? + buf.append( "\\\r\n" ); + break; + case '\r': + buf.append( "\\r" ); + break; + case '\0': + buf.append( "\\000" ); + break; + default: + buf.append( (byte) c ); + break; + } + } + buf.append( (byte) '"' ); + } + + private static final String FLAGS = "-+ #0"; + + private static class FormatDesc { + + private boolean leftAdjust; + private boolean zeroPad; + private boolean explicitPlus; + private boolean space; + private boolean alternateForm; + private static final int MAX_FLAGS = 5; + + private int width; + private int precision; + + public final int conversion; + public final int length; + + public FormatDesc(Varargs args, LuaString strfrmt, final int start) { + int p = start, n = strfrmt.length(); + int c = 0; + + boolean moreFlags = true; + while ( moreFlags ) { + switch ( c = ( (p < n) ? strfrmt.luaByte( p++ ) : 0 ) ) { + case '-': leftAdjust = true; break; + case '+': explicitPlus = true; break; + case ' ': space = true; break; + case '#': alternateForm = true; break; + case '0': zeroPad = true; break; + default: moreFlags = false; break; + } + } + if ( p - start > MAX_FLAGS ) + error("invalid format (repeated flags)"); + + width = -1; + if ( Character.isDigit( (char)c ) ) { + width = c - '0'; + c = ( (p < n) ? strfrmt.luaByte( p++ ) : 0 ); + if ( Character.isDigit( (char) c ) ) { + width = width * 10 + (c - '0'); + c = ( (p < n) ? strfrmt.luaByte( p++ ) : 0 ); + } + } + + precision = -1; + if ( c == '.' ) { + c = ( (p < n) ? strfrmt.luaByte( p++ ) : 0 ); + if ( Character.isDigit( (char) c ) ) { + precision = c - '0'; + c = ( (p < n) ? strfrmt.luaByte( p++ ) : 0 ); + if ( Character.isDigit( (char) c ) ) { + precision = precision * 10 + (c - '0'); + c = ( (p < n) ? strfrmt.luaByte( p++ ) : 0 ); + } + } + } + + if ( Character.isDigit( (char) c ) ) + error("invalid format (width or precision too long)"); + + zeroPad &= !leftAdjust; // '-' overrides '0' + conversion = c; + length = p - start; + } + + public void format(Buffer buf, byte c) { + // TODO: not clear that any of width, precision, or flags apply here. + buf.append(c); + } + + public void format(Buffer buf, long number) { + String digits; + + if ( number == 0 && precision == 0 ) { + digits = ""; + } else { + int radix; + switch ( conversion ) { + case 'x': + case 'X': + radix = 16; + break; + case 'o': + radix = 8; + break; + default: + radix = 10; + break; + } + digits = Long.toString( number, radix ); + if ( conversion == 'X' ) + digits = digits.toUpperCase(); + } + + int minwidth = digits.length(); + int ndigits = minwidth; + int nzeros; + + if ( number < 0 ) { + ndigits--; + } else if ( explicitPlus || space ) { + minwidth++; + } + + if ( precision > ndigits ) + nzeros = precision - ndigits; + else if ( precision == -1 && zeroPad && width > minwidth ) + nzeros = width - minwidth; + else + nzeros = 0; + + minwidth += nzeros; + int nspaces = width > minwidth ? width - minwidth : 0; + + if ( !leftAdjust ) + pad( buf, ' ', nspaces ); + + if ( number < 0 ) { + if ( nzeros > 0 ) { + buf.append( (byte)'-' ); + digits = digits.substring( 1 ); + } + } else if ( explicitPlus ) { + buf.append( (byte)'+' ); + } else if ( space ) { + buf.append( (byte)' ' ); + } + + if ( nzeros > 0 ) + pad( buf, '0', nzeros ); + + buf.append( digits ); + + if ( leftAdjust ) + pad( buf, ' ', nspaces ); + } + + public void format(Buffer buf, double x) { + // TODO + buf.append( String.valueOf( x ) ); + } + + public void format(Buffer buf, LuaString s) { + int nullindex = s.indexOf( (byte)'\0', 0 ); + if ( nullindex != -1 ) + s = s.substring( 0, nullindex ); + buf.append(s); + } + + public static final void pad(Buffer buf, char c, int n) { + byte b = (byte)c; + while ( n-- > 0 ) + buf.append(b); + } + } + + /** + * string.gmatch (s, pattern) + * + * Returns an iterator function that, each time it is called, returns the next captures + * from pattern over string s. If pattern specifies no captures, then the + * whole match is produced in each call. + * + * As an example, the following loop + * s = "hello world from Lua" + * for w in string.gmatch(s, "%a+") do + * print(w) + * end + * + * will iterate over all the words from string s, printing one per line. + * The next example collects all pairs key=value from the given string into a table: + * t = {} + * s = "from=world, to=Lua" + * for k, v in string.gmatch(s, "(%w+)=(%w+)") do + * t[k] = v + * end + * + * For this function, a '^' at the start of a pattern does not work as an anchor, + * as this would prevent the iteration. + */ + static Varargs gmatch( Varargs args ) { + LuaString src = args.checkstring( 1 ); + LuaString pat = args.checkstring( 2 ); + return new GMatchAux(args, src, pat); + } + + static class GMatchAux extends VarArgFunction { + private final int srclen; + private final MatchState ms; + private int soffset; + public GMatchAux(Varargs args, LuaString src, LuaString pat) { + this.srclen = src.length(); + this.ms = new MatchState(args, src, pat); + this.soffset = 0; + } + public Varargs invoke(Varargs args) { + for ( ; soffset=0 ) { + int soff = soffset; + soffset = res; + return ms.push_captures( true, soff, res ); + } + } + return NIL; + } + } + + + /** + * string.gsub (s, pattern, repl [, n]) + * Returns a copy of s in which all (or the first n, if given) occurrences of the + * pattern have been replaced by a replacement string specified by repl, which + * may be a string, a table, or a function. gsub also returns, as its second value, + * the total number of matches that occurred. + * + * If repl is a string, then its value is used for replacement. + * The character % works as an escape character: any sequence in repl of the form %n, + * with n between 1 and 9, stands for the value of the n-th captured substring (see below). + * The sequence %0 stands for the whole match. The sequence %% stands for a single %. + * + * If repl is a table, then the table is queried for every match, using the first capture + * as the key; if the pattern specifies no captures, then the whole match is used as the key. + * + * If repl is a function, then this function is called every time a match occurs, + * with all captured substrings passed as arguments, in order; if the pattern specifies + * no captures, then the whole match is passed as a sole argument. + * + * If the value returned by the table query or by the function call is a string or a number, + * then it is used as the replacement string; otherwise, if it is false or nil, + * then there is no replacement (that is, the original match is kept in the string). + * + * Here are some examples: + * x = string.gsub("hello world", "(%w+)", "%1 %1") + * --> x="hello hello world world" + * + * x = string.gsub("hello world", "%w+", "%0 %0", 1) + * --> x="hello hello world" + * + * x = string.gsub("hello world from Lua", "(%w+)%s*(%w+)", "%2 %1") + * --> x="world hello Lua from" + * + * x = string.gsub("home = $HOME, user = $USER", "%$(%w+)", os.getenv) + * --> x="home = /home/roberto, user = roberto" + * + * x = string.gsub("4+5 = $return 4+5$", "%$(.-)%$", function (s) + * return loadstring(s)() + * end) + * --> x="4+5 = 9" + * + * local t = {name="lua", version="5.1"} + * x = string.gsub("$name-$version.tar.gz", "%$(%w+)", t) + * --> x="lua-5.1.tar.gz" + */ + static Varargs gsub( Varargs args ) { + LuaString src = args.checkstring( 1 ); + final int srclen = src.length(); + LuaString p = args.checkstring( 2 ); + LuaValue repl = args.arg( 3 ); + int max_s = args.optint( 4, srclen + 1 ); + final boolean anchor = p.length() > 0 && p.charAt( 0 ) == '^'; + + Buffer lbuf = new Buffer( srclen ); + MatchState ms = new MatchState( args, src, p ); + + int soffset = 0; + int n = 0; + while ( n < max_s ) { + ms.reset(); + int res = ms.match( soffset, anchor ? 1 : 0 ); + if ( res != -1 ) { + n++; + ms.add_value( lbuf, soffset, res, repl ); + } + if ( res != -1 && res > soffset ) + soffset = res; + else if ( soffset < srclen ) + lbuf.append( (byte) src.luaByte( soffset++ ) ); + else + break; + if ( anchor ) + break; + } + lbuf.append( src.substring( soffset, srclen ) ); + return varargsOf(lbuf.tostrvalue(), valueOf(n)); + } + + /** + * string.len (s) + * + * Receives a string and returns its length. The empty string "" has length 0. + * Embedded zeros are counted, so "a\000bc\000" has length 5. + */ + static LuaValue len( LuaValue arg ) { + return arg.checkstring().len(); + } + + /** + * string.lower (s) + * + * Receives a string and returns a copy of this string with all uppercase letters + * changed to lowercase. All other characters are left unchanged. + * The definition of what an uppercase letter is depends on the current locale. + */ + static LuaValue lower( LuaValue arg ) { + return valueOf( arg.checkString().toLowerCase() ); + } + + /** + * string.match (s, pattern [, init]) + * + * Looks for the first match of pattern in the string s. If it finds one, + * then match returns the captures from the pattern; otherwise it returns + * nil. If pattern specifies no captures, then the whole match is returned. + * A third, optional numerical argument init specifies where to start the + * search; its default value is 1 and may be negative. + */ + static Varargs match( Varargs args ) { + return str_find_aux( args, false ); + } + + /** + * string.rep (s, n) + * + * Returns a string that is the concatenation of n copies of the string s. + */ + static Varargs rep( Varargs args ) { + LuaString s = args.checkstring( 1 ); + int n = args.checkint( 2 ); + final byte[] bytes = new byte[ s.length() * n ]; + int len = s.length(); + for ( int offset = 0; offset < bytes.length; offset += len ) { + s.copyInto( 0, bytes, offset, len ); + } + return valueOf( bytes ); + } + + /** + * string.reverse (s) + * + * Returns a string that is the string s reversed. + */ + static LuaValue reverse( LuaValue arg ) { + LuaString s = arg.checkstring(); + int n = s.length(); + byte[] b = new byte[n]; + for ( int i=0, j=n-1; i l ) + end = l; + + if ( start <= end ) { + return s.substring( start-1 , end ); + } else { + return EMPTYSTRING; + } + } + + /** + * string.upper (s) + * + * Receives a string and returns a copy of this string with all lowercase letters + * changed to uppercase. All other characters are left unchanged. + * The definition of what a lowercase letter is depends on the current locale. + */ + static LuaValue upper( LuaValue arg ) { + return valueOf(arg.checkString().toUpperCase()); + } + + /** + * This utility method implements both string.find and string.match. + */ + static Varargs str_find_aux( Varargs args, boolean find ) { + LuaString s = args.checkstring( 1 ); + LuaString pat = args.checkstring( 2 ); + int init = args.optint( 3, 1 ); + + if ( init > 0 ) { + init = Math.min( init - 1, s.length() ); + } else if ( init < 0 ) { + init = Math.max( 0, s.length() + init ); + } + + boolean fastMatch = find && ( args.arg(4).toboolean() || pat.indexOfAny( SPECIALS ) == -1 ); + + if ( fastMatch ) { + int result = s.indexOf( pat, init ); + if ( result != -1 ) { + return varargsOf( valueOf(result+1), valueOf(result+pat.length()) ); + } + } else { + MatchState ms = new MatchState( args, s, pat ); + + boolean anchor = false; + int poff = 0; + if ( pat.luaByte( 0 ) == '^' ) { + anchor = true; + poff = 1; + } + + int soff = init; + do { + int res; + ms.reset(); + if ( ( res = ms.match( soff, poff ) ) != -1 ) { + if ( find ) { + return varargsOf( valueOf(soff+1), valueOf(res), ms.push_captures( false, soff, res )); + } else { + return ms.push_captures( true, soff, res ); + } + } + } while ( soff++ < s.length() && !anchor ); + } + return NIL; + } + + private static int posrelat( int pos, int len ) { + return ( pos >= 0 ) ? pos : len + pos + 1; + } + + // Pattern matching implementation + + private static final int L_ESC = '%'; + private static final LuaString SPECIALS = valueOf("^$*+?.([%-"); + private static final int MAX_CAPTURES = 32; + + private static final int CAP_UNFINISHED = -1; + private static final int CAP_POSITION = -2; + + private static final byte MASK_ALPHA = 0x01; + private static final byte MASK_LOWERCASE = 0x02; + private static final byte MASK_UPPERCASE = 0x04; + private static final byte MASK_DIGIT = 0x08; + private static final byte MASK_PUNCT = 0x10; + private static final byte MASK_SPACE = 0x20; + private static final byte MASK_CONTROL = 0x40; + private static final byte MASK_HEXDIGIT = (byte)0x80; + + private static final byte[] CHAR_TABLE; + + static { + CHAR_TABLE = new byte[256]; + + for ( int i = 0; i < 256; ++i ) { + final char c = (char) i; + CHAR_TABLE[i] = (byte)( ( Character.isDigit( c ) ? MASK_DIGIT : 0 ) | + ( Character.isLowerCase( c ) ? MASK_LOWERCASE : 0 ) | + ( Character.isUpperCase( c ) ? MASK_UPPERCASE : 0 ) | + ( ( c < ' ' || c == 0x7F ) ? MASK_CONTROL : 0 ) ); + if ( ( c >= 'a' && c <= 'f' ) || ( c >= 'A' && c <= 'F' ) || ( c >= '0' && c <= '9' ) ) { + CHAR_TABLE[i] |= MASK_HEXDIGIT; + } + if ( ( c >= '!' && c <= '/' ) || ( c >= ':' && c <= '@' ) ) { + CHAR_TABLE[i] |= MASK_PUNCT; + } + if ( ( CHAR_TABLE[i] & ( MASK_LOWERCASE | MASK_UPPERCASE ) ) != 0 ) { + CHAR_TABLE[i] |= MASK_ALPHA; + } + } + + CHAR_TABLE[' '] = MASK_SPACE; + CHAR_TABLE['\r'] |= MASK_SPACE; + CHAR_TABLE['\n'] |= MASK_SPACE; + CHAR_TABLE['\t'] |= MASK_SPACE; + CHAR_TABLE[0x0C /* '\v' */ ] |= MASK_SPACE; + CHAR_TABLE['\f'] |= MASK_SPACE; + }; + + private static class MatchState { + final LuaString s; + final LuaString p; + final Varargs args; + int level; + int[] cinit; + int[] clen; + + MatchState( Varargs args, LuaString s, LuaString pattern ) { + this.s = s; + this.p = pattern; + this.args = args; + this.level = 0; + this.cinit = new int[ MAX_CAPTURES ]; + this.clen = new int[ MAX_CAPTURES ]; + } + + void reset() { + level = 0; + } + + private void add_s( Buffer lbuf, LuaString news, int soff, int e ) { + int l = news.length(); + for ( int i = 0; i < l; ++i ) { + byte b = (byte) news.luaByte( i ); + if ( b != L_ESC ) { + lbuf.append( (byte) b ); + } else { + ++i; // skip ESC + b = (byte) news.luaByte( i ); + if ( !Character.isDigit( (char) b ) ) { + lbuf.append( b ); + } else if ( b == '0' ) { + lbuf.append( s.substring( soff, e ) ); + } else { + lbuf.append( push_onecapture( b - '1', soff, e ).strvalue() ); + } + } + } + } + + public void add_value( Buffer lbuf, int soffset, int end, LuaValue repl ) { + switch ( repl.type() ) { + case LuaValue.TSTRING: + case LuaValue.TNUMBER: + add_s( lbuf, repl.strvalue(), soffset, end ); + return; + + case LuaValue.TFUNCTION: + repl = repl.invoke( push_captures( true, soffset, end ) ).arg1(); + break; + + case LuaValue.TTABLE: + // Need to call push_onecapture here for the error checking + repl = repl.get( push_onecapture( 0, soffset, end ) ); + break; + + default: + error( "bad argument: string/function/table expected" ); + return; + } + + if ( !repl.toboolean() ) { + repl = s.substring( soffset, end ); + } else if ( ! repl.isstring() ) { + error( "invalid replacement value (a "+repl.typename()+")" ); + } + lbuf.append( repl.strvalue() ); + } + + Varargs push_captures( boolean wholeMatch, int soff, int end ) { + int nlevels = ( this.level == 0 && wholeMatch ) ? 1 : this.level; + switch ( nlevels ) { + case 0: return NONE; + case 1: return push_onecapture( 0, soff, end ); + } + LuaValue[] v = new LuaValue[nlevels]; + for ( int i = 0; i < nlevels; ++i ) + v[i] = push_onecapture( i, soff, end ); + return varargsOf(v); + } + + private LuaValue push_onecapture( int i, int soff, int end ) { + if ( i >= this.level ) { + if ( i == 0 ) { + return s.substring( soff, end ); + } else { + return error( "invalid capture index" ); + } + } else { + int l = clen[i]; + if ( l == CAP_UNFINISHED ) { + return error( "unfinished capture" ); + } + if ( l == CAP_POSITION ) { + return valueOf( cinit[i] + 1 ); + } else { + int begin = cinit[i]; + return s.substring( begin, begin + l ); + } + } + } + + private int check_capture( int l ) { + l -= '1'; + if ( l < 0 || l >= level || this.clen[l] == CAP_UNFINISHED ) { + error("invalid capture index"); + } + return l; + } + + private int capture_to_close() { + int level = this.level; + for ( level--; level >= 0; level-- ) + if ( clen[level] == CAP_UNFINISHED ) + return level; + error("invalid pattern capture"); + return 0; + } + + int classend( int poffset ) { + switch ( p.luaByte( poffset++ ) ) { + case L_ESC: + if ( poffset == p.length() ) { + error( "malformed pattern (ends with %)" ); + } + return poffset + 1; + + case '[': + if ( p.luaByte( poffset ) == '^' ) poffset++; + do { + if ( poffset == p.length() ) { + error( "malformed pattern (missing ])" ); + } + if ( p.luaByte( poffset++ ) == L_ESC && poffset != p.length() ) + poffset++; + } while ( p.luaByte( poffset ) != ']' ); + return poffset + 1; + default: + return poffset; + } + } + + static boolean match_class( int c, int cl ) { + final char lcl = Character.toLowerCase( (char) cl ); + int cdata = CHAR_TABLE[c]; + + boolean res; + switch ( lcl ) { + case 'a': res = ( cdata & MASK_ALPHA ) != 0; break; + case 'd': res = ( cdata & MASK_DIGIT ) != 0; break; + case 'l': res = ( cdata & MASK_LOWERCASE ) != 0; break; + case 'u': res = ( cdata & MASK_UPPERCASE ) != 0; break; + case 'c': res = ( cdata & MASK_CONTROL ) != 0; break; + case 'p': res = ( cdata & MASK_PUNCT ) != 0; break; + case 's': res = ( cdata & MASK_SPACE ) != 0; break; + case 'w': res = ( cdata & ( MASK_ALPHA | MASK_DIGIT ) ) != 0; break; + case 'x': res = ( cdata & MASK_HEXDIGIT ) != 0; break; + case 'z': res = ( c == 0 ); break; + default: return cl == c; + } + return ( lcl == cl ) ? res : !res; + } + + boolean matchbracketclass( int c, int poff, int ec ) { + boolean sig = true; + if ( p.luaByte( poff + 1 ) == '^' ) { + sig = false; + poff++; + } + while ( ++poff < ec ) { + if ( p.luaByte( poff ) == L_ESC ) { + poff++; + if ( match_class( c, p.luaByte( poff ) ) ) + return sig; + } + else if ( ( p.luaByte( poff + 1 ) == '-' ) && ( poff + 2 < ec ) ) { + poff += 2; + if ( p.luaByte( poff - 2 ) <= c && c <= p.luaByte( poff ) ) + return sig; + } + else if ( p.luaByte( poff ) == c ) return sig; + } + return !sig; + } + + boolean singlematch( int c, int poff, int ep ) { + switch ( p.luaByte( poff ) ) { + case '.': return true; + case L_ESC: return match_class( c, p.luaByte( poff + 1 ) ); + case '[': return matchbracketclass( c, poff, ep - 1 ); + default: return p.luaByte( poff ) == c; + } + } + + /** + * Perform pattern matching. If there is a match, returns offset into s + * where match ends, otherwise returns -1. + */ + int match( int soffset, int poffset ) { + while ( true ) { + // Check if we are at the end of the pattern - + // equivalent to the '\0' case in the C version, but our pattern + // string is not NUL-terminated. + if ( poffset == p.length() ) + return soffset; + switch ( p.luaByte( poffset ) ) { + case '(': + if ( ++poffset < p.length() && p.luaByte( poffset ) == ')' ) + return start_capture( soffset, poffset + 1, CAP_POSITION ); + else + return start_capture( soffset, poffset, CAP_UNFINISHED ); + case ')': + return end_capture( soffset, poffset + 1 ); + case L_ESC: + if ( poffset + 1 == p.length() ) + error("malformed pattern (ends with '%')"); + switch ( p.luaByte( poffset + 1 ) ) { + case 'b': + soffset = matchbalance( soffset, poffset + 2 ); + if ( soffset == -1 ) return -1; + poffset += 4; + continue; + case 'f': { + poffset += 2; + if ( p.luaByte( poffset ) != '[' ) { + error("Missing [ after %f in pattern"); + } + int ep = classend( poffset ); + int previous = ( soffset == 0 ) ? -1 : s.luaByte( soffset - 1 ); + if ( matchbracketclass( previous, poffset, ep - 1 ) || + matchbracketclass( s.luaByte( soffset ), poffset, ep - 1 ) ) + return -1; + poffset = ep; + continue; + } + default: { + int c = p.luaByte( poffset + 1 ); + if ( Character.isDigit( (char) c ) ) { + soffset = match_capture( soffset, c ); + if ( soffset == -1 ) + return -1; + return match( soffset, poffset + 2 ); + } + } + } + case '$': + if ( poffset + 1 == p.length() ) + return ( soffset == s.length() ) ? soffset : -1; + } + int ep = classend( poffset ); + boolean m = soffset < s.length() && singlematch( s.luaByte( soffset ), poffset, ep ); + int pc = ( ep < p.length() ) ? p.luaByte( ep ) : '\0'; + + switch ( pc ) { + case '?': + int res; + if ( m && ( ( res = match( soffset + 1, ep + 1 ) ) != -1 ) ) + return res; + poffset = ep + 1; + continue; + case '*': + return max_expand( soffset, poffset, ep ); + case '+': + return ( m ? max_expand( soffset + 1, poffset, ep ) : -1 ); + case '-': + return min_expand( soffset, poffset, ep ); + default: + if ( !m ) + return -1; + soffset++; + poffset = ep; + continue; + } + } + } + + int max_expand( int soff, int poff, int ep ) { + int i = 0; + while ( soff + i < s.length() && + singlematch( s.luaByte( soff + i ), poff, ep ) ) + i++; + while ( i >= 0 ) { + int res = match( soff + i, ep + 1 ); + if ( res != -1 ) + return res; + i--; + } + return -1; + } + + int min_expand( int soff, int poff, int ep ) { + for ( ;; ) { + int res = match( soff, ep + 1 ); + if ( res != -1 ) + return res; + else if ( soff < s.length() && singlematch( s.luaByte( soff ), poff, ep ) ) + soff++; + else return -1; + } + } + + int start_capture( int soff, int poff, int what ) { + int res; + int level = this.level; + if ( level >= MAX_CAPTURES ) { + error( "too many captures" ); + } + cinit[ level ] = soff; + clen[ level ] = what; + this.level = level + 1; + if ( ( res = match( soff, poff ) ) == -1 ) + this.level--; + return res; + } + + int end_capture( int soff, int poff ) { + int l = capture_to_close(); + int res; + clen[l] = soff - cinit[l]; + if ( ( res = match( soff, poff ) ) == -1 ) + clen[l] = CAP_UNFINISHED; + return res; + } + + int match_capture( int soff, int l ) { + l = check_capture( l ); + int len = clen[ l ]; + if ( ( s.length() - soff ) >= len && + LuaString.equals( s, cinit[l], s, soff, len ) ) + return soff + len; + else + return -1; + } + + int matchbalance( int soff, int poff ) { + final int plen = p.length(); + if ( poff == plen || poff + 1 == plen ) { + error( "unbalanced pattern" ); + } + if ( s.luaByte( soff ) != p.luaByte( poff ) ) + return -1; + else { + int b = p.luaByte( poff ); + int e = p.luaByte( poff + 1 ); + int cont = 1; + while ( ++soff < s.length() ) { + if ( s.luaByte( soff ) == e ) { + if ( --cont == 0 ) return soff + 1; + } + else if ( s.luaByte( soff ) == b ) cont++; + } + } + return -1; + } + } +} diff --git a/src/core/org/luaj/vm2/lib/TableLib.java b/src/core/org/luaj/vm2/lib/TableLib.java new file mode 100644 index 00000000..78d15935 --- /dev/null +++ b/src/core/org/luaj/vm2/lib/TableLib.java @@ -0,0 +1,93 @@ +/******************************************************************************* +* Copyright (c) 2009 Luaj.org. All rights reserved. +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +* THE SOFTWARE. +******************************************************************************/ +package org.luaj.vm2.lib; + +import org.luaj.vm2.LuaTable; +import org.luaj.vm2.LuaValue; +import org.luaj.vm2.Varargs; + +public class TableLib extends LuaTable { + + public TableLib() { + LibFunction.bind( this, new TableFunc1().getClass(), new String[] { + "getn", // (table) -> number + "maxn", // (table) -> number + } ); + LibFunction.bind( this, new TableFuncV().getClass(), new String[] { + "remove", // (table [, pos]) -> removed-ele + "concat", // (table [, sep [, i [, j]]]) -> string + "insert", // (table, [pos,] value) -> prev-ele + "sort", // (table [, comp]) -> void + "foreach", // (table, func) -> void + "foreachi", // (table, func) -> void + } ); + } + + public static class TableFunc1 extends OneArgFunction { + public LuaValue call(LuaValue arg) { + switch ( opcode ) { + case 0: return arg.checktable().getn(); + case 1: return valueOf( arg.checktable().maxn()); + } + return NIL; + } + + } + + + public static class TableFuncV extends VarArgFunction { + public Varargs invoke(Varargs args) { + switch ( opcode ) { + case 0: { // "remove" (table [, pos]) -> removed-ele + LuaTable table = args.checktable(1); + int pos = args.narg()>1? args.checkint(2): 0; + return table.remove(pos); + } + case 1: { // "concat" (table [, sep [, i [, j]]]) -> string + LuaTable table = args.checktable(1); + return table.concat( + args.optstring(2,LuaValue.EMPTYSTRING), + args.optint(3,1), + args.isvalue(4)? args.checkint(4): table.length() ); + } + case 2: { // "insert" (table, [pos,] value) -> prev-ele + final LuaTable table = args.checktable(1); + final int pos = args.narg()>2? args.checkint(2): 0; + final LuaValue value = args.arg( args.narg()>2? 3: 2 ); + table.insert( pos, value ); + return NONE; + } + case 3: { // "sort" (table [, comp]) -> void + args.checktable(1).sort( args.optvalue(2,NIL) ); + return NONE; + } + case 4: { // (table, func) -> void + return args.checktable(1).foreach( args.checkfunction(2) ); + } + case 5: { // "foreachi" (table, func) -> void + return args.checktable(1).foreachi( args.checkfunction(2) ); + } + } + return NONE; + } + } +} diff --git a/src/core/org/luaj/vm2/lib/ThreeArgFunction.java b/src/core/org/luaj/vm2/lib/ThreeArgFunction.java new file mode 100644 index 00000000..9d640540 --- /dev/null +++ b/src/core/org/luaj/vm2/lib/ThreeArgFunction.java @@ -0,0 +1,58 @@ +/******************************************************************************* +* Copyright (c) 2009 Luaj.org. All rights reserved. +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +* THE SOFTWARE. +******************************************************************************/ +package org.luaj.vm2.lib; + +import org.luaj.vm2.LuaValue; +import org.luaj.vm2.Varargs; + +abstract public class ThreeArgFunction extends LibFunction { + + abstract public LuaValue call(LuaValue arg1, LuaValue arg2, LuaValue arg3); + + public ThreeArgFunction() { + } + + public ThreeArgFunction( LuaValue env ) { + this.env = env; + } + + public ThreeArgFunction( String name, int opcode, LuaValue env ) { + super(name, opcode, env); + } + + public final LuaValue call() { + return call(NIL, NIL, NIL); + } + + public final LuaValue call(LuaValue arg) { + return call(arg, NIL, NIL); + } + + public LuaValue call(LuaValue arg1, LuaValue arg2) { + return call(arg1, arg2, NIL); + } + + public Varargs invoke(Varargs varargs) { + return call(varargs.arg1(),varargs.arg(2),varargs.arg(3)); + } + +} diff --git a/src/core/org/luaj/vm2/lib/TwoArgFunction.java b/src/core/org/luaj/vm2/lib/TwoArgFunction.java new file mode 100644 index 00000000..13304de7 --- /dev/null +++ b/src/core/org/luaj/vm2/lib/TwoArgFunction.java @@ -0,0 +1,58 @@ +/******************************************************************************* +* Copyright (c) 2009 Luaj.org. All rights reserved. +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +* THE SOFTWARE. +******************************************************************************/ +package org.luaj.vm2.lib; + +import org.luaj.vm2.LuaValue; +import org.luaj.vm2.Varargs; + +abstract public class TwoArgFunction extends LibFunction { + + abstract public LuaValue call(LuaValue arg1, LuaValue arg2); + + public TwoArgFunction() { + } + + public TwoArgFunction( LuaValue env ) { + this.env = env; + } + + public TwoArgFunction( String name, int opcode, LuaValue env ) { + super(name, opcode, env); + } + + public final LuaValue call() { + return call(NIL, NIL); + } + + public final LuaValue call(LuaValue arg) { + return call(arg, NIL); + } + + public LuaValue call(LuaValue arg1, LuaValue arg2, LuaValue arg3) { + return call(arg1, arg2); + } + + public Varargs invoke(Varargs varargs) { + return call(varargs.arg1(),varargs.arg(2)); + } + +} diff --git a/src/core/org/luaj/vm2/lib/VarArgFunction.java b/src/core/org/luaj/vm2/lib/VarArgFunction.java new file mode 100644 index 00000000..48ec6762 --- /dev/null +++ b/src/core/org/luaj/vm2/lib/VarArgFunction.java @@ -0,0 +1,57 @@ +/******************************************************************************* +* Copyright (c) 2009 Luaj.org. All rights reserved. +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +* THE SOFTWARE. +******************************************************************************/ +package org.luaj.vm2.lib; + +import org.luaj.vm2.LuaValue; +import org.luaj.vm2.Varargs; + +abstract public class VarArgFunction extends LibFunction { + + abstract public Varargs invoke(Varargs args); + + public VarArgFunction() { + } + + public VarArgFunction( LuaValue env ) { + this.env = env; + } + + public VarArgFunction( String name, int opcode, LuaValue env ) { + super(name, opcode, env); + } + + public final LuaValue call() { + return invoke(NONE).arg1(); + } + + public final LuaValue call(LuaValue arg) { + return invoke(arg).arg1(); + } + + public LuaValue call(LuaValue arg1, LuaValue arg2) { + return invoke(varargsOf(arg1,arg2)).arg1(); + } + + public LuaValue call(LuaValue arg1, LuaValue arg2, LuaValue arg3) { + return invoke(varargsOf(arg1,arg2,arg3)).arg1(); + } +} diff --git a/src/core/org/luaj/vm2/lib/ZeroArgFunction.java b/src/core/org/luaj/vm2/lib/ZeroArgFunction.java new file mode 100644 index 00000000..1ecb70d5 --- /dev/null +++ b/src/core/org/luaj/vm2/lib/ZeroArgFunction.java @@ -0,0 +1,57 @@ +/******************************************************************************* +* Copyright (c) 2009 Luaj.org. All rights reserved. +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +* THE SOFTWARE. +******************************************************************************/ +package org.luaj.vm2.lib; + +import org.luaj.vm2.LuaValue; +import org.luaj.vm2.Varargs; + +abstract public class ZeroArgFunction extends LibFunction { + + abstract public LuaValue call(); + + public ZeroArgFunction() { + } + + public ZeroArgFunction( LuaValue env ) { + this.env = env; + } + + public ZeroArgFunction( String name, int opcode, LuaValue env ) { + super(name, opcode, env); + } + + public LuaValue call(LuaValue arg) { + return call(); + } + + public LuaValue call(LuaValue arg1, LuaValue arg2) { + return call(); + } + + public LuaValue call(LuaValue arg1, LuaValue arg2, LuaValue arg3) { + return call(); + } + + public Varargs invoke(Varargs varargs) { + return call(); + } +} diff --git a/src/jme/org/luaj/vm2/lib/JmePlatform.java b/src/jme/org/luaj/vm2/lib/JmePlatform.java new file mode 100644 index 00000000..35209ad3 --- /dev/null +++ b/src/jme/org/luaj/vm2/lib/JmePlatform.java @@ -0,0 +1,45 @@ +/******************************************************************************* + * Copyright (c) 2009 Luaj.org. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + ******************************************************************************/ +package org.luaj.vm2.lib; + +import org.luaj.vm2.LuaTable; + +public class JmePlatform { + + /** + * Create a standard set of globals for JME including all the libraries. + * + * @return Table of globals initialized with the standard JME libraries + */ + public static LuaTable standardGlobals() { + LuaTable _G = new BaseLib(); + new org.luaj.vm2.lib.PackageLib(_G); + _G.set( "io", new org.luaj.vm2.lib.jme.JseIoLib() ); + _G.set( "math", new org.luaj.vm2.lib.MathLib() ); + _G.set( "os", new org.luaj.vm2.lib.OsLib() ); + _G.set( "table", new org.luaj.vm2.lib.TableLib() ); + _G.set( "string", new org.luaj.vm2.lib.StringLib() ); + CoroutineLib.install( _G ); + return _G; + } + +} diff --git a/src/jme/org/luaj/vm2/lib/jme/JseIoLib.java b/src/jme/org/luaj/vm2/lib/jme/JseIoLib.java new file mode 100644 index 00000000..96a2b798 --- /dev/null +++ b/src/jme/org/luaj/vm2/lib/jme/JseIoLib.java @@ -0,0 +1,199 @@ +/******************************************************************************* +* Copyright (c) 2009 Luaj.org. All rights reserved. +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +* THE SOFTWARE. +******************************************************************************/ +package org.luaj.vm2.lib.jme; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import javax.microedition.io.Connector; +import javax.microedition.io.StreamConnection; + +import org.luaj.vm2.LuaString; +import org.luaj.vm2.lib.BaseLib; +import org.luaj.vm2.lib.IoLib; + +/** + * Implementation of the lua io library based on CLDC 1.0 and StreamConnection. + * + * Seek is not supported. + */ +public class JseIoLib extends IoLib { + + public JseIoLib() { + super(); + } + + protected File wrapStdin() throws IOException { + return new FileImpl(BaseLib.STDIN); + } + + protected File wrapStdout() throws IOException { + return new FileImpl(BaseLib.STDOUT); + } + + protected File openFile( String filename, boolean readMode, boolean appendMode, boolean updateMode, boolean binaryMode ) throws IOException { + String url = "file:///" + filename; + int mode = readMode? Connector.READ: Connector.READ_WRITE; + StreamConnection conn = (StreamConnection) Connector.open( url, mode ); + File f = readMode? + new FileImpl(conn, conn.openInputStream(), null): + new FileImpl(conn, conn.openInputStream(), conn.openOutputStream()); + /* + if ( appendMode ) { + f.seek("end",0); + } else { + if ( ! readMode ) + conn.truncate(0); + } + */ + return f; + } + + private static void notimplemented() throws IOException { + throw new IOException("not implemented"); + } + + protected File openProgram(String prog, String mode) throws IOException { + notimplemented(); + return null; + } + + protected File tmpFile() throws IOException { + notimplemented(); + return null; + } + + private final class FileImpl extends File { + private final StreamConnection conn; + private final InputStream is; + private final OutputStream os; + private boolean closed = false; + private boolean nobuffer = false; + private int lookahead = -1; + private FileImpl( StreamConnection conn, InputStream is, OutputStream os ) { + this.conn = conn; + this.is = is; + this.os = os; + } + private FileImpl( InputStream i ) { + this( null, i, null ); + } + private FileImpl( OutputStream o ) { + this( null, null, o ); + } + public String toString() { + return "file ("+this.hashCode()+")"; + } + public boolean isstdfile() { + return conn == null; + } + public void close() throws IOException { + closed = true; + if ( conn != null ) { + conn.close(); + } + } + public void flush() throws IOException { + if ( os != null ) + os.flush(); + } + public void write(LuaString s) throws IOException { + if ( os != null ) + os.write( s.m_bytes, s.m_offset, s.m_length ); + else + notimplemented(); + if ( nobuffer ) + flush(); + } + public boolean isclosed() { + return closed; + } + public int seek(String option, int pos) throws IOException { + /* + if ( conn != null ) { + if ( "set".equals(option) ) { + conn.seek(pos); + return (int) conn.getFilePointer(); + } else if ( "end".equals(option) ) { + conn.seek(conn.length()+1+pos); + return (int) conn.length()+1; + } else { + conn.seek(conn.getFilePointer()+pos); + return (int) conn.getFilePointer(); + } + } + */ + notimplemented(); + return 0; + } + public void setvbuf(String mode, int size) { + nobuffer = "no".equals(mode); + } + + // get length remaining to read + public int remaining() throws IOException { + return -1; + } + + // peek ahead one character + public int peek() throws IOException { + if ( lookahead < 0 ) + lookahead = is.read(); + return lookahead; + } + + // return char if read, -1 if eof, throw IOException on other exception + public int read() throws IOException { + if ( lookahead >= 0 ) { + int c = lookahead; + lookahead = -1; + return c; + } + if ( is != null ) + return is.read(); + notimplemented(); + return 0; + } + + // return number of bytes read if positive, -1 if eof, throws IOException + public int read(byte[] bytes, int offset, int length) throws IOException { + int n,i=0; + if (is!=null) { + if ( length > 0 && lookahead >= 0 ) { + bytes[offset] = (byte) lookahead; + lookahead = -1; + i += 1; + } + for ( ; i 0 ? i : -1 ); + i += n; + } + } else { + notimplemented(); + } + return length; + } + } +} diff --git a/src/jse/META-INF/services/javax.script.ScriptEngineFactory b/src/jse/META-INF/services/javax.script.ScriptEngineFactory new file mode 100644 index 00000000..e3e6dde4 --- /dev/null +++ b/src/jse/META-INF/services/javax.script.ScriptEngineFactory @@ -0,0 +1 @@ +org.luaj.vm2.script.LuaScriptEngineFactory \ No newline at end of file diff --git a/src/jse/lua.java b/src/jse/lua.java new file mode 100644 index 00000000..c7df8d17 --- /dev/null +++ b/src/jse/lua.java @@ -0,0 +1,199 @@ +/******************************************************************************* +* Copyright (c) 2009 Luaj.org. All rights reserved. +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +* THE SOFTWARE. +******************************************************************************/ + +import java.io.BufferedReader; +import java.io.ByteArrayInputStream; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; + +import org.luaj.vm2.LoadState; +import org.luaj.vm2.Lua; +import org.luaj.vm2.LuaClosure; +import org.luaj.vm2.LuaValue; +import org.luaj.vm2.Prototype; +import org.luaj.vm2.compiler.LuaC; +import org.luaj.vm2.lib.DebugLib; +import org.luaj.vm2.lib.JsePlatform; + + +/** + * lua command for use in java se environments. + */ +public class lua { + private static final String version = Lua._VERSION + "Copyright (c) 2009 Luaj.org.org"; + + private static final String usage = + "usage: java -cp luaj-jse.jar lua [options] [script [args]].\n" + + "Available options are:\n" + + " -e stat execute string 'stat'\n" + + " -l name require library 'name'\n" + + " -i enter interactive mode after executing 'script'\n" + + " -v show version information\n" + + " -- stop handling options\n" + + " - execute stdin and stop handling options"; + + private static void usageExit() { + System.out.println(usage); + System.exit(-1); + } + + private static LuaValue _G; + + public static void main( String[] args ) throws IOException { + + // new lua state + LuaC.install(); + _G = JsePlatform.standardGlobals(); + DebugLib.install( _G ); + + // process args + boolean interactive = (args.length == 0); + boolean versioninfo = false; + boolean processing = true; + try { + // stateful argument processing + for ( int i=0; i= args.length ) + usageExit(); + // input script - defer to last stage + break; + case 'l': + if ( ++i >= args.length ) + usageExit(); + loadLibrary( args[i] ); + break; + case 'i': + interactive = true; + break; + case 'v': + versioninfo = true; + break; + case '-': + if ( args[i].length() > 2 ) + usageExit(); + processing = false; + break; + default: + usageExit(); + break; + } + } + } + + // echo version + if ( versioninfo ) + System.out.println(version); + + // input script processing + processing = true; + for ( int i=0; i "); + System.out.flush(); + String line = reader.readLine(); + if ( line == null ) + return; + processScript( new ByteArrayInputStream(line.getBytes()), "-", NOARGS, 0 ); + } + } +} diff --git a/src/jse/luac.java b/src/jse/luac.java new file mode 100644 index 00000000..3b431a2c --- /dev/null +++ b/src/jse/luac.java @@ -0,0 +1,179 @@ +/******************************************************************************* +* Copyright (c) 2009 Luaj.org. All rights reserved. +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +* THE SOFTWARE. +******************************************************************************/ + +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import org.luaj.vm2.Lua; +import org.luaj.vm2.Print; +import org.luaj.vm2.Prototype; +import org.luaj.vm2.compiler.DumpState; +import org.luaj.vm2.compiler.LuaC; + + +/** + * Compiler for lua files to lua bytecode. + */ +public class luac { + private static final String version = Lua._VERSION + "Copyright (C) 2009 luaj.org"; + + private static final String usage = + "usage: java -cp luaj-jse.jar luac [options] [filenames].\n" + + "Available options are:\n" + + " - process stdin\n" + + " -l list\n" + + " -o name output to file 'name' (default is \"luac.out\")\n" + + " -p parse only\n" + + " -s strip debug information\n" + + " -e little endian format for numbers\n" + + " -i number format 'n', (n=0,1 or 4, default="+DumpState.NUMBER_FORMAT_DEFAULT+")\n" + + " -v show version information\n" + + " -- stop handling options\n"; + + private static void usageExit() { + System.out.println(usage); + System.exit(-1); + } + + private boolean list = false; + private String output = "luac.out"; + private boolean parseonly = false; + private boolean stripdebug = false; + private boolean littleendian = false; + private int numberformat = DumpState.NUMBER_FORMAT_DEFAULT; + private boolean versioninfo = false; + private boolean processing = true; + + public static void main( String[] args ) throws IOException { + new luac( args ); + } + + private luac( String[] args ) throws IOException { + + // process args + try { + // get stateful args + for ( int i=0; i= args.length ) + usageExit(); + output = args[i]; + break; + case 'p': + parseonly = true; + break; + case 's': + stripdebug = true; + break; + case 'e': + littleendian = true; + break; + case 'i': + if ( args[i].length() <= 2 ) + usageExit(); + numberformat = Integer.parseInt(args[i].substring(2)); + break; + case 'v': + versioninfo = true; + break; + case '-': + if ( args[i].length() > 2 ) + usageExit(); + processing = false; + break; + default: + usageExit(); + break; + } + } + } + + // echo version + if ( versioninfo ) + System.out.println(version); + + // open output file + OutputStream fos = new FileOutputStream( output ); + + // process input files + try { + processing = true; + for ( int i=0; i number format 'n', (n=0,1 or 4, default="+DumpState.NUMBER_FORMAT_DEFAULT+")\n" + + " -v show version information\n" + + " -- stop handling options\n"; + + private static void usageExit() { + System.out.println(usage); + System.exit(-1); + } + + private boolean list = false; + private String output = "luacj.out"; + private boolean parseonly = false; + private boolean stripdebug = false; + private boolean littleendian = false; + private int numberformat = DumpState.NUMBER_FORMAT_DEFAULT; + private boolean versioninfo = false; + private boolean processing = true; + + public static void main( String[] args ) throws IOException { + new luajc( args ); + } + + private luajc( String[] args ) throws IOException { + + // process args + try { + // get stateful args + for ( int i=0; i= args.length ) + usageExit(); + output = args[i]; + break; + case 'p': + parseonly = true; + break; + case 's': + stripdebug = true; + break; + case 'e': + littleendian = true; + break; + case 'i': + if ( args[i].length() <= 2 ) + usageExit(); + numberformat = Integer.parseInt(args[i].substring(2)); + break; + case 'v': + versioninfo = true; + break; + case '-': + if ( args[i].length() > 2 ) + usageExit(); + processing = false; + break; + default: + usageExit(); + break; + } + } + } + + // echo version + if ( versioninfo ) + System.out.println(version); + + // open output file + OutputStream fos = new FileOutputStream( output ); + + // process input files + try { + processing = true; + for ( int i=0; i nargs? 0x4000: 0x8000); + for ( int i=0; iImplements features specific to the J2se environment: + * + *
  • execute()
  • + *
  • remove()
  • + *
  • rename()
  • + *
  • tmpname()
  • + *
    + * + * @see org.luaj.vm2.lib.OsLib + */ +public class JseOsLib extends org.luaj.vm2.lib.OsLib { + + /** return code indicating the execute() threw an I/O exception */ + public static int EXEC_IOEXCEPTION = -1; + + /** return code indicating the execute() was interrupted */ + public static int EXEC_INTERRUPTED = -2; + + /** return code indicating the execute() threw an unknown exception */ + public static int EXEC_ERROR = -3; + + /** public constructor */ + public JseOsLib() { + } + + protected int execute(String command) { + Runtime r = Runtime.getRuntime(); + try { + final Process p = r.exec(command); + try { + p.waitFor(); + return p.exitValue(); + } finally { + p.destroy(); + } + } catch (IOException ioe) { + return EXEC_IOEXCEPTION; + } catch (InterruptedException e) { + return EXEC_INTERRUPTED; + } catch (Throwable t) { + return EXEC_ERROR; + } + } + + protected void remove(String filename) throws IOException { + File f = new File(filename); + if ( ! f.exists() ) + throw new IOException("No such file or directory"); + if ( ! f.delete() ) + throw new IOException("Failed to delete"); + } + + protected void rename(String oldname, String newname) throws IOException { + File f = new File(oldname); + if ( ! f.exists() ) + throw new IOException("No such file or directory"); + if ( ! f.renameTo(new File(newname)) ) + throw new IOException("Failed to delete"); + } + + protected String tmpname() { + try { + java.io.File f = java.io.File.createTempFile(TMP_PREFIX ,TMP_SUFFIX); + return f.getName(); + } catch ( IOException ioe ) { + return super.tmpname(); + } + } + +} diff --git a/src/jse/org/luaj/vm2/lib/jse/LuajavaLib.java b/src/jse/org/luaj/vm2/lib/jse/LuajavaLib.java new file mode 100644 index 00000000..9ed34bcd --- /dev/null +++ b/src/jse/org/luaj/vm2/lib/jse/LuajavaLib.java @@ -0,0 +1,372 @@ +/******************************************************************************* +* Copyright (c) 2009 Luaj.org. All rights reserved. +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +* THE SOFTWARE. +******************************************************************************/ +package org.luaj.vm2.lib.jse; + + +/** LuaJava-like bindings to Java scripting. + * + * TODO: coerce types on way in and out, pick method base on arg count ant types. + */ +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.luaj.vm2.LuaError; +import org.luaj.vm2.LuaTable; +import org.luaj.vm2.LuaUserdata; +import org.luaj.vm2.LuaValue; +import org.luaj.vm2.Varargs; +import org.luaj.vm2.lib.LibFunction; +import org.luaj.vm2.lib.ThreeArgFunction; +import org.luaj.vm2.lib.TwoArgFunction; +import org.luaj.vm2.lib.VarArgFunction; + +public class LuajavaLib extends LuaTable { + + private static final int BINDCLASS = 0; + private static final int NEWINSTANCE = 1; + private static final int NEW = 2; + private static final int CREATEPROXY = 3; + private static final int LOADLIB = 4; + + private static final String[] NAMES = { + "bindClass", + "newInstance", + "new", + "createProxy", + "loadLib" }; + + private static final Map classMetatables = new HashMap(); + + public static void install(LuaValue globals) { + globals.set("luajava", new LuajavaLib()); + } + + public LuajavaLib() { + LibFunction.bind( this, LuajavaFuncV.class, NAMES ); + } + + // perform a lua call + public static class LuajavaFuncV extends VarArgFunction { + + public Varargs invoke(final Varargs args) { + try { + switch ( opcode ) { + case BINDCLASS: { + final Class clazz = Class.forName(args.checkString(1)); + return toUserdata( clazz, clazz ); + } + case NEWINSTANCE: + case NEW: { + // get constructor + final LuaValue c = args.checkvalue(1); + final Class clazz = (opcode==NEWINSTANCE? Class.forName(c.toString()): (Class) c.checkuserdata(Class.class)); + final ParamsList params = new ParamsList( args ); + final Constructor con = resolveConstructor( clazz, params ); + + // coerce args, construct instance + Object[] cargs = CoerceLuaToJava.coerceArgs( params.values, con.getParameterTypes() ); + Object o = con.newInstance( cargs ); + + // return result + return toUserdata( o, clazz ); + } + + case CREATEPROXY: { + final int niface = args.narg()-1; + if ( niface <= 0 ) + throw new LuaError("no interfaces"); + final LuaValue lobj = args.checktable(niface+1); + + // get the interfaces + final Class[] ifaces = new Class[niface]; + for ( int i=0; i stringBuffers = new Stack(); + public StringBuffer sb; + private int indent = 0; + + public JavaCodeGenerator() { + this.sb = new StringBuffer(); + } + + private String writeChunk(LSChunk chunk) { + pushFunctionContext(); + writeln( "import org.luaj.vm2.*;" ); + writeln( "import org.luaj.vm2.lib.*;" ); + writeln(); + writeln( "public class "+chunk.chunkname+" extends VarArgFunction {" ); + ++indent; + writeln( "public Varargs invoke(Varargs $args) {"); + ++indent; + writeFunctionBody( chunk.function ); + --indent; + writeln( "}"); + --indent; + // TODO: write out chunk constants + writeln( "}" ); + return popFunctionContext(); + } + + private void pushFunctionContext() { + stringBuffers.push( sb = new StringBuffer() ); + } + + private String popFunctionContext() { + String v = stringBuffers.pop().toString(); + sb = stringBuffers.isEmpty()? null: stringBuffers.lastElement(); + return v; + } + + private void writeFunctionBody(LSFunction function) { + if ( function.hasandlogic || function.hasorlogic ) + writeln( "LuaValue $t;" ); + if ( function.hasvarargassign ) + writeln( "Varargs $v;" ); + writeStatements( function.stats ); + if ( LSStatement.isNextStatementReachable( function.stats ) ) + writeln( "return NONE;" ); + } + + private void writeStatements(List statements) { + for ( LSStatement s : statements ) { + writeStatement( s ); + } + } + + private void writeStatement(LSStatement s) { + if ( s==null ) return; + switch ( s.type ) { + case functionCall: writeFunctionCall( (LSStatement.FunctionCall) s ); break; + case doBlock: writeDoBlock( (LSStatement.DoBlock) s ); break; + case whileLoop: writeWhileLoop( (LSStatement.WhileLoop) s ); break; + case repeatUntil: writeRepeatUntil( (LSStatement.RepeatUntil) s ); break; + case varAssign: writeVarAssign( (LSStatement.VarAssign) s ); break; + case forLoop: writeForLoop( (LSStatement.ForLoop) s ); break; + case forList: writeForList( (LSStatement.ForList) s ); break; + case varNamedFunction: writeVarNamedFunction((LSStatement.VarNamedFunction) s ); break; + case localFunction: writeLocalFunction( (LSStatement.LocalFunction) s ); break; + case localAssign: writeLocalAssign( (LSStatement.LocalAssign) s ); break; + case returnStat: writeReturnStat( (LSStatement.ReturnStat) s ); break; + case breakStat: writeBreakStat( (LSStatement.BreakStat) s ); break; + case ifStat: writeIfStat( (LSIfStatement) s ); break; + } + } + + private void writeFunctionCall(FunctionCall s) { + writeindent(); + write( eval(s.variable)+";" ); + writeln(); + } + + private void writeDoBlock(DoBlock s) { + writeln( "{" ); + ++indent; + writeStatements( s.statements ); + --indent; + writeln( "}" ); + } + + private void writeWhileLoop(WhileLoop s) { + writeln( "while ("+eval(s.condition)+".toboolean()) {" ); + ++indent; + writeStatements( s.statements ); + --indent; + writeln( "}" ); + } + + private void writeRepeatUntil(RepeatUntil s) { + writeln( "do {" ); + ++indent; + writeStatements( s.statements ); + --indent; + writeln( "} while (!"+eval(s.condition)+");" ); + } + + private void writeForLoop(ForLoop s) { + writeln( "{" ); + ++indent; + // TODO: name handling, also upvalues! + String index = javaName( s.index ); + String limit = javaName( s.scope.declare("$limit") ); + String step = javaName( s.scope.declare("$step") ); + writeln( "LuaValue "+index+"="+eval(s.initial)+";" ); + writeln( "final LuaValue "+limit+"="+eval(s.limit)+";" ); + if ( s.step != null ) { + writeln( "final LuaValue "+step+"="+eval(s.step)+";" ); + writeln( "final boolean "+step+"$b="+step+".gt_b(0);" ); + } + if ( s.step != null ) { + writeln( "for ( ; "+index+".testfor_b("+limit+","+step+"$b); "+index+"="+index+".add("+step+") ) {" ); + } else { + writeln( "for ( ; "+index+".lteq_b("+limit+"); "+index+"="+index+".add(1) ) {" ); + } + ++indent; + writeStatements( s.statements ); + --indent; + writeln( "}" ); + --indent; + writeln( "}" ); + } + + private void writeForList(ForList s) { + writeln( "{" ); + ++indent; + List exprs = evalExpressions(s.expressions, 3, s.scope); + // TODO: upvalues handling! + String fun = javaName( s.scope.declare("$f") ); + String sta = javaName( s.scope.declare("$s") ); + String var = javaName( s.scope.declare("$var") ); + String res = javaName( s.scope.declare("$res") ); + writeln( "LuaValue "+fun+"="+exprs.get(0)+";" ); + writeln( "LuaValue "+sta+"="+exprs.get(1)+";" ); + writeln( "LuaValue "+var+"="+exprs.get(2)+";" ); + writeln( "while ( true ) {" ); + ++indent; + writeln( "Varargs "+res+" = "+fun+".invoke(varargsOf("+sta+","+var+"));" ); + for ( int i=1, n=s.names.size(); i<=n; i++ ) + writeln( "LuaValue "+javaName(s.names.get(i-1))+"="+res+".arg("+i+");" ); + writeln( var+"="+javaName(s.names.get(0))+";" ); + writeln( "if ( "+var+".isnil() ) break;" ); + writeStatements( s.statements ); + --indent; + writeln( "}" ); + --indent; + writeln( "}" ); + } + + private final static Set reserved = new HashSet(); + static { + String[] specials = { + // keywors used by our code generator + "name", "opcode", "env", + + // java keywords + "abstract", "continue", "for", "new", "switch", + "assert", "default", "goto", "package", "synchronized", + "boolean", "do", "if", "private", "this", + "break", "double", "implements", "protected", "throw", + "byte", "else", "import", "public", "throws", + "case", "enum", "instanceof", "return", "transient", + "catch", "extends", "int", "short", "try", + "char", "final", "interface", "static", "void", + "class", "finally", "long", "strictfp", "volatile", + "const", "float", "native", "super", "while", + }; + for ( int i=0; i0? + name.luaname+"$"+name.innerrevision: + reserved.contains(name.luaname)? (name.luaname+"$"): name.luaname; + } + + private void writeVarNamedFunction(VarNamedFunction s) { + String funcdef = evalFuncbody(s.funcbody); + writeAssign( s.funcname, funcdef ); + } + + private String evalFuncbody(LSFunction funcbody) { + pushFunctionContext(); + int n = funcbody.paramnames!=null? funcbody.paramnames.size(): 0; + boolean isvararg = (funcbody.isvararg || n > 3); + if ( isvararg ) { + write( "new VarArgFunction(env) {\n" ); + ++indent; + writeln( "public Varargs invoke(Varargs $args) {" ); + ++indent; + for ( int i=0; i 0 && funcbody.usesvarargs) || funcbody.needsarg ) + writeln( "$args = $args.subargs("+(n+1)+");" ); + if ( funcbody.needsarg ) + writeln( "LuaValue arg = new LuaTable($args);" ); + else if ( funcbody.hasarg ) + writeln( "LuaValue arg = NIL;" ); + writeFunctionBody(funcbody); + --indent; + writeln( "}" ); + --indent; + writeindent(); + write( "}" ); + } else { + write( + n==0? "new ZeroArgFunction(env) {\n": + n==1? "new OneArgFunction(env) {\n": + n==2? "new TwoArgFunction(env) {\n": + "new ThreeArgFunction(env) {\n" ); + ++indent; + writeindent(); + write( "public LuaValue call("); + for ( int i=0; i0) write( "," ); + Name name = funcbody.paramnames.get(i); + if ( name.isupvalue ) + write( "LuaValue "+javaName(name)+"$u" ); + else + write( "LuaValue "+javaName(name) ); + } + write( ") {" ); + writeln(); + ++indent; + + // upvalues + for ( int i=0; i exprs = evalExpressions(s.expressions, nassign, s.scope); + for ( int i=0; i exprs = evalExpressions(s.expressions, nassign, s.scope); + for ( int i=0; i evalExpressions(List exprs, int nassign, Scope scope) { + int nexpr = (exprs!=null? exprs.size(): 0); + List e = new ArrayList(nexpr); + boolean hasvarargs = false; + for ( int i=0; i1); + if ( hasvarargs ) + e.add( "($v="+eval(exprs.get(i))+").arg1()" ); + else + e.add( eval(exprs.get(i)) ); + } else if (hasvarargs) { + e.add( "$v.arg("+(i-nexpr+2)+")" ); + } else { + e.add( "NIL" ); + } + } + return e; + } + + + private void writeReturnStat(ReturnStat s) { + int n = s.expressions!=null? s.expressions.size(): 0; + if ( ! s.function.isvararg ) + writeln( n==0? "return NONE;": "return "+eval(s.expressions.get(0))+";" ); + else { + writeindent(); + switch ( n ) { + case 0: + write( "return NONE;" ); + break; + case 1: + write( "return "+eval( s.expressions.get(0))+";" ); + break; + case 2: case 3: { + write( "return varargsOf(" ); + for ( int i=0; i0) write( "," ); + write( eval( s.expressions.get(i)) ); + } + write( ");" ); + break; + } + default: { + write( "return varargsOf(new LuaValue[] {" ); + for ( int i=0; i0) write( "," ); + write( eval( s.expressions.get(i)) ); + } + write( "},"+eval(s.expressions.get(n-1))+");" ); + break; + } + } + writeln(); + } + } + + private void writeBreakStat(BreakStat s) { + writeln( "break;" ); + } + + private void writeIfStat(LSIfStatement s) { + writeln( "if ("+eval_bool(s.condition)+") {" ); + ++indent; + writeStatements( s.statements ); + if ( s.elseifs != null ) { + for ( LSIfStatement.ElseIf elseif : s.elseifs ) { + --indent; + writeln( "} else if ("+eval_bool(elseif.condition)+") {" ); + ++indent; + writeStatements( elseif.statements ); + } + } + if ( s.elsestatements != null ) { + --indent; + writeln( "} else {" ); + ++indent; + writeStatements( s.elsestatements ); + } + --indent; + writeln( "}" ); + } + + //------------------------------------------- + // assignment using variables + //------------------------------------------- + + /** Write assignment of a particular variable value */ + private void writeAssign(LSVariable v, String expression) { + switch ( v.type ) { + case nameVariable: writeNameAssign( (LSVariable.NameReference) v, expression); break; + case fieldVariable: writeFieldAssign( (LSVariable.Field) v, expression); break; + case methodVariable: writeMethodAssign( (LSVariable.Method) v, expression); break; + case parenthesesVariable: writeParenAssign( (LSVariable.Parentheses) v, expression); break; + case indexVariable: writeIndexAssign( (LSVariable.Index) v, expression); break; + case callFunctionVariable: writeCallFuncAssign((LSVariable.CallFunction)v, expression); break; + case callMethodVariable: writeCallMethAssign((LSVariable.CallMethod) v, expression); break; + } + } + + private void writeNameAssign(NameReference v, String expression) { + if ( v.name.isGlobal() ) + writeln( "env.set(\""+v.name.luaname+"\","+expression+");"); + else if ( v.name.isupvalue ) + writeln( javaName(v.name)+"[0]="+expression+";"); + else + writeln( javaName(v.name)+"="+expression+";"); + } + + private void writeFieldAssign(Field v, String expression) { + String base = eval(v.variable); + writeln( base+".set(\""+v.field+"\","+expression+");"); + } + + private void writeMethodAssign(Method v, String expression) { + String base = eval(v.variable); + writeln( base+".set(\""+v.method+"\","+expression+");"); + } + + private void writeParenAssign(Parentheses v, String expression) { + throw new IllegalArgumentException("no assignment for parenthesis expressions"); + } + + private void writeIndexAssign(Index v, String expression) { + String base = eval(v.variable); + writeln( base+".set("+eval(v.expression)+","+expression+");"); + } + + private void writeCallFuncAssign(CallFunction v, String expression) { + throw new IllegalArgumentException("no assignment for call function expressions"); + } + + private void writeCallMethAssign(CallMethod v, String expression) { + throw new IllegalArgumentException("no assignment for call method expressions"); + } + + //------------------------------------------- + // write out expressions + //------------------------------------------- + + private String eval_bool(LSExpression e) { + return eval(e)+".toboolean()"; + } + + /** evaluate the expression to a particular operand type */ + private String eval(LSExpression e) { + if ( e==null ) return "NONE"; + switch ( e.type ) { + case nilConstant: return "NIL"; + case trueConstant: return "TRUE"; + case falseConstant: return "FALSE"; + case unop: return evalUnop( (LSExpression.UnopExpr) e); + case binop: return evalBinop( (LSExpression.BinopExpr) e); + case functionExpr: return evalFunction((LSExpression.FunctionExpr) e); + case tableConstructor: return evalTable( (LSExpression.TableConstructor) e); + case numberConstant: return evalNumber( (LSExpression.NumberConstant) e); + case stringConstant: return evalString( (LSExpression.StringConstant) e); + + // variable types + case nameVariable: return evalNameRef( (LSVariable.NameReference) e); + case fieldVariable: return evalField( (LSVariable.Field) e); + case methodVariable: return evalMethod( (LSVariable.Method) e); + case parenthesesVariable: return evalParen( (LSVariable.Parentheses) e); + case indexVariable: return evalIndex( (LSVariable.Index) e); + case callFunctionVariable: return evalCallFunc((LSVariable.CallFunction) e); + case callMethodVariable: return evalCallMeth((LSVariable.CallMethod) e); + case varargsRef: return evalVarargs( (LSExpression.VarargsRef) e); + + default: throw new IllegalArgumentException("unknown expression type: "+e.type); + } + } + + private String evalUnop(UnopExpr e) { + switch ( e.op.type ) { + case neg: return eval( e.rhs )+".neg()"; + case not: return eval( e.rhs )+".not()"; + case len: return eval( e.rhs )+".len()"; + } + throw new IllegalArgumentException("unknown unary operand: "+e.op ); + } + + private String evalBinop(BinopExpr e) { + switch ( e.op.type ) { + case pow: return eval(e.lhs)+".pow("+eval(e.rhs)+")"; + case mul: return eval(e.lhs)+".mul("+eval(e.rhs)+")"; + case div: return eval(e.lhs)+".div("+eval(e.rhs)+")"; + case mod: return eval(e.lhs)+".mod("+eval(e.rhs)+")"; + case add: return eval(e.lhs)+".add("+eval(e.rhs)+")"; + case sub: return eval(e.lhs)+".sub("+eval(e.rhs)+")"; + case concat: return eval(e.lhs)+".concat("+eval(e.rhs)+")"; + case lt: return eval(e.lhs)+".lt("+eval(e.rhs)+")"; + case lteq: return eval(e.lhs)+".lteq("+eval(e.rhs)+")"; + case gt: return eval(e.lhs)+".gt("+eval(e.rhs)+")"; + case gteq: return eval(e.lhs)+".gteq("+eval(e.rhs)+")"; + case eq: return eval(e.lhs)+".eq("+eval(e.rhs)+")"; + case neq: return eval(e.lhs)+".neq("+eval(e.rhs)+")"; + case and: return "(($t="+eval(e.lhs)+").toboolean()? "+eval(e.rhs)+": $t)"; + case or: return "(($t="+eval(e.lhs)+").toboolean()? $t: "+eval(e.rhs)+")"; + default: throw new IllegalStateException("unknoqn binary operator: "+e.op); + } + } + + private String evalFunction(FunctionExpr e) { + return evalFuncbody(e.function); + } + + private String evalTable(TableConstructor e) { + StringBuffer named = new StringBuffer(); + StringBuffer numbered = new StringBuffer(); + LSExpression varargsLastListValue = null; + for ( int i=0, n=e.fields.size(); i0? "new LuaValue[] {"+named +"}": "null")+"," + +(numbered.length()>0? "new LuaValue[] {"+numbered+"}": "null") + +(varargsLastListValue!=null? ","+eval(varargsLastListValue): "")+")"; + } + + private String evalNumber(NumberConstant e) { + // TODO: internalize constants + return "valueOf("+e.value+")"; + } + + private String evalString(StringConstant e) { + // TODO: internalize constants + return "valueOf("+toStrValueInitializer(e.bytes)+")"; + } + + private String toStrValueInitializer(byte[] bytes) { + int n = bytes.length; + StringBuffer sb = new StringBuffer(n+2); + + // check for characters beyond ascii 128 + for ( int i=0; i0 ) sb.append(","); + byte b = bytes[j]; + switch ( b ) { + case '\n': sb.append( "'\\n'" ); break; + case '\r': sb.append( "'\\r'" ); break; + case '\t': sb.append( "'\\t'" ); break; + case '\\': sb.append( "'\\\\'" ); break; + default: + if ( b >= ' ' ) { + sb.append( '\''); + sb.append( (char) b ); + sb.append( '\''); + } else { + sb.append( String.valueOf((int)b) ); + } + break; + } + } + sb.append( "}" ); + return sb.toString(); + } + + sb.append('"'); + for ( int i=0; i= ' ' ) { + sb.append( (char) b ); break; + } else { + // convert from UTF-8 + int u = 0xff & (int) b; + if ( u>=0xc0 && i+1=0xe0 && i+2 1; + boolean isVarargsCall = n>0 && e.parameters.get(n-1).getNumReturns() < 0; + String base = eval(e.variable); + if ( n <= 3 && !isVarargsReturn && !isVarargsCall ) { + return base+".call("+evalParamList(e.parameters)+")"; + } else { + String coerce = e.numReturns==1? ".arg1()": ""; + switch ( n ) { + case 0: + return base+".invoke()"+coerce; + case 1: + case 2: + case 3: + return base+".invoke("+evalParamList(e.parameters)+")"+coerce; + default: + if ( isVarargsCall ) { + LSExpression last = e.parameters.remove(n-1); + return base+".invoke(new LuaValue[]{"+evalParamList(e.parameters)+"},"+eval(last)+")"+coerce; + } else { + return base+".invoke(new LuaValue[]{"+evalParamList(e.parameters)+"})"+coerce; + } + } + } + } + + private String evalCallMeth(CallMethod e) { + int n = e.parameters.size(); + String base = eval(e.variable); + if ( n <= 3 && e.numReturns == 0 || e.numReturns == 1 ) { + return base+".method(\""+e.method+"\""+(e.parameters.size()>0? ",": "")+evalParamList(e.parameters)+")"; + } else { + return base+".invokemethod(\""+e.method+"\",new LuaValue[]{"+evalParamList(e.parameters)+"})"; + } + } + + private String evalVarargs(VarargsRef e) { + switch ( e.numReturns ) { + case 0: return "NIL"; + case 1: return "$args.arg1()"; + default: return "$args"; + } + } + + private String evalParamList(List parameters) { + if ( parameters == null || parameters.size() == 0 ) + return ""; + StringBuffer p = new StringBuffer(); + for ( int i=0, n=parameters.size(); i0) p.append(","); + p.append( eval( parameters.get(i) ) ); + } + return p.toString(); + } + + + //------------------------------------------- + // write individual strings and lines + //------------------------------------------- + + + private void writeindent() { + for ( int i=0; i dest = Arrays.asList(new File[] { new File("bin") }); + StandardJavaFileManager fm = compiler.getStandardFileManager( null, null, null); + fm.setLocation(StandardLocation.CLASS_OUTPUT, dest); + + // compile the file + Iterable compilationUnits = fm.getJavaFileObjects(source); + CompilationTask task = compiler.getTask(null, fm, null, null, null, compilationUnits); + boolean success = task.call(); + + // instantiate, config and return + if (success) { + // compile sub-prototypes +// if ( p.p != null ) { +// for ( int i=0, n=p.p.length; i stats] + @init { $stats = new ArrayList(); } + : (stat {$stats.add($stat.s);} (';')?)* + (laststat {$stats.add($laststat.s);} (';')?)? + ; + +stat returns [LSStatement s] + @init { Name name=null; List names=null; } + : varlist1 '=' explist1 { $s=LSStatement.varAssignStatement($varlist1.vars,$explist1.exprs, CHK.peekScope(), $funcblock::func); } + | functioncall { $s=LSStatement.functionCallStatement($functioncall.v); } + | 'do' {CHK.pushScope("do");} block {CHK.popScope("do");} 'end' + { $s=LSStatement.doBlockStatement($block.stats); } + | 'while' exp 'do' {CHK.pushScope("while");} block {CHK.popScope("while");} 'end' + { $s=LSStatement.whileLoopStatement($exp.e,$block.stats); } + | 'repeat' {CHK.pushScope("repeat");} block {CHK.popScope("repeat");} 'until' exp + { $s=LSStatement.repeatUntilStatement($block.stats,$exp.e); } + | ifstat { $s=$ifstat.s; } + | 'for' {CHK.pushScope("fori");} NAME {name=CHK.declare($NAME.text);} '=' e1=exp ',' e2=exp (',' e3=exp)? 'do' {CHK.pushScope("foriblock");} block {CHK.popScope("foriblock");} 'end' + { $s=LSStatement.forLoopStatement(name,$e1.e,$e2.e,$e3.e,$block.stats,CHK.peekScope()); CHK.popScope("fori"); } + | 'for' {CHK.pushScope("for");} namelist {names=CHK.declare($namelist.names);} 'in' explist1 'do' {CHK.pushScope("forblock");} block {CHK.popScope("forblock");}'end' + { $s=LSStatement.forListStatement(names,$explist1.exprs,$block.stats,CHK.peekScope(), $funcblock::func); CHK.popScope("for");} + | 'function' funcname funcbody { $s=LSStatement.varFunctionStatement($funcname.v,$funcbody.f); } + | 'local' 'function' NAME {name=CHK.declare($NAME.text);} funcbody + { $s=LSStatement.localFunctionStatement(name,$funcbody.f); } + | 'local' namelist ('=' explist1)? { $s=LSStatement.localAssignStatement(CHK.declare($namelist.names),$explist1.exprs,CHK.peekScope(), $funcblock::func); } + ; + +ifstat returns [LSStatement s] + scope { LSIfStatement current; } + : 'if' e1=exp 'then' {CHK.pushScope("if");} b1=block {$ifstat::current=new LSIfStatement($e1.e,$b1.stats); CHK.popScope("if");} + ('elseif' e2=exp 'then' {CHK.pushScope("elseif");} b2=block {$ifstat::current.addElseif($e2.e,$b2.stats); CHK.popScope("elseif");})* + ('else' {CHK.pushScope("else");} b3=block {$ifstat::current.addElse($b3.stats); CHK.popScope("else");})? + 'end' + { $s=$ifstat::current; } + ; + +laststat returns [LSStatement s] + : 'return' (e=explist1)? {$s=LSStatement.returnStatement($funcblock::func,$e.exprs);} + | 'break' {$s=LSStatement.breakStatement();} + ; + +funcname returns [LSVariable v] + : n=NAME {$v = LSVariable.nameVariable(CHK.reference($n.text,$funcblock::func));} + ('.' n2=NAME {$v = $v.fieldVariable($n2.text);})* + (':' n3=NAME {$v = $v.methodVariable($n3.text);})? + ; + +varlist1 returns [List vars] + @init { $vars = new ArrayList(); } + : v1=var {$vars.add($v1.v);} + (',' v2=var {$vars.add($v2.v);})* + ; + +namelist returns [List names] + : n=NAME {$names=new ArrayList(); $names.add($n.text);} + (',' n2=NAME {$names.add($n2.text);})* + ; + +explist1 returns [List exprs] + @init { $exprs = new ArrayList(); } + : (e1=exp ',' {$exprs.add($e1.e);})* + e2=exp {$exprs.add($e2.e);} + ; + +exp returns [LSExpression e] + : ('nil' { $e=LSExpression.ENIL; } + | 'false' { $e=LSExpression.EFALSE; } + | 'true' { $e=LSExpression.ETRUE; } + | number { $e=LSExpression.numberExpression($number.text); } + | string { $e=$string.e; } + | '...' { $e=LSExpression.varargsRef(); $funcblock::func.setUsesVarargs(); } + | function { $e=LSExpression.functionExpression($function.f); } + | prefixexp { $e=$prefixexp.v; } + | tableconstructor { $e=$tableconstructor.e;} + | unop e1=exp { $e=LSExpression.unopExpression($unop.op,$e1.e,CHK.peekScope());} + ) (binop e2=exp { $e=LSExpression.binopExpression($e,$binop.op,$e2.e,CHK.peekScope());})* + ; + +var returns [LSVariable v] + scope { LSVariable current; } + : (n=NAME {$var::current=LSVariable.nameVariable(CHK.reference($n.text,$funcblock::func));} + | '(' exp ')' {$var::current=LSVariable.parenthesesVariable($exp.e);} varSuffix) + varSuffix* + {$v=$var::current;} + ; + +varSuffix + : (n=nameAndArgs[$var::current] {$var::current=$n.v;})* + ('[' e=exp ']' {$var::current=$var::current.indexVariable($e.e);} + | '.' n2=NAME {$var::current=$var::current.fieldVariable($n2.text);} + ) + ; + +prefixexp returns [LSVariable v] + scope { LSVariable current; } + : e=varOrExp {$prefixexp::current=$e.v;} + (n=nameAndArgs[$prefixexp::current] {$prefixexp::current=$n.v;})* + {$v=$prefixexp::current;} + ; + +functioncall returns [LSVariable v] + scope { LSVariable current; } + : e=varOrExp {$functioncall::current=$e.v;} + (n=nameAndArgs[$functioncall::current] {$functioncall::current=$n.v;})+ + {$v=$functioncall::current;} + ; + +varOrExp returns [LSVariable v] + : var {$v=$var.v;} + | '(' exp ')' {$v=LSVariable.parenthesesVariable($exp.e);} + ; + +nameAndArgs [LSVariable vin] returns [LSVariable v] + @init { String method=null; } + : (':' n=NAME {method=$n.text;})? + a=args {$v=((method==null)? + $vin.callFuncVariable($a.exprs): + $vin.callMethVariable(method,$a.exprs));} + ; + +args returns [List exprs] + @init { $exprs = new ArrayList(); } + : '(' (e=explist1 {$exprs=$e.exprs;})? ')' + | t=tableconstructor {$exprs.add($t.e);} + | s=string {$exprs.add($s.e);} + ; + +function returns [LSFunction f] + : 'function' b=funcbody {$f = $b.f;} + ; + +funcbody returns [LSFunction f] + @init { + $f = new LSFunction(); + $funcblock::func.functions.add($f); + } + : {CHK.pushScope("func",true);} '(' (parlist1 [f])? ')' funcblock[f] 'end' {CHK.popScope("func");} + ; + +parlist1 [LSFunction f] + : namelist {f.setParameterNames(CHK.declare($namelist.names));} (',' '...' {f.isvararg=true;})? + | '...' {f.isvararg=true;} + ; + +tableconstructor returns [LSExpression e] + @init { List fields = new ArrayList(); } + : '{' (fieldlist[fields])? '}' {$e=LSExpression.tableConstructorExpression(fields);} + ; + +fieldlist [List fields] + : field [fields] (fieldsep field [fields])* (fieldsep)? + ; + +field [List fields] + : '[' k=exp ']' '=' e=exp {$fields.add(LSField.keyValueField($k.e,$e.e));} + | n=NAME '=' e=exp {$fields.add(LSField.nameValueField($n.text,$e.e));} + | e=exp {$fields.add(LSField.valueField($e.e));} + ; + +fieldsep + : ',' + | ';' + ; + +binop returns [BinOp op] + : '+' {$op=BinOp.ADD;} + | '-' {$op=BinOp.SUB;} + | '*' {$op=BinOp.MUL;} + | '/' {$op=BinOp.DIV;} + | '^' {$op=BinOp.POW;} + | '%' {$op=BinOp.MOD;} + | '..' {$op=BinOp.CONCAT;} + | '<' {$op=BinOp.LT;} + | '<=' {$op=BinOp.LTEQ;} + | '>' {$op=BinOp.GT;} + | '>=' {$op=BinOp.GTEQ;} + | '==' {$op=BinOp.EQ;} + | '~=' {$op=BinOp.NEQ;} + | 'and' {$op=BinOp.AND; $funcblock::func.hasandlogic=true;} + | 'or' {$op=BinOp.OR; $funcblock::func.hasorlogic=true;} + ; + +unop returns [UnOp op] + : '-' {$op=UnOp.NEG;} + | 'not' {$op=UnOp.NOT;} + | '#' {$op=UnOp.LEN;} + ; + +number + : ('-')? INT + | ('-')? FLOAT1 + | ('-')? FLOAT2 + | ('-')? FLOAT3 + | ('-')? EXP + | HEX + ; + +string returns [LSExpression e] + : NORMALSTRING {$e=LSExpression.normalStringExpression($NORMALSTRING.text);} + | CHARSTRING {$e=LSExpression.charStringExpression($CHARSTRING.text);} + | LONGSTRING {$e=LSExpression.longStringExpression($LONGSTRING.text);} + ; + + +// LEXER + + +NAME :('a'..'z'|'A'..'Z'|'_')(options{greedy=true;}: 'a'..'z'|'A'..'Z'|'_'|'0'..'9')* + ; + +INT : ('0'..'9')+; + +FLOAT1 :'.' INT ; + +FLOAT2 :INT '.' ; + +FLOAT3 :INT '.' INT ; + +EXP : (INT | FLOAT1 | FLOAT2 | FLOAT3) ('E'|'e') ('-'|'+')? INT; + +HEX :'0' ('x' | 'X') ('0'..'9'| 'a'..'f' | 'A'..'F')+ ; + + + +NORMALSTRING + : '"' ( EscapeSequence | ~('\\'|'"') )* '"' + ; + +CHARSTRING + : '\'' ( EscapeSequence | ~('\\'|'\'') )* '\'' + ; + +LONGSTRING + : '['('=')*'[' ( EscapeSequence | ~('\\'|']') )* ']'('=')*']' + ; + +fragment +EscapeSequence + : '\\' ('a'|'b'|'f'|'n'|'r'|'t'|'v'|'\"'|'\''|'\\'|'\n') + | DecimalEscape + ; + +fragment +DecimalEscape + : '\\' ('0'..'9') (('0'..'9') ('0'..'9')?)? + ; + + +COMMENT + : '--[[' ( options {greedy=false;} : . )* '--]]' {skip();} + | '--[=[' ( options {greedy=false;} : . )* '--]==]' {skip();} + | '--[==[' ( options {greedy=false;} : . )* '--]==]' {skip();} + | '--[===[' ( options {greedy=false;} : . )* '--]===]' {skip();} + ; + +LINE_COMMENT + : '--' ~('\n'|'\r')* '\r'? '\n' {skip();} + ; + + +WS : (' '|'\t'|'\u000C') {skip();} + ; + +NEWLINE : ('\r')? '\n' {skip();} + ; diff --git a/src/jse/org/luaj/vm2/luajc/antlr/Lua.tokens b/src/jse/org/luaj/vm2/luajc/antlr/Lua.tokens new file mode 100644 index 00000000..4b8d63cd --- /dev/null +++ b/src/jse/org/luaj/vm2/luajc/antlr/Lua.tokens @@ -0,0 +1,110 @@ +T__66=66 +T__64=64 +T__29=29 +T__65=65 +T__28=28 +T__62=62 +T__27=27 +T__63=63 +T__26=26 +T__25=25 +T__24=24 +T__23=23 +T__22=22 +T__21=21 +T__20=20 +T__61=61 +T__60=60 +FLOAT3=8 +FLOAT2=7 +FLOAT1=6 +T__55=55 +T__56=56 +T__57=57 +NAME=4 +T__58=58 +T__51=51 +T__52=52 +T__53=53 +T__54=54 +EXP=9 +HEX=10 +T__59=59 +DecimalEscape=15 +COMMENT=16 +T__50=50 +T__42=42 +T__43=43 +T__40=40 +T__41=41 +T__46=46 +T__47=47 +T__44=44 +T__45=45 +LINE_COMMENT=17 +T__48=48 +T__49=49 +CHARSTRING=12 +INT=5 +LONGSTRING=13 +T__30=30 +NORMALSTRING=11 +T__31=31 +T__32=32 +WS=18 +T__33=33 +T__34=34 +NEWLINE=19 +T__35=35 +T__36=36 +T__37=37 +T__38=38 +T__39=39 +EscapeSequence=14 +'..'=56 +'end'=23 +'#'=66 +'>='=60 +'=='=61 +'/'=53 +'then'=33 +'>'=59 +'repeat'=25 +';'=20 +'='=21 +'return'=36 +'for'=27 +'+'=50 +')'=45 +'function'=30 +'.'=38 +'^'=54 +'%'=55 +'do'=22 +'elseif'=34 +'true'=42 +'}'=49 +'else'=35 +'and'=63 +'break'=37 +'{'=48 +'...'=43 +'~='=62 +'nil'=40 +'until'=26 +'<='=58 +'false'=41 +'<'=57 +'if'=32 +'not'=65 +':'=39 +'('=44 +'or'=64 +'*'=52 +'-'=51 +'['=46 +'while'=24 +'local'=31 +','=28 +'in'=29 +']'=47 diff --git a/src/jse/org/luaj/vm2/luajc/antlr/LuaLexer.java b/src/jse/org/luaj/vm2/luajc/antlr/LuaLexer.java new file mode 100644 index 00000000..66825aef --- /dev/null +++ b/src/jse/org/luaj/vm2/luajc/antlr/LuaLexer.java @@ -0,0 +1,3814 @@ +// $ANTLR 3.1.3 Mar 17, 2009 19:23:44 Lua.g 2009-10-19 10:13:34 + + package org.luaj.vm2.luajc.antlr; + + +import org.antlr.runtime.*; +import java.util.Stack; +import java.util.List; +import java.util.ArrayList; + +public class LuaLexer extends Lexer { + public static final int T__66=66; + public static final int T__64=64; + public static final int T__29=29; + public static final int T__28=28; + public static final int T__65=65; + public static final int T__27=27; + public static final int T__62=62; + public static final int T__26=26; + public static final int T__63=63; + public static final int T__25=25; + public static final int T__24=24; + public static final int T__23=23; + public static final int T__22=22; + public static final int T__21=21; + public static final int T__20=20; + public static final int T__61=61; + public static final int EOF=-1; + public static final int T__60=60; + public static final int FLOAT3=8; + public static final int FLOAT2=7; + public static final int FLOAT1=6; + public static final int T__55=55; + public static final int T__56=56; + public static final int T__57=57; + public static final int NAME=4; + public static final int T__58=58; + public static final int T__51=51; + public static final int T__52=52; + public static final int T__53=53; + public static final int T__54=54; + public static final int EXP=9; + public static final int HEX=10; + public static final int T__59=59; + public static final int DecimalEscape=15; + public static final int COMMENT=16; + public static final int T__50=50; + public static final int T__42=42; + public static final int T__43=43; + public static final int T__40=40; + public static final int T__41=41; + public static final int T__46=46; + public static final int T__47=47; + public static final int T__44=44; + public static final int T__45=45; + public static final int LINE_COMMENT=17; + public static final int T__48=48; + public static final int T__49=49; + public static final int INT=5; + public static final int CHARSTRING=12; + public static final int LONGSTRING=13; + public static final int T__30=30; + public static final int T__31=31; + public static final int NORMALSTRING=11; + public static final int T__32=32; + public static final int T__33=33; + public static final int WS=18; + public static final int T__34=34; + public static final int T__35=35; + public static final int NEWLINE=19; + public static final int T__36=36; + public static final int T__37=37; + public static final int T__38=38; + public static final int T__39=39; + public static final int EscapeSequence=14; + + // delegates + // delegators + + public LuaLexer() {;} + public LuaLexer(CharStream input) { + this(input, new RecognizerSharedState()); + } + public LuaLexer(CharStream input, RecognizerSharedState state) { + super(input,state); + + } + public String getGrammarFileName() { return "Lua.g"; } + + // $ANTLR start "T__20" + public final void mT__20() throws RecognitionException { + try { + int _type = T__20; + int _channel = DEFAULT_TOKEN_CHANNEL; + // Lua.g:7:7: ( ';' ) + // Lua.g:7:9: ';' + { + match(';'); + + } + + state.type = _type; + state.channel = _channel; + } + finally { + } + } + // $ANTLR end "T__20" + + // $ANTLR start "T__21" + public final void mT__21() throws RecognitionException { + try { + int _type = T__21; + int _channel = DEFAULT_TOKEN_CHANNEL; + // Lua.g:8:7: ( '=' ) + // Lua.g:8:9: '=' + { + match('='); + + } + + state.type = _type; + state.channel = _channel; + } + finally { + } + } + // $ANTLR end "T__21" + + // $ANTLR start "T__22" + public final void mT__22() throws RecognitionException { + try { + int _type = T__22; + int _channel = DEFAULT_TOKEN_CHANNEL; + // Lua.g:9:7: ( 'do' ) + // Lua.g:9:9: 'do' + { + match("do"); + + + } + + state.type = _type; + state.channel = _channel; + } + finally { + } + } + // $ANTLR end "T__22" + + // $ANTLR start "T__23" + public final void mT__23() throws RecognitionException { + try { + int _type = T__23; + int _channel = DEFAULT_TOKEN_CHANNEL; + // Lua.g:10:7: ( 'end' ) + // Lua.g:10:9: 'end' + { + match("end"); + + + } + + state.type = _type; + state.channel = _channel; + } + finally { + } + } + // $ANTLR end "T__23" + + // $ANTLR start "T__24" + public final void mT__24() throws RecognitionException { + try { + int _type = T__24; + int _channel = DEFAULT_TOKEN_CHANNEL; + // Lua.g:11:7: ( 'while' ) + // Lua.g:11:9: 'while' + { + match("while"); + + + } + + state.type = _type; + state.channel = _channel; + } + finally { + } + } + // $ANTLR end "T__24" + + // $ANTLR start "T__25" + public final void mT__25() throws RecognitionException { + try { + int _type = T__25; + int _channel = DEFAULT_TOKEN_CHANNEL; + // Lua.g:12:7: ( 'repeat' ) + // Lua.g:12:9: 'repeat' + { + match("repeat"); + + + } + + state.type = _type; + state.channel = _channel; + } + finally { + } + } + // $ANTLR end "T__25" + + // $ANTLR start "T__26" + public final void mT__26() throws RecognitionException { + try { + int _type = T__26; + int _channel = DEFAULT_TOKEN_CHANNEL; + // Lua.g:13:7: ( 'until' ) + // Lua.g:13:9: 'until' + { + match("until"); + + + } + + state.type = _type; + state.channel = _channel; + } + finally { + } + } + // $ANTLR end "T__26" + + // $ANTLR start "T__27" + public final void mT__27() throws RecognitionException { + try { + int _type = T__27; + int _channel = DEFAULT_TOKEN_CHANNEL; + // Lua.g:14:7: ( 'for' ) + // Lua.g:14:9: 'for' + { + match("for"); + + + } + + state.type = _type; + state.channel = _channel; + } + finally { + } + } + // $ANTLR end "T__27" + + // $ANTLR start "T__28" + public final void mT__28() throws RecognitionException { + try { + int _type = T__28; + int _channel = DEFAULT_TOKEN_CHANNEL; + // Lua.g:15:7: ( ',' ) + // Lua.g:15:9: ',' + { + match(','); + + } + + state.type = _type; + state.channel = _channel; + } + finally { + } + } + // $ANTLR end "T__28" + + // $ANTLR start "T__29" + public final void mT__29() throws RecognitionException { + try { + int _type = T__29; + int _channel = DEFAULT_TOKEN_CHANNEL; + // Lua.g:16:7: ( 'in' ) + // Lua.g:16:9: 'in' + { + match("in"); + + + } + + state.type = _type; + state.channel = _channel; + } + finally { + } + } + // $ANTLR end "T__29" + + // $ANTLR start "T__30" + public final void mT__30() throws RecognitionException { + try { + int _type = T__30; + int _channel = DEFAULT_TOKEN_CHANNEL; + // Lua.g:17:7: ( 'function' ) + // Lua.g:17:9: 'function' + { + match("function"); + + + } + + state.type = _type; + state.channel = _channel; + } + finally { + } + } + // $ANTLR end "T__30" + + // $ANTLR start "T__31" + public final void mT__31() throws RecognitionException { + try { + int _type = T__31; + int _channel = DEFAULT_TOKEN_CHANNEL; + // Lua.g:18:7: ( 'local' ) + // Lua.g:18:9: 'local' + { + match("local"); + + + } + + state.type = _type; + state.channel = _channel; + } + finally { + } + } + // $ANTLR end "T__31" + + // $ANTLR start "T__32" + public final void mT__32() throws RecognitionException { + try { + int _type = T__32; + int _channel = DEFAULT_TOKEN_CHANNEL; + // Lua.g:19:7: ( 'if' ) + // Lua.g:19:9: 'if' + { + match("if"); + + + } + + state.type = _type; + state.channel = _channel; + } + finally { + } + } + // $ANTLR end "T__32" + + // $ANTLR start "T__33" + public final void mT__33() throws RecognitionException { + try { + int _type = T__33; + int _channel = DEFAULT_TOKEN_CHANNEL; + // Lua.g:20:7: ( 'then' ) + // Lua.g:20:9: 'then' + { + match("then"); + + + } + + state.type = _type; + state.channel = _channel; + } + finally { + } + } + // $ANTLR end "T__33" + + // $ANTLR start "T__34" + public final void mT__34() throws RecognitionException { + try { + int _type = T__34; + int _channel = DEFAULT_TOKEN_CHANNEL; + // Lua.g:21:7: ( 'elseif' ) + // Lua.g:21:9: 'elseif' + { + match("elseif"); + + + } + + state.type = _type; + state.channel = _channel; + } + finally { + } + } + // $ANTLR end "T__34" + + // $ANTLR start "T__35" + public final void mT__35() throws RecognitionException { + try { + int _type = T__35; + int _channel = DEFAULT_TOKEN_CHANNEL; + // Lua.g:22:7: ( 'else' ) + // Lua.g:22:9: 'else' + { + match("else"); + + + } + + state.type = _type; + state.channel = _channel; + } + finally { + } + } + // $ANTLR end "T__35" + + // $ANTLR start "T__36" + public final void mT__36() throws RecognitionException { + try { + int _type = T__36; + int _channel = DEFAULT_TOKEN_CHANNEL; + // Lua.g:23:7: ( 'return' ) + // Lua.g:23:9: 'return' + { + match("return"); + + + } + + state.type = _type; + state.channel = _channel; + } + finally { + } + } + // $ANTLR end "T__36" + + // $ANTLR start "T__37" + public final void mT__37() throws RecognitionException { + try { + int _type = T__37; + int _channel = DEFAULT_TOKEN_CHANNEL; + // Lua.g:24:7: ( 'break' ) + // Lua.g:24:9: 'break' + { + match("break"); + + + } + + state.type = _type; + state.channel = _channel; + } + finally { + } + } + // $ANTLR end "T__37" + + // $ANTLR start "T__38" + public final void mT__38() throws RecognitionException { + try { + int _type = T__38; + int _channel = DEFAULT_TOKEN_CHANNEL; + // Lua.g:25:7: ( '.' ) + // Lua.g:25:9: '.' + { + match('.'); + + } + + state.type = _type; + state.channel = _channel; + } + finally { + } + } + // $ANTLR end "T__38" + + // $ANTLR start "T__39" + public final void mT__39() throws RecognitionException { + try { + int _type = T__39; + int _channel = DEFAULT_TOKEN_CHANNEL; + // Lua.g:26:7: ( ':' ) + // Lua.g:26:9: ':' + { + match(':'); + + } + + state.type = _type; + state.channel = _channel; + } + finally { + } + } + // $ANTLR end "T__39" + + // $ANTLR start "T__40" + public final void mT__40() throws RecognitionException { + try { + int _type = T__40; + int _channel = DEFAULT_TOKEN_CHANNEL; + // Lua.g:27:7: ( 'nil' ) + // Lua.g:27:9: 'nil' + { + match("nil"); + + + } + + state.type = _type; + state.channel = _channel; + } + finally { + } + } + // $ANTLR end "T__40" + + // $ANTLR start "T__41" + public final void mT__41() throws RecognitionException { + try { + int _type = T__41; + int _channel = DEFAULT_TOKEN_CHANNEL; + // Lua.g:28:7: ( 'false' ) + // Lua.g:28:9: 'false' + { + match("false"); + + + } + + state.type = _type; + state.channel = _channel; + } + finally { + } + } + // $ANTLR end "T__41" + + // $ANTLR start "T__42" + public final void mT__42() throws RecognitionException { + try { + int _type = T__42; + int _channel = DEFAULT_TOKEN_CHANNEL; + // Lua.g:29:7: ( 'true' ) + // Lua.g:29:9: 'true' + { + match("true"); + + + } + + state.type = _type; + state.channel = _channel; + } + finally { + } + } + // $ANTLR end "T__42" + + // $ANTLR start "T__43" + public final void mT__43() throws RecognitionException { + try { + int _type = T__43; + int _channel = DEFAULT_TOKEN_CHANNEL; + // Lua.g:30:7: ( '...' ) + // Lua.g:30:9: '...' + { + match("..."); + + + } + + state.type = _type; + state.channel = _channel; + } + finally { + } + } + // $ANTLR end "T__43" + + // $ANTLR start "T__44" + public final void mT__44() throws RecognitionException { + try { + int _type = T__44; + int _channel = DEFAULT_TOKEN_CHANNEL; + // Lua.g:31:7: ( '(' ) + // Lua.g:31:9: '(' + { + match('('); + + } + + state.type = _type; + state.channel = _channel; + } + finally { + } + } + // $ANTLR end "T__44" + + // $ANTLR start "T__45" + public final void mT__45() throws RecognitionException { + try { + int _type = T__45; + int _channel = DEFAULT_TOKEN_CHANNEL; + // Lua.g:32:7: ( ')' ) + // Lua.g:32:9: ')' + { + match(')'); + + } + + state.type = _type; + state.channel = _channel; + } + finally { + } + } + // $ANTLR end "T__45" + + // $ANTLR start "T__46" + public final void mT__46() throws RecognitionException { + try { + int _type = T__46; + int _channel = DEFAULT_TOKEN_CHANNEL; + // Lua.g:33:7: ( '[' ) + // Lua.g:33:9: '[' + { + match('['); + + } + + state.type = _type; + state.channel = _channel; + } + finally { + } + } + // $ANTLR end "T__46" + + // $ANTLR start "T__47" + public final void mT__47() throws RecognitionException { + try { + int _type = T__47; + int _channel = DEFAULT_TOKEN_CHANNEL; + // Lua.g:34:7: ( ']' ) + // Lua.g:34:9: ']' + { + match(']'); + + } + + state.type = _type; + state.channel = _channel; + } + finally { + } + } + // $ANTLR end "T__47" + + // $ANTLR start "T__48" + public final void mT__48() throws RecognitionException { + try { + int _type = T__48; + int _channel = DEFAULT_TOKEN_CHANNEL; + // Lua.g:35:7: ( '{' ) + // Lua.g:35:9: '{' + { + match('{'); + + } + + state.type = _type; + state.channel = _channel; + } + finally { + } + } + // $ANTLR end "T__48" + + // $ANTLR start "T__49" + public final void mT__49() throws RecognitionException { + try { + int _type = T__49; + int _channel = DEFAULT_TOKEN_CHANNEL; + // Lua.g:36:7: ( '}' ) + // Lua.g:36:9: '}' + { + match('}'); + + } + + state.type = _type; + state.channel = _channel; + } + finally { + } + } + // $ANTLR end "T__49" + + // $ANTLR start "T__50" + public final void mT__50() throws RecognitionException { + try { + int _type = T__50; + int _channel = DEFAULT_TOKEN_CHANNEL; + // Lua.g:37:7: ( '+' ) + // Lua.g:37:9: '+' + { + match('+'); + + } + + state.type = _type; + state.channel = _channel; + } + finally { + } + } + // $ANTLR end "T__50" + + // $ANTLR start "T__51" + public final void mT__51() throws RecognitionException { + try { + int _type = T__51; + int _channel = DEFAULT_TOKEN_CHANNEL; + // Lua.g:38:7: ( '-' ) + // Lua.g:38:9: '-' + { + match('-'); + + } + + state.type = _type; + state.channel = _channel; + } + finally { + } + } + // $ANTLR end "T__51" + + // $ANTLR start "T__52" + public final void mT__52() throws RecognitionException { + try { + int _type = T__52; + int _channel = DEFAULT_TOKEN_CHANNEL; + // Lua.g:39:7: ( '*' ) + // Lua.g:39:9: '*' + { + match('*'); + + } + + state.type = _type; + state.channel = _channel; + } + finally { + } + } + // $ANTLR end "T__52" + + // $ANTLR start "T__53" + public final void mT__53() throws RecognitionException { + try { + int _type = T__53; + int _channel = DEFAULT_TOKEN_CHANNEL; + // Lua.g:40:7: ( '/' ) + // Lua.g:40:9: '/' + { + match('/'); + + } + + state.type = _type; + state.channel = _channel; + } + finally { + } + } + // $ANTLR end "T__53" + + // $ANTLR start "T__54" + public final void mT__54() throws RecognitionException { + try { + int _type = T__54; + int _channel = DEFAULT_TOKEN_CHANNEL; + // Lua.g:41:7: ( '^' ) + // Lua.g:41:9: '^' + { + match('^'); + + } + + state.type = _type; + state.channel = _channel; + } + finally { + } + } + // $ANTLR end "T__54" + + // $ANTLR start "T__55" + public final void mT__55() throws RecognitionException { + try { + int _type = T__55; + int _channel = DEFAULT_TOKEN_CHANNEL; + // Lua.g:42:7: ( '%' ) + // Lua.g:42:9: '%' + { + match('%'); + + } + + state.type = _type; + state.channel = _channel; + } + finally { + } + } + // $ANTLR end "T__55" + + // $ANTLR start "T__56" + public final void mT__56() throws RecognitionException { + try { + int _type = T__56; + int _channel = DEFAULT_TOKEN_CHANNEL; + // Lua.g:43:7: ( '..' ) + // Lua.g:43:9: '..' + { + match(".."); + + + } + + state.type = _type; + state.channel = _channel; + } + finally { + } + } + // $ANTLR end "T__56" + + // $ANTLR start "T__57" + public final void mT__57() throws RecognitionException { + try { + int _type = T__57; + int _channel = DEFAULT_TOKEN_CHANNEL; + // Lua.g:44:7: ( '<' ) + // Lua.g:44:9: '<' + { + match('<'); + + } + + state.type = _type; + state.channel = _channel; + } + finally { + } + } + // $ANTLR end "T__57" + + // $ANTLR start "T__58" + public final void mT__58() throws RecognitionException { + try { + int _type = T__58; + int _channel = DEFAULT_TOKEN_CHANNEL; + // Lua.g:45:7: ( '<=' ) + // Lua.g:45:9: '<=' + { + match("<="); + + + } + + state.type = _type; + state.channel = _channel; + } + finally { + } + } + // $ANTLR end "T__58" + + // $ANTLR start "T__59" + public final void mT__59() throws RecognitionException { + try { + int _type = T__59; + int _channel = DEFAULT_TOKEN_CHANNEL; + // Lua.g:46:7: ( '>' ) + // Lua.g:46:9: '>' + { + match('>'); + + } + + state.type = _type; + state.channel = _channel; + } + finally { + } + } + // $ANTLR end "T__59" + + // $ANTLR start "T__60" + public final void mT__60() throws RecognitionException { + try { + int _type = T__60; + int _channel = DEFAULT_TOKEN_CHANNEL; + // Lua.g:47:7: ( '>=' ) + // Lua.g:47:9: '>=' + { + match(">="); + + + } + + state.type = _type; + state.channel = _channel; + } + finally { + } + } + // $ANTLR end "T__60" + + // $ANTLR start "T__61" + public final void mT__61() throws RecognitionException { + try { + int _type = T__61; + int _channel = DEFAULT_TOKEN_CHANNEL; + // Lua.g:48:7: ( '==' ) + // Lua.g:48:9: '==' + { + match("=="); + + + } + + state.type = _type; + state.channel = _channel; + } + finally { + } + } + // $ANTLR end "T__61" + + // $ANTLR start "T__62" + public final void mT__62() throws RecognitionException { + try { + int _type = T__62; + int _channel = DEFAULT_TOKEN_CHANNEL; + // Lua.g:49:7: ( '~=' ) + // Lua.g:49:9: '~=' + { + match("~="); + + + } + + state.type = _type; + state.channel = _channel; + } + finally { + } + } + // $ANTLR end "T__62" + + // $ANTLR start "T__63" + public final void mT__63() throws RecognitionException { + try { + int _type = T__63; + int _channel = DEFAULT_TOKEN_CHANNEL; + // Lua.g:50:7: ( 'and' ) + // Lua.g:50:9: 'and' + { + match("and"); + + + } + + state.type = _type; + state.channel = _channel; + } + finally { + } + } + // $ANTLR end "T__63" + + // $ANTLR start "T__64" + public final void mT__64() throws RecognitionException { + try { + int _type = T__64; + int _channel = DEFAULT_TOKEN_CHANNEL; + // Lua.g:51:7: ( 'or' ) + // Lua.g:51:9: 'or' + { + match("or"); + + + } + + state.type = _type; + state.channel = _channel; + } + finally { + } + } + // $ANTLR end "T__64" + + // $ANTLR start "T__65" + public final void mT__65() throws RecognitionException { + try { + int _type = T__65; + int _channel = DEFAULT_TOKEN_CHANNEL; + // Lua.g:52:7: ( 'not' ) + // Lua.g:52:9: 'not' + { + match("not"); + + + } + + state.type = _type; + state.channel = _channel; + } + finally { + } + } + // $ANTLR end "T__65" + + // $ANTLR start "T__66" + public final void mT__66() throws RecognitionException { + try { + int _type = T__66; + int _channel = DEFAULT_TOKEN_CHANNEL; + // Lua.g:53:7: ( '#' ) + // Lua.g:53:9: '#' + { + match('#'); + + } + + state.type = _type; + state.channel = _channel; + } + finally { + } + } + // $ANTLR end "T__66" + + // $ANTLR start "NAME" + public final void mNAME() throws RecognitionException { + try { + int _type = NAME; + int _channel = DEFAULT_TOKEN_CHANNEL; + // Lua.g:240:6: ( ( 'a' .. 'z' | 'A' .. 'Z' | '_' ) ( options {greedy=true; } : 'a' .. 'z' | 'A' .. 'Z' | '_' | '0' .. '9' )* ) + // Lua.g:240:7: ( 'a' .. 'z' | 'A' .. 'Z' | '_' ) ( options {greedy=true; } : 'a' .. 'z' | 'A' .. 'Z' | '_' | '0' .. '9' )* + { + if ( (input.LA(1)>='A' && input.LA(1)<='Z')||input.LA(1)=='_'||(input.LA(1)>='a' && input.LA(1)<='z') ) { + input.consume(); + + } + else { + MismatchedSetException mse = new MismatchedSetException(null,input); + recover(mse); + throw mse;} + + // Lua.g:240:30: ( options {greedy=true; } : 'a' .. 'z' | 'A' .. 'Z' | '_' | '0' .. '9' )* + loop1: + do { + int alt1=5; + switch ( input.LA(1) ) { + case 'a': + case 'b': + case 'c': + case 'd': + case 'e': + case 'f': + case 'g': + case 'h': + case 'i': + case 'j': + case 'k': + case 'l': + case 'm': + case 'n': + case 'o': + case 'p': + case 'q': + case 'r': + case 's': + case 't': + case 'u': + case 'v': + case 'w': + case 'x': + case 'y': + case 'z': + { + alt1=1; + } + break; + case 'A': + case 'B': + case 'C': + case 'D': + case 'E': + case 'F': + case 'G': + case 'H': + case 'I': + case 'J': + case 'K': + case 'L': + case 'M': + case 'N': + case 'O': + case 'P': + case 'Q': + case 'R': + case 'S': + case 'T': + case 'U': + case 'V': + case 'W': + case 'X': + case 'Y': + case 'Z': + { + alt1=2; + } + break; + case '_': + { + alt1=3; + } + break; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + { + alt1=4; + } + break; + + } + + switch (alt1) { + case 1 : + // Lua.g:240:54: 'a' .. 'z' + { + matchRange('a','z'); + + } + break; + case 2 : + // Lua.g:240:63: 'A' .. 'Z' + { + matchRange('A','Z'); + + } + break; + case 3 : + // Lua.g:240:72: '_' + { + match('_'); + + } + break; + case 4 : + // Lua.g:240:76: '0' .. '9' + { + matchRange('0','9'); + + } + break; + + default : + break loop1; + } + } while (true); + + + } + + state.type = _type; + state.channel = _channel; + } + finally { + } + } + // $ANTLR end "NAME" + + // $ANTLR start "INT" + public final void mINT() throws RecognitionException { + try { + int _type = INT; + int _channel = DEFAULT_TOKEN_CHANNEL; + // Lua.g:243:5: ( ( '0' .. '9' )+ ) + // Lua.g:243:7: ( '0' .. '9' )+ + { + // Lua.g:243:7: ( '0' .. '9' )+ + int cnt2=0; + loop2: + do { + int alt2=2; + int LA2_0 = input.LA(1); + + if ( ((LA2_0>='0' && LA2_0<='9')) ) { + alt2=1; + } + + + switch (alt2) { + case 1 : + // Lua.g:243:8: '0' .. '9' + { + matchRange('0','9'); + + } + break; + + default : + if ( cnt2 >= 1 ) break loop2; + EarlyExitException eee = + new EarlyExitException(2, input); + throw eee; + } + cnt2++; + } while (true); + + + } + + state.type = _type; + state.channel = _channel; + } + finally { + } + } + // $ANTLR end "INT" + + // $ANTLR start "FLOAT1" + public final void mFLOAT1() throws RecognitionException { + try { + int _type = FLOAT1; + int _channel = DEFAULT_TOKEN_CHANNEL; + // Lua.g:245:9: ( '.' INT ) + // Lua.g:245:10: '.' INT + { + match('.'); + mINT(); + + } + + state.type = _type; + state.channel = _channel; + } + finally { + } + } + // $ANTLR end "FLOAT1" + + // $ANTLR start "FLOAT2" + public final void mFLOAT2() throws RecognitionException { + try { + int _type = FLOAT2; + int _channel = DEFAULT_TOKEN_CHANNEL; + // Lua.g:247:9: ( INT '.' ) + // Lua.g:247:10: INT '.' + { + mINT(); + match('.'); + + } + + state.type = _type; + state.channel = _channel; + } + finally { + } + } + // $ANTLR end "FLOAT2" + + // $ANTLR start "FLOAT3" + public final void mFLOAT3() throws RecognitionException { + try { + int _type = FLOAT3; + int _channel = DEFAULT_TOKEN_CHANNEL; + // Lua.g:249:9: ( INT '.' INT ) + // Lua.g:249:10: INT '.' INT + { + mINT(); + match('.'); + mINT(); + + } + + state.type = _type; + state.channel = _channel; + } + finally { + } + } + // $ANTLR end "FLOAT3" + + // $ANTLR start "EXP" + public final void mEXP() throws RecognitionException { + try { + int _type = EXP; + int _channel = DEFAULT_TOKEN_CHANNEL; + // Lua.g:251:5: ( ( INT | FLOAT1 | FLOAT2 | FLOAT3 ) ( 'E' | 'e' ) ( '-' | '+' )? INT ) + // Lua.g:251:7: ( INT | FLOAT1 | FLOAT2 | FLOAT3 ) ( 'E' | 'e' ) ( '-' | '+' )? INT + { + // Lua.g:251:7: ( INT | FLOAT1 | FLOAT2 | FLOAT3 ) + int alt3=4; + alt3 = dfa3.predict(input); + switch (alt3) { + case 1 : + // Lua.g:251:8: INT + { + mINT(); + + } + break; + case 2 : + // Lua.g:251:14: FLOAT1 + { + mFLOAT1(); + + } + break; + case 3 : + // Lua.g:251:23: FLOAT2 + { + mFLOAT2(); + + } + break; + case 4 : + // Lua.g:251:32: FLOAT3 + { + mFLOAT3(); + + } + break; + + } + + if ( input.LA(1)=='E'||input.LA(1)=='e' ) { + input.consume(); + + } + else { + MismatchedSetException mse = new MismatchedSetException(null,input); + recover(mse); + throw mse;} + + // Lua.g:251:50: ( '-' | '+' )? + int alt4=2; + int LA4_0 = input.LA(1); + + if ( (LA4_0=='+'||LA4_0=='-') ) { + alt4=1; + } + switch (alt4) { + case 1 : + // Lua.g: + { + if ( input.LA(1)=='+'||input.LA(1)=='-' ) { + input.consume(); + + } + else { + MismatchedSetException mse = new MismatchedSetException(null,input); + recover(mse); + throw mse;} + + + } + break; + + } + + mINT(); + + } + + state.type = _type; + state.channel = _channel; + } + finally { + } + } + // $ANTLR end "EXP" + + // $ANTLR start "HEX" + public final void mHEX() throws RecognitionException { + try { + int _type = HEX; + int _channel = DEFAULT_TOKEN_CHANNEL; + // Lua.g:253:5: ( '0' ( 'x' | 'X' ) ( '0' .. '9' | 'a' .. 'f' | 'A' .. 'F' )+ ) + // Lua.g:253:6: '0' ( 'x' | 'X' ) ( '0' .. '9' | 'a' .. 'f' | 'A' .. 'F' )+ + { + match('0'); + if ( input.LA(1)=='X'||input.LA(1)=='x' ) { + input.consume(); + + } + else { + MismatchedSetException mse = new MismatchedSetException(null,input); + recover(mse); + throw mse;} + + // Lua.g:253:22: ( '0' .. '9' | 'a' .. 'f' | 'A' .. 'F' )+ + int cnt5=0; + loop5: + do { + int alt5=2; + int LA5_0 = input.LA(1); + + if ( ((LA5_0>='0' && LA5_0<='9')||(LA5_0>='A' && LA5_0<='F')||(LA5_0>='a' && LA5_0<='f')) ) { + alt5=1; + } + + + switch (alt5) { + case 1 : + // Lua.g: + { + if ( (input.LA(1)>='0' && input.LA(1)<='9')||(input.LA(1)>='A' && input.LA(1)<='F')||(input.LA(1)>='a' && input.LA(1)<='f') ) { + input.consume(); + + } + else { + MismatchedSetException mse = new MismatchedSetException(null,input); + recover(mse); + throw mse;} + + + } + break; + + default : + if ( cnt5 >= 1 ) break loop5; + EarlyExitException eee = + new EarlyExitException(5, input); + throw eee; + } + cnt5++; + } while (true); + + + } + + state.type = _type; + state.channel = _channel; + } + finally { + } + } + // $ANTLR end "HEX" + + // $ANTLR start "NORMALSTRING" + public final void mNORMALSTRING() throws RecognitionException { + try { + int _type = NORMALSTRING; + int _channel = DEFAULT_TOKEN_CHANNEL; + // Lua.g:258:5: ( '\"' ( EscapeSequence | ~ ( '\\\\' | '\"' ) )* '\"' ) + // Lua.g:258:8: '\"' ( EscapeSequence | ~ ( '\\\\' | '\"' ) )* '\"' + { + match('\"'); + // Lua.g:258:12: ( EscapeSequence | ~ ( '\\\\' | '\"' ) )* + loop6: + do { + int alt6=3; + int LA6_0 = input.LA(1); + + if ( (LA6_0=='\\') ) { + alt6=1; + } + else if ( ((LA6_0>='\u0000' && LA6_0<='!')||(LA6_0>='#' && LA6_0<='[')||(LA6_0>=']' && LA6_0<='\uFFFF')) ) { + alt6=2; + } + + + switch (alt6) { + case 1 : + // Lua.g:258:14: EscapeSequence + { + mEscapeSequence(); + + } + break; + case 2 : + // Lua.g:258:31: ~ ( '\\\\' | '\"' ) + { + if ( (input.LA(1)>='\u0000' && input.LA(1)<='!')||(input.LA(1)>='#' && input.LA(1)<='[')||(input.LA(1)>=']' && input.LA(1)<='\uFFFF') ) { + input.consume(); + + } + else { + MismatchedSetException mse = new MismatchedSetException(null,input); + recover(mse); + throw mse;} + + + } + break; + + default : + break loop6; + } + } while (true); + + match('\"'); + + } + + state.type = _type; + state.channel = _channel; + } + finally { + } + } + // $ANTLR end "NORMALSTRING" + + // $ANTLR start "CHARSTRING" + public final void mCHARSTRING() throws RecognitionException { + try { + int _type = CHARSTRING; + int _channel = DEFAULT_TOKEN_CHANNEL; + // Lua.g:262:4: ( '\\'' ( EscapeSequence | ~ ( '\\\\' | '\\'' ) )* '\\'' ) + // Lua.g:262:6: '\\'' ( EscapeSequence | ~ ( '\\\\' | '\\'' ) )* '\\'' + { + match('\''); + // Lua.g:262:11: ( EscapeSequence | ~ ( '\\\\' | '\\'' ) )* + loop7: + do { + int alt7=3; + int LA7_0 = input.LA(1); + + if ( (LA7_0=='\\') ) { + alt7=1; + } + else if ( ((LA7_0>='\u0000' && LA7_0<='&')||(LA7_0>='(' && LA7_0<='[')||(LA7_0>=']' && LA7_0<='\uFFFF')) ) { + alt7=2; + } + + + switch (alt7) { + case 1 : + // Lua.g:262:13: EscapeSequence + { + mEscapeSequence(); + + } + break; + case 2 : + // Lua.g:262:30: ~ ( '\\\\' | '\\'' ) + { + if ( (input.LA(1)>='\u0000' && input.LA(1)<='&')||(input.LA(1)>='(' && input.LA(1)<='[')||(input.LA(1)>=']' && input.LA(1)<='\uFFFF') ) { + input.consume(); + + } + else { + MismatchedSetException mse = new MismatchedSetException(null,input); + recover(mse); + throw mse;} + + + } + break; + + default : + break loop7; + } + } while (true); + + match('\''); + + } + + state.type = _type; + state.channel = _channel; + } + finally { + } + } + // $ANTLR end "CHARSTRING" + + // $ANTLR start "LONGSTRING" + public final void mLONGSTRING() throws RecognitionException { + try { + int _type = LONGSTRING; + int _channel = DEFAULT_TOKEN_CHANNEL; + // Lua.g:266:2: ( '[' ( '=' )* '[' ( EscapeSequence | ~ ( '\\\\' | ']' ) )* ']' ( '=' )* ']' ) + // Lua.g:266:4: '[' ( '=' )* '[' ( EscapeSequence | ~ ( '\\\\' | ']' ) )* ']' ( '=' )* ']' + { + match('['); + // Lua.g:266:7: ( '=' )* + loop8: + do { + int alt8=2; + int LA8_0 = input.LA(1); + + if ( (LA8_0=='=') ) { + alt8=1; + } + + + switch (alt8) { + case 1 : + // Lua.g:266:8: '=' + { + match('='); + + } + break; + + default : + break loop8; + } + } while (true); + + match('['); + // Lua.g:266:17: ( EscapeSequence | ~ ( '\\\\' | ']' ) )* + loop9: + do { + int alt9=3; + int LA9_0 = input.LA(1); + + if ( (LA9_0=='\\') ) { + alt9=1; + } + else if ( ((LA9_0>='\u0000' && LA9_0<='[')||(LA9_0>='^' && LA9_0<='\uFFFF')) ) { + alt9=2; + } + + + switch (alt9) { + case 1 : + // Lua.g:266:19: EscapeSequence + { + mEscapeSequence(); + + } + break; + case 2 : + // Lua.g:266:36: ~ ( '\\\\' | ']' ) + { + if ( (input.LA(1)>='\u0000' && input.LA(1)<='[')||(input.LA(1)>='^' && input.LA(1)<='\uFFFF') ) { + input.consume(); + + } + else { + MismatchedSetException mse = new MismatchedSetException(null,input); + recover(mse); + throw mse;} + + + } + break; + + default : + break loop9; + } + } while (true); + + match(']'); + // Lua.g:266:54: ( '=' )* + loop10: + do { + int alt10=2; + int LA10_0 = input.LA(1); + + if ( (LA10_0=='=') ) { + alt10=1; + } + + + switch (alt10) { + case 1 : + // Lua.g:266:55: '=' + { + match('='); + + } + break; + + default : + break loop10; + } + } while (true); + + match(']'); + + } + + state.type = _type; + state.channel = _channel; + } + finally { + } + } + // $ANTLR end "LONGSTRING" + + // $ANTLR start "EscapeSequence" + public final void mEscapeSequence() throws RecognitionException { + try { + // Lua.g:271:5: ( '\\\\' ( 'a' | 'b' | 'f' | 'n' | 'r' | 't' | 'v' | '\\\"' | '\\'' | '\\\\' | '\\n' ) | DecimalEscape ) + int alt11=2; + int LA11_0 = input.LA(1); + + if ( (LA11_0=='\\') ) { + int LA11_1 = input.LA(2); + + if ( (LA11_1=='\n'||LA11_1=='\"'||LA11_1=='\''||LA11_1=='\\'||(LA11_1>='a' && LA11_1<='b')||LA11_1=='f'||LA11_1=='n'||LA11_1=='r'||LA11_1=='t'||LA11_1=='v') ) { + alt11=1; + } + else if ( ((LA11_1>='0' && LA11_1<='9')) ) { + alt11=2; + } + else { + NoViableAltException nvae = + new NoViableAltException("", 11, 1, input); + + throw nvae; + } + } + else { + NoViableAltException nvae = + new NoViableAltException("", 11, 0, input); + + throw nvae; + } + switch (alt11) { + case 1 : + // Lua.g:271:9: '\\\\' ( 'a' | 'b' | 'f' | 'n' | 'r' | 't' | 'v' | '\\\"' | '\\'' | '\\\\' | '\\n' ) + { + match('\\'); + if ( input.LA(1)=='\n'||input.LA(1)=='\"'||input.LA(1)=='\''||input.LA(1)=='\\'||(input.LA(1)>='a' && input.LA(1)<='b')||input.LA(1)=='f'||input.LA(1)=='n'||input.LA(1)=='r'||input.LA(1)=='t'||input.LA(1)=='v' ) { + input.consume(); + + } + else { + MismatchedSetException mse = new MismatchedSetException(null,input); + recover(mse); + throw mse;} + + + } + break; + case 2 : + // Lua.g:272:9: DecimalEscape + { + mDecimalEscape(); + + } + break; + + } + } + finally { + } + } + // $ANTLR end "EscapeSequence" + + // $ANTLR start "DecimalEscape" + public final void mDecimalEscape() throws RecognitionException { + try { + // Lua.g:277:5: ( '\\\\' ( '0' .. '9' ) ( ( '0' .. '9' ) ( '0' .. '9' )? )? ) + // Lua.g:277:9: '\\\\' ( '0' .. '9' ) ( ( '0' .. '9' ) ( '0' .. '9' )? )? + { + match('\\'); + // Lua.g:277:14: ( '0' .. '9' ) + // Lua.g:277:15: '0' .. '9' + { + matchRange('0','9'); + + } + + // Lua.g:277:25: ( ( '0' .. '9' ) ( '0' .. '9' )? )? + int alt13=2; + int LA13_0 = input.LA(1); + + if ( ((LA13_0>='0' && LA13_0<='9')) ) { + alt13=1; + } + switch (alt13) { + case 1 : + // Lua.g:277:26: ( '0' .. '9' ) ( '0' .. '9' )? + { + // Lua.g:277:26: ( '0' .. '9' ) + // Lua.g:277:27: '0' .. '9' + { + matchRange('0','9'); + + } + + // Lua.g:277:37: ( '0' .. '9' )? + int alt12=2; + int LA12_0 = input.LA(1); + + if ( ((LA12_0>='0' && LA12_0<='9')) ) { + alt12=1; + } + switch (alt12) { + case 1 : + // Lua.g:277:38: '0' .. '9' + { + matchRange('0','9'); + + } + break; + + } + + + } + break; + + } + + + } + + } + finally { + } + } + // $ANTLR end "DecimalEscape" + + // $ANTLR start "COMMENT" + public final void mCOMMENT() throws RecognitionException { + try { + int _type = COMMENT; + int _channel = DEFAULT_TOKEN_CHANNEL; + // Lua.g:282:5: ( '--[[' ( options {greedy=false; } : . )* '--]]' | '--[=[' ( options {greedy=false; } : . )* '--]==]' | '--[==[' ( options {greedy=false; } : . )* '--]==]' | '--[===[' ( options {greedy=false; } : . )* '--]===]' ) + int alt18=4; + alt18 = dfa18.predict(input); + switch (alt18) { + case 1 : + // Lua.g:282:9: '--[[' ( options {greedy=false; } : . )* '--]]' + { + match("--[["); + + // Lua.g:282:16: ( options {greedy=false; } : . )* + loop14: + do { + int alt14=2; + int LA14_0 = input.LA(1); + + if ( (LA14_0=='-') ) { + int LA14_1 = input.LA(2); + + if ( (LA14_1=='-') ) { + int LA14_3 = input.LA(3); + + if ( (LA14_3==']') ) { + int LA14_4 = input.LA(4); + + if ( (LA14_4==']') ) { + alt14=2; + } + else if ( ((LA14_4>='\u0000' && LA14_4<='\\')||(LA14_4>='^' && LA14_4<='\uFFFF')) ) { + alt14=1; + } + + + } + else if ( ((LA14_3>='\u0000' && LA14_3<='\\')||(LA14_3>='^' && LA14_3<='\uFFFF')) ) { + alt14=1; + } + + + } + else if ( ((LA14_1>='\u0000' && LA14_1<=',')||(LA14_1>='.' && LA14_1<='\uFFFF')) ) { + alt14=1; + } + + + } + else if ( ((LA14_0>='\u0000' && LA14_0<=',')||(LA14_0>='.' && LA14_0<='\uFFFF')) ) { + alt14=1; + } + + + switch (alt14) { + case 1 : + // Lua.g:282:44: . + { + matchAny(); + + } + break; + + default : + break loop14; + } + } while (true); + + match("--]]"); + + skip(); + + } + break; + case 2 : + // Lua.g:283:9: '--[=[' ( options {greedy=false; } : . )* '--]==]' + { + match("--[=["); + + // Lua.g:283:17: ( options {greedy=false; } : . )* + loop15: + do { + int alt15=2; + int LA15_0 = input.LA(1); + + if ( (LA15_0=='-') ) { + int LA15_1 = input.LA(2); + + if ( (LA15_1=='-') ) { + int LA15_3 = input.LA(3); + + if ( (LA15_3==']') ) { + int LA15_4 = input.LA(4); + + if ( (LA15_4=='=') ) { + int LA15_5 = input.LA(5); + + if ( (LA15_5=='=') ) { + int LA15_6 = input.LA(6); + + if ( (LA15_6==']') ) { + alt15=2; + } + else if ( ((LA15_6>='\u0000' && LA15_6<='\\')||(LA15_6>='^' && LA15_6<='\uFFFF')) ) { + alt15=1; + } + + + } + else if ( ((LA15_5>='\u0000' && LA15_5<='<')||(LA15_5>='>' && LA15_5<='\uFFFF')) ) { + alt15=1; + } + + + } + else if ( ((LA15_4>='\u0000' && LA15_4<='<')||(LA15_4>='>' && LA15_4<='\uFFFF')) ) { + alt15=1; + } + + + } + else if ( ((LA15_3>='\u0000' && LA15_3<='\\')||(LA15_3>='^' && LA15_3<='\uFFFF')) ) { + alt15=1; + } + + + } + else if ( ((LA15_1>='\u0000' && LA15_1<=',')||(LA15_1>='.' && LA15_1<='\uFFFF')) ) { + alt15=1; + } + + + } + else if ( ((LA15_0>='\u0000' && LA15_0<=',')||(LA15_0>='.' && LA15_0<='\uFFFF')) ) { + alt15=1; + } + + + switch (alt15) { + case 1 : + // Lua.g:283:45: . + { + matchAny(); + + } + break; + + default : + break loop15; + } + } while (true); + + match("--]==]"); + + skip(); + + } + break; + case 3 : + // Lua.g:284:9: '--[==[' ( options {greedy=false; } : . )* '--]==]' + { + match("--[==["); + + // Lua.g:284:18: ( options {greedy=false; } : . )* + loop16: + do { + int alt16=2; + int LA16_0 = input.LA(1); + + if ( (LA16_0=='-') ) { + int LA16_1 = input.LA(2); + + if ( (LA16_1=='-') ) { + int LA16_3 = input.LA(3); + + if ( (LA16_3==']') ) { + int LA16_4 = input.LA(4); + + if ( (LA16_4=='=') ) { + int LA16_5 = input.LA(5); + + if ( (LA16_5=='=') ) { + int LA16_6 = input.LA(6); + + if ( (LA16_6==']') ) { + alt16=2; + } + else if ( ((LA16_6>='\u0000' && LA16_6<='\\')||(LA16_6>='^' && LA16_6<='\uFFFF')) ) { + alt16=1; + } + + + } + else if ( ((LA16_5>='\u0000' && LA16_5<='<')||(LA16_5>='>' && LA16_5<='\uFFFF')) ) { + alt16=1; + } + + + } + else if ( ((LA16_4>='\u0000' && LA16_4<='<')||(LA16_4>='>' && LA16_4<='\uFFFF')) ) { + alt16=1; + } + + + } + else if ( ((LA16_3>='\u0000' && LA16_3<='\\')||(LA16_3>='^' && LA16_3<='\uFFFF')) ) { + alt16=1; + } + + + } + else if ( ((LA16_1>='\u0000' && LA16_1<=',')||(LA16_1>='.' && LA16_1<='\uFFFF')) ) { + alt16=1; + } + + + } + else if ( ((LA16_0>='\u0000' && LA16_0<=',')||(LA16_0>='.' && LA16_0<='\uFFFF')) ) { + alt16=1; + } + + + switch (alt16) { + case 1 : + // Lua.g:284:46: . + { + matchAny(); + + } + break; + + default : + break loop16; + } + } while (true); + + match("--]==]"); + + skip(); + + } + break; + case 4 : + // Lua.g:285:9: '--[===[' ( options {greedy=false; } : . )* '--]===]' + { + match("--[===["); + + // Lua.g:285:19: ( options {greedy=false; } : . )* + loop17: + do { + int alt17=2; + int LA17_0 = input.LA(1); + + if ( (LA17_0=='-') ) { + int LA17_1 = input.LA(2); + + if ( (LA17_1=='-') ) { + int LA17_3 = input.LA(3); + + if ( (LA17_3==']') ) { + int LA17_4 = input.LA(4); + + if ( (LA17_4=='=') ) { + int LA17_5 = input.LA(5); + + if ( (LA17_5=='=') ) { + int LA17_6 = input.LA(6); + + if ( (LA17_6=='=') ) { + int LA17_7 = input.LA(7); + + if ( (LA17_7==']') ) { + alt17=2; + } + else if ( ((LA17_7>='\u0000' && LA17_7<='\\')||(LA17_7>='^' && LA17_7<='\uFFFF')) ) { + alt17=1; + } + + + } + else if ( ((LA17_6>='\u0000' && LA17_6<='<')||(LA17_6>='>' && LA17_6<='\uFFFF')) ) { + alt17=1; + } + + + } + else if ( ((LA17_5>='\u0000' && LA17_5<='<')||(LA17_5>='>' && LA17_5<='\uFFFF')) ) { + alt17=1; + } + + + } + else if ( ((LA17_4>='\u0000' && LA17_4<='<')||(LA17_4>='>' && LA17_4<='\uFFFF')) ) { + alt17=1; + } + + + } + else if ( ((LA17_3>='\u0000' && LA17_3<='\\')||(LA17_3>='^' && LA17_3<='\uFFFF')) ) { + alt17=1; + } + + + } + else if ( ((LA17_1>='\u0000' && LA17_1<=',')||(LA17_1>='.' && LA17_1<='\uFFFF')) ) { + alt17=1; + } + + + } + else if ( ((LA17_0>='\u0000' && LA17_0<=',')||(LA17_0>='.' && LA17_0<='\uFFFF')) ) { + alt17=1; + } + + + switch (alt17) { + case 1 : + // Lua.g:285:47: . + { + matchAny(); + + } + break; + + default : + break loop17; + } + } while (true); + + match("--]===]"); + + skip(); + + } + break; + + } + state.type = _type; + state.channel = _channel; + } + finally { + } + } + // $ANTLR end "COMMENT" + + // $ANTLR start "LINE_COMMENT" + public final void mLINE_COMMENT() throws RecognitionException { + try { + int _type = LINE_COMMENT; + int _channel = DEFAULT_TOKEN_CHANNEL; + // Lua.g:289:5: ( '--' (~ ( '\\n' | '\\r' ) )* ( '\\r' )? '\\n' ) + // Lua.g:289:7: '--' (~ ( '\\n' | '\\r' ) )* ( '\\r' )? '\\n' + { + match("--"); + + // Lua.g:289:12: (~ ( '\\n' | '\\r' ) )* + loop19: + do { + int alt19=2; + int LA19_0 = input.LA(1); + + if ( ((LA19_0>='\u0000' && LA19_0<='\t')||(LA19_0>='\u000B' && LA19_0<='\f')||(LA19_0>='\u000E' && LA19_0<='\uFFFF')) ) { + alt19=1; + } + + + switch (alt19) { + case 1 : + // Lua.g:289:12: ~ ( '\\n' | '\\r' ) + { + if ( (input.LA(1)>='\u0000' && input.LA(1)<='\t')||(input.LA(1)>='\u000B' && input.LA(1)<='\f')||(input.LA(1)>='\u000E' && input.LA(1)<='\uFFFF') ) { + input.consume(); + + } + else { + MismatchedSetException mse = new MismatchedSetException(null,input); + recover(mse); + throw mse;} + + + } + break; + + default : + break loop19; + } + } while (true); + + // Lua.g:289:26: ( '\\r' )? + int alt20=2; + int LA20_0 = input.LA(1); + + if ( (LA20_0=='\r') ) { + alt20=1; + } + switch (alt20) { + case 1 : + // Lua.g:289:26: '\\r' + { + match('\r'); + + } + break; + + } + + match('\n'); + skip(); + + } + + state.type = _type; + state.channel = _channel; + } + finally { + } + } + // $ANTLR end "LINE_COMMENT" + + // $ANTLR start "WS" + public final void mWS() throws RecognitionException { + try { + int _type = WS; + int _channel = DEFAULT_TOKEN_CHANNEL; + // Lua.g:293:5: ( ( ' ' | '\\t' | '\\u000C' ) ) + // Lua.g:293:8: ( ' ' | '\\t' | '\\u000C' ) + { + if ( input.LA(1)=='\t'||input.LA(1)=='\f'||input.LA(1)==' ' ) { + input.consume(); + + } + else { + MismatchedSetException mse = new MismatchedSetException(null,input); + recover(mse); + throw mse;} + + skip(); + + } + + state.type = _type; + state.channel = _channel; + } + finally { + } + } + // $ANTLR end "WS" + + // $ANTLR start "NEWLINE" + public final void mNEWLINE() throws RecognitionException { + try { + int _type = NEWLINE; + int _channel = DEFAULT_TOKEN_CHANNEL; + // Lua.g:296:9: ( ( '\\r' )? '\\n' ) + // Lua.g:296:11: ( '\\r' )? '\\n' + { + // Lua.g:296:11: ( '\\r' )? + int alt21=2; + int LA21_0 = input.LA(1); + + if ( (LA21_0=='\r') ) { + alt21=1; + } + switch (alt21) { + case 1 : + // Lua.g:296:12: '\\r' + { + match('\r'); + + } + break; + + } + + match('\n'); + skip(); + + } + + state.type = _type; + state.channel = _channel; + } + finally { + } + } + // $ANTLR end "NEWLINE" + + public void mTokens() throws RecognitionException { + // Lua.g:1:8: ( T__20 | T__21 | T__22 | T__23 | T__24 | T__25 | T__26 | T__27 | T__28 | T__29 | T__30 | T__31 | T__32 | T__33 | T__34 | T__35 | T__36 | T__37 | T__38 | T__39 | T__40 | T__41 | T__42 | T__43 | T__44 | T__45 | T__46 | T__47 | T__48 | T__49 | T__50 | T__51 | T__52 | T__53 | T__54 | T__55 | T__56 | T__57 | T__58 | T__59 | T__60 | T__61 | T__62 | T__63 | T__64 | T__65 | T__66 | NAME | INT | FLOAT1 | FLOAT2 | FLOAT3 | EXP | HEX | NORMALSTRING | CHARSTRING | LONGSTRING | COMMENT | LINE_COMMENT | WS | NEWLINE ) + int alt22=61; + alt22 = dfa22.predict(input); + switch (alt22) { + case 1 : + // Lua.g:1:10: T__20 + { + mT__20(); + + } + break; + case 2 : + // Lua.g:1:16: T__21 + { + mT__21(); + + } + break; + case 3 : + // Lua.g:1:22: T__22 + { + mT__22(); + + } + break; + case 4 : + // Lua.g:1:28: T__23 + { + mT__23(); + + } + break; + case 5 : + // Lua.g:1:34: T__24 + { + mT__24(); + + } + break; + case 6 : + // Lua.g:1:40: T__25 + { + mT__25(); + + } + break; + case 7 : + // Lua.g:1:46: T__26 + { + mT__26(); + + } + break; + case 8 : + // Lua.g:1:52: T__27 + { + mT__27(); + + } + break; + case 9 : + // Lua.g:1:58: T__28 + { + mT__28(); + + } + break; + case 10 : + // Lua.g:1:64: T__29 + { + mT__29(); + + } + break; + case 11 : + // Lua.g:1:70: T__30 + { + mT__30(); + + } + break; + case 12 : + // Lua.g:1:76: T__31 + { + mT__31(); + + } + break; + case 13 : + // Lua.g:1:82: T__32 + { + mT__32(); + + } + break; + case 14 : + // Lua.g:1:88: T__33 + { + mT__33(); + + } + break; + case 15 : + // Lua.g:1:94: T__34 + { + mT__34(); + + } + break; + case 16 : + // Lua.g:1:100: T__35 + { + mT__35(); + + } + break; + case 17 : + // Lua.g:1:106: T__36 + { + mT__36(); + + } + break; + case 18 : + // Lua.g:1:112: T__37 + { + mT__37(); + + } + break; + case 19 : + // Lua.g:1:118: T__38 + { + mT__38(); + + } + break; + case 20 : + // Lua.g:1:124: T__39 + { + mT__39(); + + } + break; + case 21 : + // Lua.g:1:130: T__40 + { + mT__40(); + + } + break; + case 22 : + // Lua.g:1:136: T__41 + { + mT__41(); + + } + break; + case 23 : + // Lua.g:1:142: T__42 + { + mT__42(); + + } + break; + case 24 : + // Lua.g:1:148: T__43 + { + mT__43(); + + } + break; + case 25 : + // Lua.g:1:154: T__44 + { + mT__44(); + + } + break; + case 26 : + // Lua.g:1:160: T__45 + { + mT__45(); + + } + break; + case 27 : + // Lua.g:1:166: T__46 + { + mT__46(); + + } + break; + case 28 : + // Lua.g:1:172: T__47 + { + mT__47(); + + } + break; + case 29 : + // Lua.g:1:178: T__48 + { + mT__48(); + + } + break; + case 30 : + // Lua.g:1:184: T__49 + { + mT__49(); + + } + break; + case 31 : + // Lua.g:1:190: T__50 + { + mT__50(); + + } + break; + case 32 : + // Lua.g:1:196: T__51 + { + mT__51(); + + } + break; + case 33 : + // Lua.g:1:202: T__52 + { + mT__52(); + + } + break; + case 34 : + // Lua.g:1:208: T__53 + { + mT__53(); + + } + break; + case 35 : + // Lua.g:1:214: T__54 + { + mT__54(); + + } + break; + case 36 : + // Lua.g:1:220: T__55 + { + mT__55(); + + } + break; + case 37 : + // Lua.g:1:226: T__56 + { + mT__56(); + + } + break; + case 38 : + // Lua.g:1:232: T__57 + { + mT__57(); + + } + break; + case 39 : + // Lua.g:1:238: T__58 + { + mT__58(); + + } + break; + case 40 : + // Lua.g:1:244: T__59 + { + mT__59(); + + } + break; + case 41 : + // Lua.g:1:250: T__60 + { + mT__60(); + + } + break; + case 42 : + // Lua.g:1:256: T__61 + { + mT__61(); + + } + break; + case 43 : + // Lua.g:1:262: T__62 + { + mT__62(); + + } + break; + case 44 : + // Lua.g:1:268: T__63 + { + mT__63(); + + } + break; + case 45 : + // Lua.g:1:274: T__64 + { + mT__64(); + + } + break; + case 46 : + // Lua.g:1:280: T__65 + { + mT__65(); + + } + break; + case 47 : + // Lua.g:1:286: T__66 + { + mT__66(); + + } + break; + case 48 : + // Lua.g:1:292: NAME + { + mNAME(); + + } + break; + case 49 : + // Lua.g:1:297: INT + { + mINT(); + + } + break; + case 50 : + // Lua.g:1:301: FLOAT1 + { + mFLOAT1(); + + } + break; + case 51 : + // Lua.g:1:308: FLOAT2 + { + mFLOAT2(); + + } + break; + case 52 : + // Lua.g:1:315: FLOAT3 + { + mFLOAT3(); + + } + break; + case 53 : + // Lua.g:1:322: EXP + { + mEXP(); + + } + break; + case 54 : + // Lua.g:1:326: HEX + { + mHEX(); + + } + break; + case 55 : + // Lua.g:1:330: NORMALSTRING + { + mNORMALSTRING(); + + } + break; + case 56 : + // Lua.g:1:343: CHARSTRING + { + mCHARSTRING(); + + } + break; + case 57 : + // Lua.g:1:354: LONGSTRING + { + mLONGSTRING(); + + } + break; + case 58 : + // Lua.g:1:365: COMMENT + { + mCOMMENT(); + + } + break; + case 59 : + // Lua.g:1:373: LINE_COMMENT + { + mLINE_COMMENT(); + + } + break; + case 60 : + // Lua.g:1:386: WS + { + mWS(); + + } + break; + case 61 : + // Lua.g:1:389: NEWLINE + { + mNEWLINE(); + + } + break; + + } + + } + + + protected DFA3 dfa3 = new DFA3(this); + protected DFA18 dfa18 = new DFA18(this); + protected DFA22 dfa22 = new DFA22(this); + static final String DFA3_eotS = + "\7\uffff"; + static final String DFA3_eofS = + "\7\uffff"; + static final String DFA3_minS = + "\2\56\2\uffff\1\60\2\uffff"; + static final String DFA3_maxS = + "\1\71\1\145\2\uffff\1\145\2\uffff"; + static final String DFA3_acceptS = + "\2\uffff\1\2\1\1\1\uffff\1\4\1\3"; + static final String DFA3_specialS = + "\7\uffff}>"; + static final String[] DFA3_transitionS = { + "\1\2\1\uffff\12\1", + "\1\4\1\uffff\12\1\13\uffff\1\3\37\uffff\1\3", + "", + "", + "\12\5\13\uffff\1\6\37\uffff\1\6", + "", + "" + }; + + static final short[] DFA3_eot = DFA.unpackEncodedString(DFA3_eotS); + static final short[] DFA3_eof = DFA.unpackEncodedString(DFA3_eofS); + static final char[] DFA3_min = DFA.unpackEncodedStringToUnsignedChars(DFA3_minS); + static final char[] DFA3_max = DFA.unpackEncodedStringToUnsignedChars(DFA3_maxS); + static final short[] DFA3_accept = DFA.unpackEncodedString(DFA3_acceptS); + static final short[] DFA3_special = DFA.unpackEncodedString(DFA3_specialS); + static final short[][] DFA3_transition; + + static { + int numStates = DFA3_transitionS.length; + DFA3_transition = new short[numStates][]; + for (int i=0; i"; + static final String[] DFA18_transitionS = { + "\1\1", + "\1\2", + "\1\3", + "\1\5\35\uffff\1\4", + "", + "\1\7\35\uffff\1\6", + "", + "\1\11\35\uffff\1\10", + "", + "" + }; + + static final short[] DFA18_eot = DFA.unpackEncodedString(DFA18_eotS); + static final short[] DFA18_eof = DFA.unpackEncodedString(DFA18_eofS); + static final char[] DFA18_min = DFA.unpackEncodedStringToUnsignedChars(DFA18_minS); + static final char[] DFA18_max = DFA.unpackEncodedStringToUnsignedChars(DFA18_maxS); + static final short[] DFA18_accept = DFA.unpackEncodedString(DFA18_acceptS); + static final short[] DFA18_special = DFA.unpackEncodedString(DFA18_specialS); + static final short[][] DFA18_transition; + + static { + int numStates = DFA18_transitionS.length; + DFA18_transition = new short[numStates][]; + for (int i=0; i"; + static final String[] DFA22_transitionS = { + "\1\50\1\51\1\uffff\1\50\1\51\22\uffff\1\50\1\uffff\1\46\1\42"+ + "\1\uffff\1\34\1\uffff\1\47\1\21\1\22\1\31\1\27\1\11\1\30\1\16"+ + "\1\32\1\44\11\45\1\17\1\1\1\35\1\2\1\36\2\uffff\32\43\1\23\1"+ + "\uffff\1\24\1\33\1\43\1\uffff\1\40\1\15\1\43\1\3\1\4\1\10\2"+ + "\43\1\12\2\43\1\13\1\43\1\20\1\41\2\43\1\6\1\43\1\14\1\7\1\43"+ + "\1\5\3\43\1\25\1\uffff\1\26\1\37", + "", + "\1\52", + "\1\54", + "\1\56\1\uffff\1\55", + "\1\57", + "\1\60", + "\1\61", + "\1\64\15\uffff\1\62\5\uffff\1\63", + "", + "\1\66\7\uffff\1\65", + "\1\67", + "\1\70\11\uffff\1\71", + "\1\72", + "\1\73\1\uffff\12\74", + "", + "\1\76\5\uffff\1\77", + "", + "", + "\1\100\35\uffff\1\100", + "", + "", + "", + "", + "\1\102", + "", + "", + "", + "", + "\1\104", + "\1\106", + "", + "\1\110", + "\1\111", + "", + "", + "\1\114\1\uffff\12\45\13\uffff\1\115\22\uffff\1\112\14\uffff"+ + "\1\115\22\uffff\1\112", + "\1\114\1\uffff\12\45\13\uffff\1\115\37\uffff\1\115", + "", + "", + "", + "", + "", + "", + "\12\43\7\uffff\32\43\4\uffff\1\43\1\uffff\32\43", + "\1\117", + "\1\120", + "\1\121", + "\1\122\3\uffff\1\123", + "\1\124", + "\1\125", + "\1\126", + "\1\127", + "\12\43\7\uffff\32\43\4\uffff\1\43\1\uffff\32\43", + "\12\43\7\uffff\32\43\4\uffff\1\43\1\uffff\32\43", + "\1\132", + "\1\133", + "\1\134", + "\1\135", + "\1\136", + "\12\74\13\uffff\1\115\37\uffff\1\115", + "", + "\1\141", + "\1\142", + "", + "", + "\133\144\1\143\uffa4\144", + "", + "", + "", + "", + "", + "\1\145", + "\12\43\7\uffff\32\43\4\uffff\1\43\1\uffff\32\43", + "", + "", + "\12\147\13\uffff\1\115\37\uffff\1\115", + "", + "", + "\12\43\7\uffff\32\43\4\uffff\1\43\1\uffff\32\43", + "\1\152", + "\1\153", + "\1\154", + "\1\155", + "\1\156", + "\12\43\7\uffff\32\43\4\uffff\1\43\1\uffff\32\43", + "\1\160", + "\1\161", + "", + "", + "\1\162", + "\1\163", + "\1\164", + "\1\165", + "", + "", + "", + "\12\43\7\uffff\32\43\4\uffff\1\43\1\uffff\32\43", + "\12\43\7\uffff\32\43\4\uffff\1\43\1\uffff\32\43", + "\75\144\1\171\35\144\1\170\uffa4\144", + "", + "\12\43\7\uffff\32\43\4\uffff\1\43\1\uffff\32\43", + "", + "\12\147\13\uffff\1\115\37\uffff\1\115", + "", + "", + "\12\43\7\uffff\32\43\4\uffff\1\43\1\uffff\10\43\1\174\21\43", + "\1\176", + "\1\177", + "\1\u0080", + "\1\u0081", + "", + "\1\u0082", + "\1\u0083", + "\1\u0084", + "\12\43\7\uffff\32\43\4\uffff\1\43\1\uffff\32\43", + "\12\43\7\uffff\32\43\4\uffff\1\43\1\uffff\32\43", + "\1\u0087", + "", + "", + "\12\u008b\1\u008a\2\u008b\1\u0089\37\u008b\1\u0088\uffd2\u008b", + "\75\144\1\u008d\35\144\1\u008c\uffa4\144", + "", + "", + "\1\u008e", + "", + "\12\43\7\uffff\32\43\4\uffff\1\43\1\uffff\32\43", + "\1\u0090", + "\1\u0091", + "\12\43\7\uffff\32\43\4\uffff\1\43\1\uffff\32\43", + "\1\u0093", + "\12\43\7\uffff\32\43\4\uffff\1\43\1\uffff\32\43", + "\12\43\7\uffff\32\43\4\uffff\1\43\1\uffff\32\43", + "", + "", + "\12\43\7\uffff\32\43\4\uffff\1\43\1\uffff\32\43", + "\12\u008b\1\u008a\2\u008b\1\u0089\37\u008b\1\u0097\uffd2\u008b", + "\12\u0098\1\u008a\ufff5\u0098", + "\0\u0098", + "\12\u008b\1\u008a\2\u008b\1\u0089\37\u008b\1\u0088\uffd2\u008b", + "\12\u009c\1\u009b\2\u009c\1\u009a\37\u009c\1\u0099\uffd2\u009c", + "\75\144\1\u009e\35\144\1\u009d\uffa4\144", + "\12\43\7\uffff\32\43\4\uffff\1\43\1\uffff\32\43", + "", + "\12\43\7\uffff\32\43\4\uffff\1\43\1\uffff\32\43", + "\12\43\7\uffff\32\43\4\uffff\1\43\1\uffff\32\43", + "", + "\1\u00a2", + "", + "", + "", + "\12\u008b\1\u008a\2\u008b\1\u0089\37\u008b\1\u0097\57\u008b"+ + "\1\u00a3\uffa2\u008b", + "", + "\12\u009c\1\u009b\2\u009c\1\u009a\37\u009c\1\u00a4\uffd2\u009c", + "\12\u0098\1\u009b\ufff5\u0098", + "\0\u0098", + "\12\u009c\1\u009b\2\u009c\1\u009a\37\u009c\1\u0099\uffd2\u009c", + "\12\u00a8\1\u00a6\2\u00a8\1\u00a5\37\u00a8\1\u00a7\uffd2\u00a8", + "\133\144\1\u00a9\uffa4\144", + "", + "", + "", + "\1\u00aa", + "\12\u008b\1\u008a\2\u008b\1\u0089\37\u008b\1\u0088\57\u008b"+ + "\1\u00ab\uffa2\u008b", + "\12\u009c\1\u009b\2\u009c\1\u009a\37\u009c\1\u00a4\57\u009c"+ + "\1\u00ac\uffa2\u009c", + "\12\u0098\1\u00a6\ufff5\u0098", + "\0\u0098", + "\12\u00a8\1\u00a6\2\u00a8\1\u00a5\37\u00a8\1\u00ad\uffd2\u00a8", + "\12\u00a8\1\u00a6\2\u00a8\1\u00a5\37\u00a8\1\u00a7\uffd2\u00a8", + "\12\u00b1\1\u00af\2\u00b1\1\u00ae\37\u00b1\1\u00b0\uffd2\u00b1", + "\12\43\7\uffff\32\43\4\uffff\1\43\1\uffff\32\43", + "\12\u008b\1\u008a\2\u008b\1\u0089\37\u008b\1\u0088\uffd2\u008b", + "\12\u009c\1\u009b\2\u009c\1\u009a\37\u009c\1\u0099\17\u009c"+ + "\1\u00b3\uffc2\u009c", + "\12\u00a8\1\u00a6\2\u00a8\1\u00a5\37\u00a8\1\u00ad\57\u00a8"+ + "\1\u00b4\uffa2\u00a8", + "\12\u0098\1\u00af\ufff5\u0098", + "\0\u0098", + "\12\u00b1\1\u00af\2\u00b1\1\u00ae\37\u00b1\1\u00b5\uffd2\u00b1", + "\12\u00b1\1\u00af\2\u00b1\1\u00ae\37\u00b1\1\u00b0\uffd2\u00b1", + "", + "\12\u009c\1\u009b\2\u009c\1\u009a\37\u009c\1\u0099\17\u009c"+ + "\1\u00b6\uffc2\u009c", + "\12\u00a8\1\u00a6\2\u00a8\1\u00a5\37\u00a8\1\u00a7\17\u00a8"+ + "\1\u00b7\uffc2\u00a8", + "\12\u00b1\1\u00af\2\u00b1\1\u00ae\37\u00b1\1\u00b5\57\u00b1"+ + "\1\u00b8\uffa2\u00b1", + "\12\u009c\1\u009b\2\u009c\1\u009a\37\u009c\1\u0099\57\u009c"+ + "\1\u00b9\uffa2\u009c", + "\12\u00a8\1\u00a6\2\u00a8\1\u00a5\37\u00a8\1\u00a7\17\u00a8"+ + "\1\u00ba\uffc2\u00a8", + "\12\u00b1\1\u00af\2\u00b1\1\u00ae\37\u00b1\1\u00b0\17\u00b1"+ + "\1\u00bb\uffc2\u00b1", + "\12\u009c\1\u009b\2\u009c\1\u009a\37\u009c\1\u0099\uffd2\u009c", + "\12\u00a8\1\u00a6\2\u00a8\1\u00a5\37\u00a8\1\u00a7\57\u00a8"+ + "\1\u00bc\uffa2\u00a8", + "\12\u00b1\1\u00af\2\u00b1\1\u00ae\37\u00b1\1\u00b0\17\u00b1"+ + "\1\u00bd\uffc2\u00b1", + "\12\u00a8\1\u00a6\2\u00a8\1\u00a5\37\u00a8\1\u00a7\uffd2\u00a8", + "\12\u00b1\1\u00af\2\u00b1\1\u00ae\37\u00b1\1\u00b0\17\u00b1"+ + "\1\u00be\uffc2\u00b1", + "\12\u00b1\1\u00af\2\u00b1\1\u00ae\37\u00b1\1\u00b0\57\u00b1"+ + "\1\u00bf\uffa2\u00b1", + "\12\u00b1\1\u00af\2\u00b1\1\u00ae\37\u00b1\1\u00b0\uffd2\u00b1" + }; + + static final short[] DFA22_eot = DFA.unpackEncodedString(DFA22_eotS); + static final short[] DFA22_eof = DFA.unpackEncodedString(DFA22_eofS); + static final char[] DFA22_min = DFA.unpackEncodedStringToUnsignedChars(DFA22_minS); + static final char[] DFA22_max = DFA.unpackEncodedStringToUnsignedChars(DFA22_maxS); + static final short[] DFA22_accept = DFA.unpackEncodedString(DFA22_acceptS); + static final short[] DFA22_special = DFA.unpackEncodedString(DFA22_specialS); + static final short[][] DFA22_transition; + + static { + int numStates = DFA22_transitionS.length; + DFA22_transition = new short[numStates][]; + for (int i=0; i='\u0000' && LA22_151<='\t')||(LA22_151>='\u000B' && LA22_151<='\f')||(LA22_151>='\u000E' && LA22_151<=',')||(LA22_151>='.' && LA22_151<='\\')||(LA22_151>='^' && LA22_151<='\uFFFF')) ) {s = 139;} + + if ( s>=0 ) return s; + break; + case 1 : + int LA22_158 = input.LA(1); + + s = -1; + if ( (LA22_158=='[') ) {s = 169;} + + else if ( ((LA22_158>='\u0000' && LA22_158<='Z')||(LA22_158>='\\' && LA22_158<='\uFFFF')) ) {s = 100;} + + if ( s>=0 ) return s; + break; + case 2 : + int LA22_141 = input.LA(1); + + s = -1; + if ( (LA22_141=='[') ) {s = 157;} + + else if ( (LA22_141=='=') ) {s = 158;} + + else if ( ((LA22_141>='\u0000' && LA22_141<='<')||(LA22_141>='>' && LA22_141<='Z')||(LA22_141>='\\' && LA22_141<='\uFFFF')) ) {s = 100;} + + if ( s>=0 ) return s; + break; + case 3 : + int LA22_171 = input.LA(1); + + s = -1; + if ( (LA22_171=='-') ) {s = 136;} + + else if ( (LA22_171=='\r') ) {s = 137;} + + else if ( (LA22_171=='\n') ) {s = 138;} + + else if ( ((LA22_171>='\u0000' && LA22_171<='\t')||(LA22_171>='\u000B' && LA22_171<='\f')||(LA22_171>='\u000E' && LA22_171<=',')||(LA22_171>='.' && LA22_171<='\uFFFF')) ) {s = 139;} + + else s = 152; + + if ( s>=0 ) return s; + break; + case 4 : + int LA22_165 = input.LA(1); + + s = -1; + if ( ((LA22_165>='\u0000' && LA22_165<='\t')||(LA22_165>='\u000B' && LA22_165<='\uFFFF')) ) {s = 152;} + + else if ( (LA22_165=='\n') ) {s = 166;} + + if ( s>=0 ) return s; + break; + case 5 : + int LA22_120 = input.LA(1); + + s = -1; + if ( (LA22_120=='-') ) {s = 136;} + + else if ( (LA22_120=='\r') ) {s = 137;} + + else if ( (LA22_120=='\n') ) {s = 138;} + + else if ( ((LA22_120>='\u0000' && LA22_120<='\t')||(LA22_120>='\u000B' && LA22_120<='\f')||(LA22_120>='\u000E' && LA22_120<=',')||(LA22_120>='.' && LA22_120<='\uFFFF')) ) {s = 139;} + + if ( s>=0 ) return s; + break; + case 6 : + int LA22_172 = input.LA(1); + + s = -1; + if ( (LA22_172=='=') ) {s = 179;} + + else if ( (LA22_172=='\r') ) {s = 154;} + + else if ( (LA22_172=='\n') ) {s = 155;} + + else if ( (LA22_172=='-') ) {s = 153;} + + else if ( ((LA22_172>='\u0000' && LA22_172<='\t')||(LA22_172>='\u000B' && LA22_172<='\f')||(LA22_172>='\u000E' && LA22_172<=',')||(LA22_172>='.' && LA22_172<='<')||(LA22_172>='>' && LA22_172<='\uFFFF')) ) {s = 156;} + + if ( s>=0 ) return s; + break; + case 7 : + int LA22_182 = input.LA(1); + + s = -1; + if ( (LA22_182==']') ) {s = 185;} + + else if ( (LA22_182=='\r') ) {s = 154;} + + else if ( (LA22_182=='\n') ) {s = 155;} + + else if ( (LA22_182=='-') ) {s = 153;} + + else if ( ((LA22_182>='\u0000' && LA22_182<='\t')||(LA22_182>='\u000B' && LA22_182<='\f')||(LA22_182>='\u000E' && LA22_182<=',')||(LA22_182>='.' && LA22_182<='\\')||(LA22_182>='^' && LA22_182<='\uFFFF')) ) {s = 156;} + + if ( s>=0 ) return s; + break; + case 8 : + int LA22_179 = input.LA(1); + + s = -1; + if ( (LA22_179=='=') ) {s = 182;} + + else if ( (LA22_179=='-') ) {s = 153;} + + else if ( (LA22_179=='\r') ) {s = 154;} + + else if ( (LA22_179=='\n') ) {s = 155;} + + else if ( ((LA22_179>='\u0000' && LA22_179<='\t')||(LA22_179>='\u000B' && LA22_179<='\f')||(LA22_179>='\u000E' && LA22_179<=',')||(LA22_179>='.' && LA22_179<='<')||(LA22_179>='>' && LA22_179<='\uFFFF')) ) {s = 156;} + + if ( s>=0 ) return s; + break; + case 9 : + int LA22_139 = input.LA(1); + + s = -1; + if ( (LA22_139=='-') ) {s = 136;} + + else if ( (LA22_139=='\r') ) {s = 137;} + + else if ( (LA22_139=='\n') ) {s = 138;} + + else if ( ((LA22_139>='\u0000' && LA22_139<='\t')||(LA22_139>='\u000B' && LA22_139<='\f')||(LA22_139>='\u000E' && LA22_139<=',')||(LA22_139>='.' && LA22_139<='\uFFFF')) ) {s = 139;} + + if ( s>=0 ) return s; + break; + case 10 : + int LA22_153 = input.LA(1); + + s = -1; + if ( (LA22_153=='-') ) {s = 164;} + + else if ( (LA22_153=='\r') ) {s = 154;} + + else if ( (LA22_153=='\n') ) {s = 155;} + + else if ( ((LA22_153>='\u0000' && LA22_153<='\t')||(LA22_153>='\u000B' && LA22_153<='\f')||(LA22_153>='\u000E' && LA22_153<=',')||(LA22_153>='.' && LA22_153<='\uFFFF')) ) {s = 156;} + + if ( s>=0 ) return s; + break; + case 11 : + int LA22_175 = input.LA(1); + + s = -1; + if ( ((LA22_175>='\u0000' && LA22_175<='\uFFFF')) ) {s = 152;} + + else s = 100; + + if ( s>=0 ) return s; + break; + case 12 : + int LA22_163 = input.LA(1); + + s = -1; + if ( (LA22_163==']') ) {s = 171;} + + else if ( (LA22_163=='-') ) {s = 136;} + + else if ( (LA22_163=='\r') ) {s = 137;} + + else if ( (LA22_163=='\n') ) {s = 138;} + + else if ( ((LA22_163>='\u0000' && LA22_163<='\t')||(LA22_163>='\u000B' && LA22_163<='\f')||(LA22_163>='\u000E' && LA22_163<=',')||(LA22_163>='.' && LA22_163<='\\')||(LA22_163>='^' && LA22_163<='\uFFFF')) ) {s = 139;} + + if ( s>=0 ) return s; + break; + case 13 : + int LA22_136 = input.LA(1); + + s = -1; + if ( (LA22_136=='-') ) {s = 151;} + + else if ( (LA22_136=='\r') ) {s = 137;} + + else if ( (LA22_136=='\n') ) {s = 138;} + + else if ( ((LA22_136>='\u0000' && LA22_136<='\t')||(LA22_136>='\u000B' && LA22_136<='\f')||(LA22_136>='\u000E' && LA22_136<=',')||(LA22_136>='.' && LA22_136<='\uFFFF')) ) {s = 139;} + + if ( s>=0 ) return s; + break; + case 14 : + int LA22_154 = input.LA(1); + + s = -1; + if ( ((LA22_154>='\u0000' && LA22_154<='\t')||(LA22_154>='\u000B' && LA22_154<='\uFFFF')) ) {s = 152;} + + else if ( (LA22_154=='\n') ) {s = 155;} + + if ( s>=0 ) return s; + break; + case 15 : + int LA22_191 = input.LA(1); + + s = -1; + if ( (LA22_191=='\r') ) {s = 174;} + + else if ( (LA22_191=='\n') ) {s = 175;} + + else if ( (LA22_191=='-') ) {s = 176;} + + else if ( ((LA22_191>='\u0000' && LA22_191<='\t')||(LA22_191>='\u000B' && LA22_191<='\f')||(LA22_191>='\u000E' && LA22_191<=',')||(LA22_191>='.' && LA22_191<='\uFFFF')) ) {s = 177;} + + else s = 152; + + if ( s>=0 ) return s; + break; + case 16 : + int LA22_186 = input.LA(1); + + s = -1; + if ( (LA22_186==']') ) {s = 188;} + + else if ( (LA22_186=='\r') ) {s = 165;} + + else if ( (LA22_186=='\n') ) {s = 166;} + + else if ( (LA22_186=='-') ) {s = 167;} + + else if ( ((LA22_186>='\u0000' && LA22_186<='\t')||(LA22_186>='\u000B' && LA22_186<='\f')||(LA22_186>='\u000E' && LA22_186<=',')||(LA22_186>='.' && LA22_186<='\\')||(LA22_186>='^' && LA22_186<='\uFFFF')) ) {s = 168;} + + if ( s>=0 ) return s; + break; + case 17 : + int LA22_183 = input.LA(1); + + s = -1; + if ( (LA22_183=='=') ) {s = 186;} + + else if ( (LA22_183=='\r') ) {s = 165;} + + else if ( (LA22_183=='\n') ) {s = 166;} + + else if ( (LA22_183=='-') ) {s = 167;} + + else if ( ((LA22_183>='\u0000' && LA22_183<='\t')||(LA22_183>='\u000B' && LA22_183<='\f')||(LA22_183>='\u000E' && LA22_183<=',')||(LA22_183>='.' && LA22_183<='<')||(LA22_183>='>' && LA22_183<='\uFFFF')) ) {s = 168;} + + if ( s>=0 ) return s; + break; + case 18 : + int LA22_140 = input.LA(1); + + s = -1; + if ( (LA22_140=='-') ) {s = 153;} + + else if ( (LA22_140=='\r') ) {s = 154;} + + else if ( (LA22_140=='\n') ) {s = 155;} + + else if ( ((LA22_140>='\u0000' && LA22_140<='\t')||(LA22_140>='\u000B' && LA22_140<='\f')||(LA22_140>='\u000E' && LA22_140<=',')||(LA22_140>='.' && LA22_140<='\uFFFF')) ) {s = 156;} + + if ( s>=0 ) return s; + break; + case 19 : + int LA22_167 = input.LA(1); + + s = -1; + if ( (LA22_167=='-') ) {s = 173;} + + else if ( (LA22_167=='\r') ) {s = 165;} + + else if ( (LA22_167=='\n') ) {s = 166;} + + else if ( ((LA22_167>='\u0000' && LA22_167<='\t')||(LA22_167>='\u000B' && LA22_167<='\f')||(LA22_167>='\u000E' && LA22_167<=',')||(LA22_167>='.' && LA22_167<='\uFFFF')) ) {s = 168;} + + if ( s>=0 ) return s; + break; + case 20 : + int LA22_180 = input.LA(1); + + s = -1; + if ( (LA22_180=='=') ) {s = 183;} + + else if ( (LA22_180=='\r') ) {s = 165;} + + else if ( (LA22_180=='\n') ) {s = 166;} + + else if ( (LA22_180=='-') ) {s = 167;} + + else if ( ((LA22_180>='\u0000' && LA22_180<='\t')||(LA22_180>='\u000B' && LA22_180<='\f')||(LA22_180>='\u000E' && LA22_180<=',')||(LA22_180>='.' && LA22_180<='<')||(LA22_180>='>' && LA22_180<='\uFFFF')) ) {s = 168;} + + if ( s>=0 ) return s; + break; + case 21 : + int LA22_164 = input.LA(1); + + s = -1; + if ( (LA22_164==']') ) {s = 172;} + + else if ( (LA22_164=='-') ) {s = 164;} + + else if ( (LA22_164=='\r') ) {s = 154;} + + else if ( (LA22_164=='\n') ) {s = 155;} + + else if ( ((LA22_164>='\u0000' && LA22_164<='\t')||(LA22_164>='\u000B' && LA22_164<='\f')||(LA22_164>='\u000E' && LA22_164<=',')||(LA22_164>='.' && LA22_164<='\\')||(LA22_164>='^' && LA22_164<='\uFFFF')) ) {s = 156;} + + if ( s>=0 ) return s; + break; + case 22 : + int LA22_137 = input.LA(1); + + s = -1; + if ( ((LA22_137>='\u0000' && LA22_137<='\t')||(LA22_137>='\u000B' && LA22_137<='\uFFFF')) ) {s = 152;} + + else if ( (LA22_137=='\n') ) {s = 138;} + + if ( s>=0 ) return s; + break; + case 23 : + int LA22_177 = input.LA(1); + + s = -1; + if ( (LA22_177=='\r') ) {s = 174;} + + else if ( (LA22_177=='\n') ) {s = 175;} + + else if ( (LA22_177=='-') ) {s = 176;} + + else if ( ((LA22_177>='\u0000' && LA22_177<='\t')||(LA22_177>='\u000B' && LA22_177<='\f')||(LA22_177>='\u000E' && LA22_177<=',')||(LA22_177>='.' && LA22_177<='\uFFFF')) ) {s = 177;} + + if ( s>=0 ) return s; + break; + case 24 : + int LA22_176 = input.LA(1); + + s = -1; + if ( (LA22_176=='-') ) {s = 181;} + + else if ( (LA22_176=='\r') ) {s = 174;} + + else if ( (LA22_176=='\n') ) {s = 175;} + + else if ( ((LA22_176>='\u0000' && LA22_176<='\t')||(LA22_176>='\u000B' && LA22_176<='\f')||(LA22_176>='\u000E' && LA22_176<=',')||(LA22_176>='.' && LA22_176<='\uFFFF')) ) {s = 177;} + + if ( s>=0 ) return s; + break; + case 25 : + int LA22_187 = input.LA(1); + + s = -1; + if ( (LA22_187=='=') ) {s = 189;} + + else if ( (LA22_187=='\r') ) {s = 174;} + + else if ( (LA22_187=='\n') ) {s = 175;} + + else if ( (LA22_187=='-') ) {s = 176;} + + else if ( ((LA22_187>='\u0000' && LA22_187<='\t')||(LA22_187>='\u000B' && LA22_187<='\f')||(LA22_187>='\u000E' && LA22_187<=',')||(LA22_187>='.' && LA22_187<='<')||(LA22_187>='>' && LA22_187<='\uFFFF')) ) {s = 177;} + + if ( s>=0 ) return s; + break; + case 26 : + int LA22_184 = input.LA(1); + + s = -1; + if ( (LA22_184=='=') ) {s = 187;} + + else if ( (LA22_184=='\r') ) {s = 174;} + + else if ( (LA22_184=='\n') ) {s = 175;} + + else if ( (LA22_184=='-') ) {s = 176;} + + else if ( ((LA22_184>='\u0000' && LA22_184<='\t')||(LA22_184>='\u000B' && LA22_184<='\f')||(LA22_184>='\u000E' && LA22_184<=',')||(LA22_184>='.' && LA22_184<='<')||(LA22_184>='>' && LA22_184<='\uFFFF')) ) {s = 177;} + + if ( s>=0 ) return s; + break; + case 27 : + int LA22_155 = input.LA(1); + + s = -1; + if ( ((LA22_155>='\u0000' && LA22_155<='\uFFFF')) ) {s = 152;} + + else s = 100; + + if ( s>=0 ) return s; + break; + case 28 : + int LA22_66 = input.LA(1); + + s = -1; + if ( (LA22_66=='[') ) {s = 99;} + + else if ( ((LA22_66>='\u0000' && LA22_66<='Z')||(LA22_66>='\\' && LA22_66<='\uFFFF')) ) {s = 100;} + + if ( s>=0 ) return s; + break; + case 29 : + int LA22_173 = input.LA(1); + + s = -1; + if ( (LA22_173==']') ) {s = 180;} + + else if ( (LA22_173=='-') ) {s = 173;} + + else if ( (LA22_173=='\r') ) {s = 165;} + + else if ( (LA22_173=='\n') ) {s = 166;} + + else if ( ((LA22_173>='\u0000' && LA22_173<='\t')||(LA22_173>='\u000B' && LA22_173<='\f')||(LA22_173>='\u000E' && LA22_173<=',')||(LA22_173>='.' && LA22_173<='\\')||(LA22_173>='^' && LA22_173<='\uFFFF')) ) {s = 168;} + + if ( s>=0 ) return s; + break; + case 30 : + int LA22_188 = input.LA(1); + + s = -1; + if ( (LA22_188=='\r') ) {s = 165;} + + else if ( (LA22_188=='\n') ) {s = 166;} + + else if ( (LA22_188=='-') ) {s = 167;} + + else if ( ((LA22_188>='\u0000' && LA22_188<='\t')||(LA22_188>='\u000B' && LA22_188<='\f')||(LA22_188>='\u000E' && LA22_188<=',')||(LA22_188>='.' && LA22_188<='\uFFFF')) ) {s = 168;} + + else s = 152; + + if ( s>=0 ) return s; + break; + case 31 : + int LA22_189 = input.LA(1); + + s = -1; + if ( (LA22_189=='=') ) {s = 190;} + + else if ( (LA22_189=='\r') ) {s = 174;} + + else if ( (LA22_189=='\n') ) {s = 175;} + + else if ( (LA22_189=='-') ) {s = 176;} + + else if ( ((LA22_189>='\u0000' && LA22_189<='\t')||(LA22_189>='\u000B' && LA22_189<='\f')||(LA22_189>='\u000E' && LA22_189<=',')||(LA22_189>='.' && LA22_189<='<')||(LA22_189>='>' && LA22_189<='\uFFFF')) ) {s = 177;} + + if ( s>=0 ) return s; + break; + case 32 : + int LA22_190 = input.LA(1); + + s = -1; + if ( (LA22_190==']') ) {s = 191;} + + else if ( (LA22_190=='\r') ) {s = 174;} + + else if ( (LA22_190=='\n') ) {s = 175;} + + else if ( (LA22_190=='-') ) {s = 176;} + + else if ( ((LA22_190>='\u0000' && LA22_190<='\t')||(LA22_190>='\u000B' && LA22_190<='\f')||(LA22_190>='\u000E' && LA22_190<=',')||(LA22_190>='.' && LA22_190<='\\')||(LA22_190>='^' && LA22_190<='\uFFFF')) ) {s = 177;} + + if ( s>=0 ) return s; + break; + case 33 : + int LA22_157 = input.LA(1); + + s = -1; + if ( (LA22_157=='\r') ) {s = 165;} + + else if ( (LA22_157=='\n') ) {s = 166;} + + else if ( (LA22_157=='-') ) {s = 167;} + + else if ( ((LA22_157>='\u0000' && LA22_157<='\t')||(LA22_157>='\u000B' && LA22_157<='\f')||(LA22_157>='\u000E' && LA22_157<=',')||(LA22_157>='.' && LA22_157<='\uFFFF')) ) {s = 168;} + + if ( s>=0 ) return s; + break; + case 34 : + int LA22_138 = input.LA(1); + + s = -1; + if ( ((LA22_138>='\u0000' && LA22_138<='\uFFFF')) ) {s = 152;} + + else s = 100; + + if ( s>=0 ) return s; + break; + case 35 : + int LA22_181 = input.LA(1); + + s = -1; + if ( (LA22_181==']') ) {s = 184;} + + else if ( (LA22_181=='-') ) {s = 181;} + + else if ( (LA22_181=='\r') ) {s = 174;} + + else if ( (LA22_181=='\n') ) {s = 175;} + + else if ( ((LA22_181>='\u0000' && LA22_181<='\t')||(LA22_181>='\u000B' && LA22_181<='\f')||(LA22_181>='\u000E' && LA22_181<=',')||(LA22_181>='.' && LA22_181<='\\')||(LA22_181>='^' && LA22_181<='\uFFFF')) ) {s = 177;} + + if ( s>=0 ) return s; + break; + case 36 : + int LA22_185 = input.LA(1); + + s = -1; + if ( (LA22_185=='-') ) {s = 153;} + + else if ( (LA22_185=='\r') ) {s = 154;} + + else if ( (LA22_185=='\n') ) {s = 155;} + + else if ( ((LA22_185>='\u0000' && LA22_185<='\t')||(LA22_185>='\u000B' && LA22_185<='\f')||(LA22_185>='\u000E' && LA22_185<=',')||(LA22_185>='.' && LA22_185<='\uFFFF')) ) {s = 156;} + + else s = 152; + + if ( s>=0 ) return s; + break; + case 37 : + int LA22_168 = input.LA(1); + + s = -1; + if ( (LA22_168=='\r') ) {s = 165;} + + else if ( (LA22_168=='\n') ) {s = 166;} + + else if ( (LA22_168=='-') ) {s = 167;} + + else if ( ((LA22_168>='\u0000' && LA22_168<='\t')||(LA22_168>='\u000B' && LA22_168<='\f')||(LA22_168>='\u000E' && LA22_168<=',')||(LA22_168>='.' && LA22_168<='\uFFFF')) ) {s = 168;} + + if ( s>=0 ) return s; + break; + case 38 : + int LA22_169 = input.LA(1); + + s = -1; + if ( (LA22_169=='\r') ) {s = 174;} + + else if ( (LA22_169=='\n') ) {s = 175;} + + else if ( (LA22_169=='-') ) {s = 176;} + + else if ( ((LA22_169>='\u0000' && LA22_169<='\t')||(LA22_169>='\u000B' && LA22_169<='\f')||(LA22_169>='\u000E' && LA22_169<=',')||(LA22_169>='.' && LA22_169<='\uFFFF')) ) {s = 177;} + + if ( s>=0 ) return s; + break; + case 39 : + int LA22_174 = input.LA(1); + + s = -1; + if ( ((LA22_174>='\u0000' && LA22_174<='\t')||(LA22_174>='\u000B' && LA22_174<='\uFFFF')) ) {s = 152;} + + else if ( (LA22_174=='\n') ) {s = 175;} + + if ( s>=0 ) return s; + break; + case 40 : + int LA22_99 = input.LA(1); + + s = -1; + if ( (LA22_99=='[') ) {s = 120;} + + else if ( (LA22_99=='=') ) {s = 121;} + + else if ( ((LA22_99>='\u0000' && LA22_99<='<')||(LA22_99>='>' && LA22_99<='Z')||(LA22_99>='\\' && LA22_99<='\uFFFF')) ) {s = 100;} + + if ( s>=0 ) return s; + break; + case 41 : + int LA22_166 = input.LA(1); + + s = -1; + if ( ((LA22_166>='\u0000' && LA22_166<='\uFFFF')) ) {s = 152;} + + else s = 100; + + if ( s>=0 ) return s; + break; + case 42 : + int LA22_121 = input.LA(1); + + s = -1; + if ( (LA22_121=='[') ) {s = 140;} + + else if ( (LA22_121=='=') ) {s = 141;} + + else if ( ((LA22_121>='\u0000' && LA22_121<='<')||(LA22_121>='>' && LA22_121<='Z')||(LA22_121>='\\' && LA22_121<='\uFFFF')) ) {s = 100;} + + if ( s>=0 ) return s; + break; + case 43 : + int LA22_156 = input.LA(1); + + s = -1; + if ( (LA22_156=='\r') ) {s = 154;} + + else if ( (LA22_156=='\n') ) {s = 155;} + + else if ( (LA22_156=='-') ) {s = 153;} + + else if ( ((LA22_156>='\u0000' && LA22_156<='\t')||(LA22_156>='\u000B' && LA22_156<='\f')||(LA22_156>='\u000E' && LA22_156<=',')||(LA22_156>='.' && LA22_156<='\uFFFF')) ) {s = 156;} + + if ( s>=0 ) return s; + break; + } + NoViableAltException nvae = + new NoViableAltException(getDescription(), 22, _s, input); + error(nvae); + throw nvae; + } + } + + +} \ No newline at end of file diff --git a/src/jse/org/luaj/vm2/luajc/antlr/LuaParser.java b/src/jse/org/luaj/vm2/luajc/antlr/LuaParser.java new file mode 100644 index 00000000..2fe29941 --- /dev/null +++ b/src/jse/org/luaj/vm2/luajc/antlr/LuaParser.java @@ -0,0 +1,5168 @@ +// $ANTLR 3.1.3 Mar 17, 2009 19:23:44 Lua.g 2009-10-19 10:13:33 + + package org.luaj.vm2.luajc.antlr; + import org.luaj.vm2.luajc.lst.*; + + +import org.antlr.runtime.*; +import java.util.Stack; +import java.util.List; +import java.util.ArrayList; +import java.util.Map; +import java.util.HashMap; +public class LuaParser extends Parser { + public static final String[] tokenNames = new String[] { + "", "", "", "", "NAME", "INT", "FLOAT1", "FLOAT2", "FLOAT3", "EXP", "HEX", "NORMALSTRING", "CHARSTRING", "LONGSTRING", "EscapeSequence", "DecimalEscape", "COMMENT", "LINE_COMMENT", "WS", "NEWLINE", "';'", "'='", "'do'", "'end'", "'while'", "'repeat'", "'until'", "'for'", "','", "'in'", "'function'", "'local'", "'if'", "'then'", "'elseif'", "'else'", "'return'", "'break'", "'.'", "':'", "'nil'", "'false'", "'true'", "'...'", "'('", "')'", "'['", "']'", "'{'", "'}'", "'+'", "'-'", "'*'", "'/'", "'^'", "'%'", "'..'", "'<'", "'<='", "'>'", "'>='", "'=='", "'~='", "'and'", "'or'", "'not'", "'#'" + }; + public static final int T__66=66; + public static final int T__64=64; + public static final int T__29=29; + public static final int T__65=65; + public static final int T__28=28; + public static final int T__62=62; + public static final int T__27=27; + public static final int T__63=63; + public static final int T__26=26; + public static final int T__25=25; + public static final int T__24=24; + public static final int T__23=23; + public static final int T__22=22; + public static final int T__21=21; + public static final int T__20=20; + public static final int T__61=61; + public static final int T__60=60; + public static final int EOF=-1; + public static final int FLOAT3=8; + public static final int FLOAT2=7; + public static final int FLOAT1=6; + public static final int T__55=55; + public static final int T__56=56; + public static final int T__57=57; + public static final int NAME=4; + public static final int T__58=58; + public static final int T__51=51; + public static final int T__52=52; + public static final int T__53=53; + public static final int T__54=54; + public static final int EXP=9; + public static final int HEX=10; + public static final int T__59=59; + public static final int DecimalEscape=15; + public static final int COMMENT=16; + public static final int T__50=50; + public static final int T__42=42; + public static final int T__43=43; + public static final int T__40=40; + public static final int T__41=41; + public static final int T__46=46; + public static final int T__47=47; + public static final int T__44=44; + public static final int T__45=45; + public static final int LINE_COMMENT=17; + public static final int T__48=48; + public static final int T__49=49; + public static final int CHARSTRING=12; + public static final int INT=5; + public static final int LONGSTRING=13; + public static final int T__30=30; + public static final int NORMALSTRING=11; + public static final int T__31=31; + public static final int T__32=32; + public static final int WS=18; + public static final int T__33=33; + public static final int T__34=34; + public static final int NEWLINE=19; + public static final int T__35=35; + public static final int T__36=36; + public static final int T__37=37; + public static final int T__38=38; + public static final int T__39=39; + public static final int EscapeSequence=14; + + // delegates + // delegators + + + public LuaParser(TokenStream input) { + this(input, new RecognizerSharedState()); + } + public LuaParser(TokenStream input, RecognizerSharedState state) { + super(input, state); + + } + + + public String[] getTokenNames() { return LuaParser.tokenNames; } + public String getGrammarFileName() { return "Lua.g"; } + + + LSChunk CHK = null; + + + + // $ANTLR start "chunk" + // Lua.g:22:1: chunk[String chunkname] returns [LSChunk c] : funcblock[CHK.function] ; + public final LSChunk chunk(String chunkname) throws RecognitionException { + LSChunk c = null; + + CHK = new LSChunk(chunkname); + try { + // Lua.g:24:2: ( funcblock[CHK.function] ) + // Lua.g:24:4: funcblock[CHK.function] + { + pushFollow(FOLLOW_funcblock_in_chunk58); + funcblock(CHK.function); + + state._fsp--; + if (state.failed) return c; + if ( state.backtracking==0 ) { + c =CHK; + } + + } + + } + catch (RecognitionException re) { + reportError(re); + recover(input,re); + } + finally { + } + return c; + } + // $ANTLR end "chunk" + + protected static class funcblock_scope { + LSFunction func; + } + protected Stack funcblock_stack = new Stack(); + + + // $ANTLR start "funcblock" + // Lua.g:27:1: funcblock[LSFunction f] : block ; + public final void funcblock(LSFunction f) throws RecognitionException { + funcblock_stack.push(new funcblock_scope()); + List block1 = null; + + + ((funcblock_scope)funcblock_stack.peek()).func = f; + try { + // Lua.g:30:2: ( block ) + // Lua.g:30:4: block + { + if ( state.backtracking==0 ) { + CHK.pushScope("body"); + } + pushFollow(FOLLOW_block_in_funcblock88); + block1=block(); + + state._fsp--; + if (state.failed) return ; + if ( state.backtracking==0 ) { + f.setStatements(block1); CHK.popScope("body"); + } + + } + + } + catch (RecognitionException re) { + reportError(re); + recover(input,re); + } + finally { + funcblock_stack.pop(); + } + return ; + } + // $ANTLR end "funcblock" + + + // $ANTLR start "block" + // Lua.g:33:1: block returns [List stats] : ( stat ( ';' )? )* ( laststat ( ';' )? )? ; + public final List block() throws RecognitionException { + List stats = null; + + LSStatement stat2 = null; + + LSStatement laststat3 = null; + + + stats = new ArrayList(); + try { + // Lua.g:35:2: ( ( stat ( ';' )? )* ( laststat ( ';' )? )? ) + // Lua.g:35:4: ( stat ( ';' )? )* ( laststat ( ';' )? )? + { + // Lua.g:35:4: ( stat ( ';' )? )* + loop2: + do { + int alt2=2; + int LA2_0 = input.LA(1); + + if ( (LA2_0==NAME||LA2_0==22||(LA2_0>=24 && LA2_0<=25)||LA2_0==27||(LA2_0>=30 && LA2_0<=32)||LA2_0==44) ) { + alt2=1; + } + + + switch (alt2) { + case 1 : + // Lua.g:35:5: stat ( ';' )? + { + pushFollow(FOLLOW_stat_in_block113); + stat2=stat(); + + state._fsp--; + if (state.failed) return stats; + if ( state.backtracking==0 ) { + stats.add(stat2); + } + // Lua.g:35:33: ( ';' )? + int alt1=2; + int LA1_0 = input.LA(1); + + if ( (LA1_0==20) ) { + alt1=1; + } + switch (alt1) { + case 1 : + // Lua.g:35:34: ';' + { + match(input,20,FOLLOW_20_in_block118); if (state.failed) return stats; + + } + break; + + } + + + } + break; + + default : + break loop2; + } + } while (true); + + // Lua.g:36:3: ( laststat ( ';' )? )? + int alt4=2; + int LA4_0 = input.LA(1); + + if ( ((LA4_0>=36 && LA4_0<=37)) ) { + alt4=1; + } + switch (alt4) { + case 1 : + // Lua.g:36:4: laststat ( ';' )? + { + pushFollow(FOLLOW_laststat_in_block128); + laststat3=laststat(); + + state._fsp--; + if (state.failed) return stats; + if ( state.backtracking==0 ) { + stats.add(laststat3); + } + // Lua.g:36:40: ( ';' )? + int alt3=2; + int LA3_0 = input.LA(1); + + if ( (LA3_0==20) ) { + alt3=1; + } + switch (alt3) { + case 1 : + // Lua.g:36:41: ';' + { + match(input,20,FOLLOW_20_in_block133); if (state.failed) return stats; + + } + break; + + } + + + } + break; + + } + + + } + + } + catch (RecognitionException re) { + reportError(re); + recover(input,re); + } + finally { + } + return stats; + } + // $ANTLR end "block" + + + // $ANTLR start "stat" + // Lua.g:39:1: stat returns [LSStatement s] : ( varlist1 '=' explist1 | functioncall | 'do' block 'end' | 'while' exp 'do' block 'end' | 'repeat' block 'until' exp | ifstat | 'for' NAME '=' e1= exp ',' e2= exp ( ',' e3= exp )? 'do' block 'end' | 'for' namelist 'in' explist1 'do' block 'end' | 'function' funcname funcbody | 'local' 'function' NAME funcbody | 'local' namelist ( '=' explist1 )? ); + public final LSStatement stat() throws RecognitionException { + LSStatement s = null; + + Token NAME13=null; + Token NAME20=null; + LSExpression e1 = null; + + LSExpression e2 = null; + + LSExpression e3 = null; + + List varlist14 = null; + + List explist15 = null; + + LSVariable functioncall6 = null; + + List block7 = null; + + LSExpression exp8 = null; + + List block9 = null; + + List block10 = null; + + LSExpression exp11 = null; + + LSStatement ifstat12 = null; + + List block14 = null; + + List namelist15 = null; + + List explist116 = null; + + List block17 = null; + + LSVariable funcname18 = null; + + LSFunction funcbody19 = null; + + LSFunction funcbody21 = null; + + List namelist22 = null; + + List explist123 = null; + + + Name name=null; List names=null; + try { + // Lua.g:41:2: ( varlist1 '=' explist1 | functioncall | 'do' block 'end' | 'while' exp 'do' block 'end' | 'repeat' block 'until' exp | ifstat | 'for' NAME '=' e1= exp ',' e2= exp ( ',' e3= exp )? 'do' block 'end' | 'for' namelist 'in' explist1 'do' block 'end' | 'function' funcname funcbody | 'local' 'function' NAME funcbody | 'local' namelist ( '=' explist1 )? ) + int alt7=11; + alt7 = dfa7.predict(input); + switch (alt7) { + case 1 : + // Lua.g:41:4: varlist1 '=' explist1 + { + pushFollow(FOLLOW_varlist1_in_stat159); + varlist14=varlist1(); + + state._fsp--; + if (state.failed) return s; + match(input,21,FOLLOW_21_in_stat161); if (state.failed) return s; + pushFollow(FOLLOW_explist1_in_stat163); + explist15=explist1(); + + state._fsp--; + if (state.failed) return s; + if ( state.backtracking==0 ) { + s =LSStatement.varAssignStatement(varlist14,explist15, CHK.peekScope(), ((funcblock_scope)funcblock_stack.peek()).func); + } + + } + break; + case 2 : + // Lua.g:42:4: functioncall + { + pushFollow(FOLLOW_functioncall_in_stat183); + functioncall6=functioncall(); + + state._fsp--; + if (state.failed) return s; + if ( state.backtracking==0 ) { + s =LSStatement.functionCallStatement(functioncall6); + } + + } + break; + case 3 : + // Lua.g:43:4: 'do' block 'end' + { + match(input,22,FOLLOW_22_in_stat213); if (state.failed) return s; + if ( state.backtracking==0 ) { + CHK.pushScope("do"); + } + pushFollow(FOLLOW_block_in_stat217); + block7=block(); + + state._fsp--; + if (state.failed) return s; + if ( state.backtracking==0 ) { + CHK.popScope("do"); + } + match(input,23,FOLLOW_23_in_stat221); if (state.failed) return s; + if ( state.backtracking==0 ) { + s =LSStatement.doBlockStatement(block7); + } + + } + break; + case 4 : + // Lua.g:45:4: 'while' exp 'do' block 'end' + { + match(input,24,FOLLOW_24_in_stat286); if (state.failed) return s; + pushFollow(FOLLOW_exp_in_stat288); + exp8=exp(); + + state._fsp--; + if (state.failed) return s; + match(input,22,FOLLOW_22_in_stat290); if (state.failed) return s; + if ( state.backtracking==0 ) { + CHK.pushScope("while"); + } + pushFollow(FOLLOW_block_in_stat294); + block9=block(); + + state._fsp--; + if (state.failed) return s; + if ( state.backtracking==0 ) { + CHK.popScope("while"); + } + match(input,23,FOLLOW_23_in_stat298); if (state.failed) return s; + if ( state.backtracking==0 ) { + s =LSStatement.whileLoopStatement(exp8,block9); + } + + } + break; + case 5 : + // Lua.g:47:4: 'repeat' block 'until' exp + { + match(input,25,FOLLOW_25_in_stat344); if (state.failed) return s; + if ( state.backtracking==0 ) { + CHK.pushScope("repeat"); + } + pushFollow(FOLLOW_block_in_stat349); + block10=block(); + + state._fsp--; + if (state.failed) return s; + if ( state.backtracking==0 ) { + CHK.popScope("repeat"); + } + match(input,26,FOLLOW_26_in_stat354); if (state.failed) return s; + pushFollow(FOLLOW_exp_in_stat356); + exp11=exp(); + + state._fsp--; + if (state.failed) return s; + if ( state.backtracking==0 ) { + s =LSStatement.repeatUntilStatement(block10,exp11); + } + + } + break; + case 6 : + // Lua.g:49:4: ifstat + { + pushFollow(FOLLOW_ifstat_in_stat410); + ifstat12=ifstat(); + + state._fsp--; + if (state.failed) return s; + if ( state.backtracking==0 ) { + s =ifstat12; + } + + } + break; + case 7 : + // Lua.g:50:4: 'for' NAME '=' e1= exp ',' e2= exp ( ',' e3= exp )? 'do' block 'end' + { + match(input,27,FOLLOW_27_in_stat445); if (state.failed) return s; + if ( state.backtracking==0 ) { + CHK.pushScope("fori"); + } + NAME13=(Token)match(input,NAME,FOLLOW_NAME_in_stat449); if (state.failed) return s; + if ( state.backtracking==0 ) { + name=CHK.declare((NAME13!=null?NAME13.getText():null)); + } + match(input,21,FOLLOW_21_in_stat453); if (state.failed) return s; + pushFollow(FOLLOW_exp_in_stat457); + e1=exp(); + + state._fsp--; + if (state.failed) return s; + match(input,28,FOLLOW_28_in_stat459); if (state.failed) return s; + pushFollow(FOLLOW_exp_in_stat463); + e2=exp(); + + state._fsp--; + if (state.failed) return s; + // Lua.g:50:94: ( ',' e3= exp )? + int alt5=2; + int LA5_0 = input.LA(1); + + if ( (LA5_0==28) ) { + alt5=1; + } + switch (alt5) { + case 1 : + // Lua.g:50:95: ',' e3= exp + { + match(input,28,FOLLOW_28_in_stat466); if (state.failed) return s; + pushFollow(FOLLOW_exp_in_stat470); + e3=exp(); + + state._fsp--; + if (state.failed) return s; + + } + break; + + } + + match(input,22,FOLLOW_22_in_stat474); if (state.failed) return s; + if ( state.backtracking==0 ) { + CHK.pushScope("foriblock"); + } + pushFollow(FOLLOW_block_in_stat478); + block14=block(); + + state._fsp--; + if (state.failed) return s; + if ( state.backtracking==0 ) { + CHK.popScope("foriblock"); + } + match(input,23,FOLLOW_23_in_stat482); if (state.failed) return s; + if ( state.backtracking==0 ) { + s =LSStatement.forLoopStatement(name,e1,e2,e3,block14,CHK.peekScope()); CHK.popScope("fori"); + } + + } + break; + case 8 : + // Lua.g:52:4: 'for' namelist 'in' explist1 'do' block 'end' + { + match(input,27,FOLLOW_27_in_stat528); if (state.failed) return s; + if ( state.backtracking==0 ) { + CHK.pushScope("for"); + } + pushFollow(FOLLOW_namelist_in_stat533); + namelist15=namelist(); + + state._fsp--; + if (state.failed) return s; + if ( state.backtracking==0 ) { + names=CHK.declare(namelist15); + } + match(input,29,FOLLOW_29_in_stat537); if (state.failed) return s; + pushFollow(FOLLOW_explist1_in_stat539); + explist116=explist1(); + + state._fsp--; + if (state.failed) return s; + match(input,22,FOLLOW_22_in_stat541); if (state.failed) return s; + if ( state.backtracking==0 ) { + CHK.pushScope("forblock"); + } + pushFollow(FOLLOW_block_in_stat545); + block17=block(); + + state._fsp--; + if (state.failed) return s; + if ( state.backtracking==0 ) { + CHK.popScope("forblock"); + } + match(input,23,FOLLOW_23_in_stat548); if (state.failed) return s; + if ( state.backtracking==0 ) { + s =LSStatement.forListStatement(names,explist116,block17,CHK.peekScope(), ((funcblock_scope)funcblock_stack.peek()).func); CHK.popScope("for"); + } + + } + break; + case 9 : + // Lua.g:54:4: 'function' funcname funcbody + { + match(input,30,FOLLOW_30_in_stat593); if (state.failed) return s; + pushFollow(FOLLOW_funcname_in_stat595); + funcname18=funcname(); + + state._fsp--; + if (state.failed) return s; + pushFollow(FOLLOW_funcbody_in_stat597); + funcbody19=funcbody(); + + state._fsp--; + if (state.failed) return s; + if ( state.backtracking==0 ) { + s =LSStatement.varFunctionStatement(funcname18,funcbody19); + } + + } + break; + case 10 : + // Lua.g:55:4: 'local' 'function' NAME funcbody + { + match(input,31,FOLLOW_31_in_stat610); if (state.failed) return s; + match(input,30,FOLLOW_30_in_stat612); if (state.failed) return s; + NAME20=(Token)match(input,NAME,FOLLOW_NAME_in_stat614); if (state.failed) return s; + if ( state.backtracking==0 ) { + name=CHK.declare((NAME20!=null?NAME20.getText():null)); + } + pushFollow(FOLLOW_funcbody_in_stat618); + funcbody21=funcbody(); + + state._fsp--; + if (state.failed) return s; + if ( state.backtracking==0 ) { + s =LSStatement.localFunctionStatement(name,funcbody21); + } + + } + break; + case 11 : + // Lua.g:57:4: 'local' namelist ( '=' explist1 )? + { + match(input,31,FOLLOW_31_in_stat666); if (state.failed) return s; + pushFollow(FOLLOW_namelist_in_stat668); + namelist22=namelist(); + + state._fsp--; + if (state.failed) return s; + // Lua.g:57:21: ( '=' explist1 )? + int alt6=2; + int LA6_0 = input.LA(1); + + if ( (LA6_0==21) ) { + alt6=1; + } + switch (alt6) { + case 1 : + // Lua.g:57:22: '=' explist1 + { + match(input,21,FOLLOW_21_in_stat671); if (state.failed) return s; + pushFollow(FOLLOW_explist1_in_stat673); + explist123=explist1(); + + state._fsp--; + if (state.failed) return s; + + } + break; + + } + + if ( state.backtracking==0 ) { + s =LSStatement.localAssignStatement(CHK.declare(namelist22),explist123,CHK.peekScope(), ((funcblock_scope)funcblock_stack.peek()).func); + } + + } + break; + + } + } + catch (RecognitionException re) { + reportError(re); + recover(input,re); + } + finally { + } + return s; + } + // $ANTLR end "stat" + + protected static class ifstat_scope { + LSIfStatement current; + } + protected Stack ifstat_stack = new Stack(); + + + // $ANTLR start "ifstat" + // Lua.g:60:1: ifstat returns [LSStatement s] : 'if' e1= exp 'then' b1= block ( 'elseif' e2= exp 'then' b2= block )* ( 'else' b3= block )? 'end' ; + public final LSStatement ifstat() throws RecognitionException { + ifstat_stack.push(new ifstat_scope()); + LSStatement s = null; + + LSExpression e1 = null; + + List b1 = null; + + LSExpression e2 = null; + + List b2 = null; + + List b3 = null; + + + try { + // Lua.g:62:2: ( 'if' e1= exp 'then' b1= block ( 'elseif' e2= exp 'then' b2= block )* ( 'else' b3= block )? 'end' ) + // Lua.g:62:4: 'if' e1= exp 'then' b1= block ( 'elseif' e2= exp 'then' b2= block )* ( 'else' b3= block )? 'end' + { + match(input,32,FOLLOW_32_in_ifstat699); if (state.failed) return s; + pushFollow(FOLLOW_exp_in_ifstat703); + e1=exp(); + + state._fsp--; + if (state.failed) return s; + match(input,33,FOLLOW_33_in_ifstat705); if (state.failed) return s; + if ( state.backtracking==0 ) { + CHK.pushScope("if"); + } + pushFollow(FOLLOW_block_in_ifstat712); + b1=block(); + + state._fsp--; + if (state.failed) return s; + if ( state.backtracking==0 ) { + ((ifstat_scope)ifstat_stack.peek()).current =new LSIfStatement(e1,b1); CHK.popScope("if"); + } + // Lua.g:63:4: ( 'elseif' e2= exp 'then' b2= block )* + loop8: + do { + int alt8=2; + int LA8_0 = input.LA(1); + + if ( (LA8_0==34) ) { + alt8=1; + } + + + switch (alt8) { + case 1 : + // Lua.g:63:5: 'elseif' e2= exp 'then' b2= block + { + match(input,34,FOLLOW_34_in_ifstat721); if (state.failed) return s; + pushFollow(FOLLOW_exp_in_ifstat725); + e2=exp(); + + state._fsp--; + if (state.failed) return s; + match(input,33,FOLLOW_33_in_ifstat727); if (state.failed) return s; + if ( state.backtracking==0 ) { + CHK.pushScope("elseif"); + } + pushFollow(FOLLOW_block_in_ifstat734); + b2=block(); + + state._fsp--; + if (state.failed) return s; + if ( state.backtracking==0 ) { + ((ifstat_scope)ifstat_stack.peek()).current.addElseif(e2,b2); CHK.popScope("elseif"); + } + + } + break; + + default : + break loop8; + } + } while (true); + + // Lua.g:64:4: ( 'else' b3= block )? + int alt9=2; + int LA9_0 = input.LA(1); + + if ( (LA9_0==35) ) { + alt9=1; + } + switch (alt9) { + case 1 : + // Lua.g:64:5: 'else' b3= block + { + match(input,35,FOLLOW_35_in_ifstat745); if (state.failed) return s; + if ( state.backtracking==0 ) { + CHK.pushScope("else"); + } + pushFollow(FOLLOW_block_in_ifstat752); + b3=block(); + + state._fsp--; + if (state.failed) return s; + if ( state.backtracking==0 ) { + ((ifstat_scope)ifstat_stack.peek()).current.addElse(b3); CHK.popScope("else"); + } + + } + break; + + } + + match(input,23,FOLLOW_23_in_ifstat762); if (state.failed) return s; + if ( state.backtracking==0 ) { + s =((ifstat_scope)ifstat_stack.peek()).current; + } + + } + + } + catch (RecognitionException re) { + reportError(re); + recover(input,re); + } + finally { + ifstat_stack.pop(); + } + return s; + } + // $ANTLR end "ifstat" + + + // $ANTLR start "laststat" + // Lua.g:69:1: laststat returns [LSStatement s] : ( 'return' (e= explist1 )? | 'break' ); + public final LSStatement laststat() throws RecognitionException { + LSStatement s = null; + + List e = null; + + + try { + // Lua.g:70:2: ( 'return' (e= explist1 )? | 'break' ) + int alt11=2; + int LA11_0 = input.LA(1); + + if ( (LA11_0==36) ) { + alt11=1; + } + else if ( (LA11_0==37) ) { + alt11=2; + } + else { + if (state.backtracking>0) {state.failed=true; return s;} + NoViableAltException nvae = + new NoViableAltException("", 11, 0, input); + + throw nvae; + } + switch (alt11) { + case 1 : + // Lua.g:70:4: 'return' (e= explist1 )? + { + match(input,36,FOLLOW_36_in_laststat782); if (state.failed) return s; + // Lua.g:70:13: (e= explist1 )? + int alt10=2; + int LA10_0 = input.LA(1); + + if ( ((LA10_0>=NAME && LA10_0<=LONGSTRING)||LA10_0==30||(LA10_0>=40 && LA10_0<=44)||LA10_0==48||LA10_0==51||(LA10_0>=65 && LA10_0<=66)) ) { + alt10=1; + } + switch (alt10) { + case 1 : + // Lua.g:70:14: e= explist1 + { + pushFollow(FOLLOW_explist1_in_laststat787); + e=explist1(); + + state._fsp--; + if (state.failed) return s; + + } + break; + + } + + if ( state.backtracking==0 ) { + s =LSStatement.returnStatement(((funcblock_scope)funcblock_stack.peek()).func,e); + } + + } + break; + case 2 : + // Lua.g:71:4: 'break' + { + match(input,37,FOLLOW_37_in_laststat797); if (state.failed) return s; + if ( state.backtracking==0 ) { + s =LSStatement.breakStatement(); + } + + } + break; + + } + } + catch (RecognitionException re) { + reportError(re); + recover(input,re); + } + finally { + } + return s; + } + // $ANTLR end "laststat" + + + // $ANTLR start "funcname" + // Lua.g:74:1: funcname returns [LSVariable v] : n= NAME ( '.' n2= NAME )* ( ':' n3= NAME )? ; + public final LSVariable funcname() throws RecognitionException { + LSVariable v = null; + + Token n=null; + Token n2=null; + Token n3=null; + + try { + // Lua.g:75:2: (n= NAME ( '.' n2= NAME )* ( ':' n3= NAME )? ) + // Lua.g:75:4: n= NAME ( '.' n2= NAME )* ( ':' n3= NAME )? + { + n=(Token)match(input,NAME,FOLLOW_NAME_in_funcname831); if (state.failed) return v; + if ( state.backtracking==0 ) { + v = LSVariable.nameVariable(CHK.reference((n!=null?n.getText():null),((funcblock_scope)funcblock_stack.peek()).func)); + } + // Lua.g:76:3: ( '.' n2= NAME )* + loop12: + do { + int alt12=2; + int LA12_0 = input.LA(1); + + if ( (LA12_0==38) ) { + alt12=1; + } + + + switch (alt12) { + case 1 : + // Lua.g:76:4: '.' n2= NAME + { + match(input,38,FOLLOW_38_in_funcname839); if (state.failed) return v; + n2=(Token)match(input,NAME,FOLLOW_NAME_in_funcname843); if (state.failed) return v; + if ( state.backtracking==0 ) { + v = v.fieldVariable((n2!=null?n2.getText():null)); + } + + } + break; + + default : + break loop12; + } + } while (true); + + // Lua.g:77:3: ( ':' n3= NAME )? + int alt13=2; + int LA13_0 = input.LA(1); + + if ( (LA13_0==39) ) { + alt13=1; + } + switch (alt13) { + case 1 : + // Lua.g:77:4: ':' n3= NAME + { + match(input,39,FOLLOW_39_in_funcname854); if (state.failed) return v; + n3=(Token)match(input,NAME,FOLLOW_NAME_in_funcname858); if (state.failed) return v; + if ( state.backtracking==0 ) { + v = v.methodVariable((n3!=null?n3.getText():null)); + } + + } + break; + + } + + + } + + } + catch (RecognitionException re) { + reportError(re); + recover(input,re); + } + finally { + } + return v; + } + // $ANTLR end "funcname" + + + // $ANTLR start "varlist1" + // Lua.g:80:1: varlist1 returns [List vars] : v1= var ( ',' v2= var )* ; + public final List varlist1() throws RecognitionException { + List vars = null; + + LSVariable v1 = null; + + LSVariable v2 = null; + + + vars = new ArrayList(); + try { + // Lua.g:82:2: (v1= var ( ',' v2= var )* ) + // Lua.g:82:4: v1= var ( ',' v2= var )* + { + pushFollow(FOLLOW_var_in_varlist1887); + v1=var(); + + state._fsp--; + if (state.failed) return vars; + if ( state.backtracking==0 ) { + vars.add(v1); + } + // Lua.g:83:3: ( ',' v2= var )* + loop14: + do { + int alt14=2; + int LA14_0 = input.LA(1); + + if ( (LA14_0==28) ) { + alt14=1; + } + + + switch (alt14) { + case 1 : + // Lua.g:83:4: ',' v2= var + { + match(input,28,FOLLOW_28_in_varlist1894); if (state.failed) return vars; + pushFollow(FOLLOW_var_in_varlist1898); + v2=var(); + + state._fsp--; + if (state.failed) return vars; + if ( state.backtracking==0 ) { + vars.add(v2); + } + + } + break; + + default : + break loop14; + } + } while (true); + + + } + + } + catch (RecognitionException re) { + reportError(re); + recover(input,re); + } + finally { + } + return vars; + } + // $ANTLR end "varlist1" + + + // $ANTLR start "namelist" + // Lua.g:86:1: namelist returns [List names] : n= NAME ( ',' n2= NAME )* ; + public final List namelist() throws RecognitionException { + List names = null; + + Token n=null; + Token n2=null; + + try { + // Lua.g:87:2: (n= NAME ( ',' n2= NAME )* ) + // Lua.g:87:4: n= NAME ( ',' n2= NAME )* + { + n=(Token)match(input,NAME,FOLLOW_NAME_in_namelist919); if (state.failed) return names; + if ( state.backtracking==0 ) { + names =new ArrayList(); names.add((n!=null?n.getText():null)); + } + // Lua.g:88:3: ( ',' n2= NAME )* + loop15: + do { + int alt15=2; + int LA15_0 = input.LA(1); + + if ( (LA15_0==28) ) { + int LA15_2 = input.LA(2); + + if ( (LA15_2==NAME) ) { + alt15=1; + } + + + } + + + switch (alt15) { + case 1 : + // Lua.g:88:4: ',' n2= NAME + { + match(input,28,FOLLOW_28_in_namelist927); if (state.failed) return names; + n2=(Token)match(input,NAME,FOLLOW_NAME_in_namelist931); if (state.failed) return names; + if ( state.backtracking==0 ) { + names.add((n2!=null?n2.getText():null)); + } + + } + break; + + default : + break loop15; + } + } while (true); + + + } + + } + catch (RecognitionException re) { + reportError(re); + recover(input,re); + } + finally { + } + return names; + } + // $ANTLR end "namelist" + + + // $ANTLR start "explist1" + // Lua.g:91:1: explist1 returns [List exprs] : (e1= exp ',' )* e2= exp ; + public final List explist1() throws RecognitionException { + List exprs = null; + + LSExpression e1 = null; + + LSExpression e2 = null; + + + exprs = new ArrayList(); + try { + // Lua.g:93:2: ( (e1= exp ',' )* e2= exp ) + // Lua.g:93:4: (e1= exp ',' )* e2= exp + { + // Lua.g:93:4: (e1= exp ',' )* + loop16: + do { + int alt16=2; + alt16 = dfa16.predict(input); + switch (alt16) { + case 1 : + // Lua.g:93:5: e1= exp ',' + { + pushFollow(FOLLOW_exp_in_explist1960); + e1=exp(); + + state._fsp--; + if (state.failed) return exprs; + match(input,28,FOLLOW_28_in_explist1962); if (state.failed) return exprs; + if ( state.backtracking==0 ) { + exprs.add(e1); + } + + } + break; + + default : + break loop16; + } + } while (true); + + pushFollow(FOLLOW_exp_in_explist1973); + e2=exp(); + + state._fsp--; + if (state.failed) return exprs; + if ( state.backtracking==0 ) { + exprs.add(e2); + } + + } + + } + catch (RecognitionException re) { + reportError(re); + recover(input,re); + } + finally { + } + return exprs; + } + // $ANTLR end "explist1" + + + // $ANTLR start "exp" + // Lua.g:97:1: exp returns [LSExpression e] : ( 'nil' | 'false' | 'true' | number | string | '...' | function | prefixexp | tableconstructor | unop e1= exp ) ( binop e2= exp )* ; + public final LSExpression exp() throws RecognitionException { + LSExpression e = null; + + LSExpression e1 = null; + + LSExpression e2 = null; + + LuaParser.number_return number24 = null; + + LSExpression string25 = null; + + LSFunction function26 = null; + + LSVariable prefixexp27 = null; + + LSExpression tableconstructor28 = null; + + UnOp unop29 = null; + + BinOp binop30 = null; + + + try { + // Lua.g:98:2: ( ( 'nil' | 'false' | 'true' | number | string | '...' | function | prefixexp | tableconstructor | unop e1= exp ) ( binop e2= exp )* ) + // Lua.g:98:4: ( 'nil' | 'false' | 'true' | number | string | '...' | function | prefixexp | tableconstructor | unop e1= exp ) ( binop e2= exp )* + { + // Lua.g:98:4: ( 'nil' | 'false' | 'true' | number | string | '...' | function | prefixexp | tableconstructor | unop e1= exp ) + int alt17=10; + alt17 = dfa17.predict(input); + switch (alt17) { + case 1 : + // Lua.g:98:5: 'nil' + { + match(input,40,FOLLOW_40_in_exp991); if (state.failed) return e; + if ( state.backtracking==0 ) { + e =LSExpression.ENIL; + } + + } + break; + case 2 : + // Lua.g:99:5: 'false' + { + match(input,41,FOLLOW_41_in_exp1013); if (state.failed) return e; + if ( state.backtracking==0 ) { + e =LSExpression.EFALSE; + } + + } + break; + case 3 : + // Lua.g:100:5: 'true' + { + match(input,42,FOLLOW_42_in_exp1030); if (state.failed) return e; + if ( state.backtracking==0 ) { + e =LSExpression.ETRUE; + } + + } + break; + case 4 : + // Lua.g:101:5: number + { + pushFollow(FOLLOW_number_in_exp1048); + number24=number(); + + state._fsp--; + if (state.failed) return e; + if ( state.backtracking==0 ) { + e =LSExpression.numberExpression((number24!=null?input.toString(number24.start,number24.stop):null)); + } + + } + break; + case 5 : + // Lua.g:102:5: string + { + pushFollow(FOLLOW_string_in_exp1066); + string25=string(); + + state._fsp--; + if (state.failed) return e; + if ( state.backtracking==0 ) { + e =string25; + } + + } + break; + case 6 : + // Lua.g:103:5: '...' + { + match(input,43,FOLLOW_43_in_exp1084); if (state.failed) return e; + if ( state.backtracking==0 ) { + e =LSExpression.varargsRef(); ((funcblock_scope)funcblock_stack.peek()).func.setUsesVarargs(); + } + + } + break; + case 7 : + // Lua.g:104:5: function + { + pushFollow(FOLLOW_function_in_exp1103); + function26=function(); + + state._fsp--; + if (state.failed) return e; + if ( state.backtracking==0 ) { + e =LSExpression.functionExpression(function26); + } + + } + break; + case 8 : + // Lua.g:105:5: prefixexp + { + pushFollow(FOLLOW_prefixexp_in_exp1119); + prefixexp27=prefixexp(); + + state._fsp--; + if (state.failed) return e; + if ( state.backtracking==0 ) { + e =prefixexp27; + } + + } + break; + case 9 : + // Lua.g:106:5: tableconstructor + { + pushFollow(FOLLOW_tableconstructor_in_exp1134); + tableconstructor28=tableconstructor(); + + state._fsp--; + if (state.failed) return e; + if ( state.backtracking==0 ) { + e =tableconstructor28; + } + + } + break; + case 10 : + // Lua.g:107:5: unop e1= exp + { + pushFollow(FOLLOW_unop_in_exp1142); + unop29=unop(); + + state._fsp--; + if (state.failed) return e; + pushFollow(FOLLOW_exp_in_exp1146); + e1=exp(); + + state._fsp--; + if (state.failed) return e; + if ( state.backtracking==0 ) { + e =LSExpression.unopExpression(unop29,e1,CHK.peekScope()); + } + + } + break; + + } + + // Lua.g:108:5: ( binop e2= exp )* + loop18: + do { + int alt18=2; + alt18 = dfa18.predict(input); + switch (alt18) { + case 1 : + // Lua.g:108:6: binop e2= exp + { + pushFollow(FOLLOW_binop_in_exp1160); + binop30=binop(); + + state._fsp--; + if (state.failed) return e; + pushFollow(FOLLOW_exp_in_exp1164); + e2=exp(); + + state._fsp--; + if (state.failed) return e; + if ( state.backtracking==0 ) { + e =LSExpression.binopExpression(e,binop30,e2,CHK.peekScope()); + } + + } + break; + + default : + break loop18; + } + } while (true); + + + } + + } + catch (RecognitionException re) { + reportError(re); + recover(input,re); + } + finally { + } + return e; + } + // $ANTLR end "exp" + + protected static class var_scope { + LSVariable current; + } + protected Stack var_stack = new Stack(); + + + // $ANTLR start "var" + // Lua.g:111:1: var returns [LSVariable v] : (n= NAME | '(' exp ')' varSuffix ) ( varSuffix )* ; + public final LSVariable var() throws RecognitionException { + var_stack.push(new var_scope()); + LSVariable v = null; + + Token n=null; + LSExpression exp31 = null; + + + try { + // Lua.g:113:2: ( (n= NAME | '(' exp ')' varSuffix ) ( varSuffix )* ) + // Lua.g:113:4: (n= NAME | '(' exp ')' varSuffix ) ( varSuffix )* + { + // Lua.g:113:4: (n= NAME | '(' exp ')' varSuffix ) + int alt19=2; + int LA19_0 = input.LA(1); + + if ( (LA19_0==NAME) ) { + alt19=1; + } + else if ( (LA19_0==44) ) { + alt19=2; + } + else { + if (state.backtracking>0) {state.failed=true; return v;} + NoViableAltException nvae = + new NoViableAltException("", 19, 0, input); + + throw nvae; + } + switch (alt19) { + case 1 : + // Lua.g:113:5: n= NAME + { + n=(Token)match(input,NAME,FOLLOW_NAME_in_var1195); if (state.failed) return v; + if ( state.backtracking==0 ) { + ((var_scope)var_stack.peek()).current =LSVariable.nameVariable(CHK.reference((n!=null?n.getText():null),((funcblock_scope)funcblock_stack.peek()).func)); + } + + } + break; + case 2 : + // Lua.g:114:8: '(' exp ')' varSuffix + { + match(input,44,FOLLOW_44_in_var1207); if (state.failed) return v; + pushFollow(FOLLOW_exp_in_var1209); + exp31=exp(); + + state._fsp--; + if (state.failed) return v; + match(input,45,FOLLOW_45_in_var1211); if (state.failed) return v; + if ( state.backtracking==0 ) { + ((var_scope)var_stack.peek()).current =LSVariable.parenthesesVariable(exp31); + } + pushFollow(FOLLOW_varSuffix_in_var1215); + varSuffix(); + + state._fsp--; + if (state.failed) return v; + + } + break; + + } + + // Lua.g:115:7: ( varSuffix )* + loop20: + do { + int alt20=2; + alt20 = dfa20.predict(input); + switch (alt20) { + case 1 : + // Lua.g:0:0: varSuffix + { + pushFollow(FOLLOW_varSuffix_in_var1225); + varSuffix(); + + state._fsp--; + if (state.failed) return v; + + } + break; + + default : + break loop20; + } + } while (true); + + if ( state.backtracking==0 ) { + v =((var_scope)var_stack.peek()).current; + } + + } + + } + catch (RecognitionException re) { + reportError(re); + recover(input,re); + } + finally { + var_stack.pop(); + } + return v; + } + // $ANTLR end "var" + + + // $ANTLR start "varSuffix" + // Lua.g:119:1: varSuffix : (n= nameAndArgs[$var::current] )* ( '[' e= exp ']' | '.' n2= NAME ) ; + public final void varSuffix() throws RecognitionException { + Token n2=null; + LSVariable n = null; + + LSExpression e = null; + + + try { + // Lua.g:120:2: ( (n= nameAndArgs[$var::current] )* ( '[' e= exp ']' | '.' n2= NAME ) ) + // Lua.g:120:4: (n= nameAndArgs[$var::current] )* ( '[' e= exp ']' | '.' n2= NAME ) + { + // Lua.g:120:4: (n= nameAndArgs[$var::current] )* + loop21: + do { + int alt21=2; + int LA21_0 = input.LA(1); + + if ( ((LA21_0>=NORMALSTRING && LA21_0<=LONGSTRING)||LA21_0==39||LA21_0==44||LA21_0==48) ) { + alt21=1; + } + + + switch (alt21) { + case 1 : + // Lua.g:120:5: n= nameAndArgs[$var::current] + { + pushFollow(FOLLOW_nameAndArgs_in_varSuffix1245); + n=nameAndArgs(((var_scope)var_stack.peek()).current); + + state._fsp--; + if (state.failed) return ; + if ( state.backtracking==0 ) { + ((var_scope)var_stack.peek()).current =n; + } + + } + break; + + default : + break loop21; + } + } while (true); + + // Lua.g:121:3: ( '[' e= exp ']' | '.' n2= NAME ) + int alt22=2; + int LA22_0 = input.LA(1); + + if ( (LA22_0==46) ) { + alt22=1; + } + else if ( (LA22_0==38) ) { + alt22=2; + } + else { + if (state.backtracking>0) {state.failed=true; return ;} + NoViableAltException nvae = + new NoViableAltException("", 22, 0, input); + + throw nvae; + } + switch (alt22) { + case 1 : + // Lua.g:121:4: '[' e= exp ']' + { + match(input,46,FOLLOW_46_in_varSuffix1256); if (state.failed) return ; + pushFollow(FOLLOW_exp_in_varSuffix1260); + e=exp(); + + state._fsp--; + if (state.failed) return ; + match(input,47,FOLLOW_47_in_varSuffix1262); if (state.failed) return ; + if ( state.backtracking==0 ) { + ((var_scope)var_stack.peek()).current =((var_scope)var_stack.peek()).current.indexVariable(e); + } + + } + break; + case 2 : + // Lua.g:122:5: '.' n2= NAME + { + match(input,38,FOLLOW_38_in_varSuffix1275); if (state.failed) return ; + n2=(Token)match(input,NAME,FOLLOW_NAME_in_varSuffix1279); if (state.failed) return ; + if ( state.backtracking==0 ) { + ((var_scope)var_stack.peek()).current =((var_scope)var_stack.peek()).current.fieldVariable((n2!=null?n2.getText():null)); + } + + } + break; + + } + + + } + + } + catch (RecognitionException re) { + reportError(re); + recover(input,re); + } + finally { + } + return ; + } + // $ANTLR end "varSuffix" + + protected static class prefixexp_scope { + LSVariable current; + } + protected Stack prefixexp_stack = new Stack(); + + + // $ANTLR start "prefixexp" + // Lua.g:126:1: prefixexp returns [LSVariable v] : e= varOrExp (n= nameAndArgs[$prefixexp::current] )* ; + public final LSVariable prefixexp() throws RecognitionException { + prefixexp_stack.push(new prefixexp_scope()); + LSVariable v = null; + + LSVariable e = null; + + LSVariable n = null; + + + try { + // Lua.g:128:2: (e= varOrExp (n= nameAndArgs[$prefixexp::current] )* ) + // Lua.g:128:4: e= varOrExp (n= nameAndArgs[$prefixexp::current] )* + { + pushFollow(FOLLOW_varOrExp_in_prefixexp1311); + e=varOrExp(); + + state._fsp--; + if (state.failed) return v; + if ( state.backtracking==0 ) { + ((prefixexp_scope)prefixexp_stack.peek()).current =e; + } + // Lua.g:129:3: (n= nameAndArgs[$prefixexp::current] )* + loop23: + do { + int alt23=2; + alt23 = dfa23.predict(input); + switch (alt23) { + case 1 : + // Lua.g:129:4: n= nameAndArgs[$prefixexp::current] + { + pushFollow(FOLLOW_nameAndArgs_in_prefixexp1321); + n=nameAndArgs(((prefixexp_scope)prefixexp_stack.peek()).current); + + state._fsp--; + if (state.failed) return v; + if ( state.backtracking==0 ) { + ((prefixexp_scope)prefixexp_stack.peek()).current =n; + } + + } + break; + + default : + break loop23; + } + } while (true); + + if ( state.backtracking==0 ) { + v =((prefixexp_scope)prefixexp_stack.peek()).current; + } + + } + + } + catch (RecognitionException re) { + reportError(re); + recover(input,re); + } + finally { + prefixexp_stack.pop(); + } + return v; + } + // $ANTLR end "prefixexp" + + protected static class functioncall_scope { + LSVariable current; + } + protected Stack functioncall_stack = new Stack(); + + + // $ANTLR start "functioncall" + // Lua.g:133:1: functioncall returns [LSVariable v] : e= varOrExp (n= nameAndArgs[$functioncall::current] )+ ; + public final LSVariable functioncall() throws RecognitionException { + functioncall_stack.push(new functioncall_scope()); + LSVariable v = null; + + LSVariable e = null; + + LSVariable n = null; + + + try { + // Lua.g:135:2: (e= varOrExp (n= nameAndArgs[$functioncall::current] )+ ) + // Lua.g:135:4: e= varOrExp (n= nameAndArgs[$functioncall::current] )+ + { + pushFollow(FOLLOW_varOrExp_in_functioncall1352); + e=varOrExp(); + + state._fsp--; + if (state.failed) return v; + if ( state.backtracking==0 ) { + ((functioncall_scope)functioncall_stack.peek()).current =e; + } + // Lua.g:136:3: (n= nameAndArgs[$functioncall::current] )+ + int cnt24=0; + loop24: + do { + int alt24=2; + alt24 = dfa24.predict(input); + switch (alt24) { + case 1 : + // Lua.g:136:4: n= nameAndArgs[$functioncall::current] + { + pushFollow(FOLLOW_nameAndArgs_in_functioncall1362); + n=nameAndArgs(((functioncall_scope)functioncall_stack.peek()).current); + + state._fsp--; + if (state.failed) return v; + if ( state.backtracking==0 ) { + ((functioncall_scope)functioncall_stack.peek()).current =n; + } + + } + break; + + default : + if ( cnt24 >= 1 ) break loop24; + if (state.backtracking>0) {state.failed=true; return v;} + EarlyExitException eee = + new EarlyExitException(24, input); + throw eee; + } + cnt24++; + } while (true); + + if ( state.backtracking==0 ) { + v =((functioncall_scope)functioncall_stack.peek()).current; + } + + } + + } + catch (RecognitionException re) { + reportError(re); + recover(input,re); + } + finally { + functioncall_stack.pop(); + } + return v; + } + // $ANTLR end "functioncall" + + + // $ANTLR start "varOrExp" + // Lua.g:140:1: varOrExp returns [LSVariable v] : ( var | '(' exp ')' ); + public final LSVariable varOrExp() throws RecognitionException { + LSVariable v = null; + + LSVariable var32 = null; + + LSExpression exp33 = null; + + + try { + // Lua.g:141:2: ( var | '(' exp ')' ) + int alt25=2; + int LA25_0 = input.LA(1); + + if ( (LA25_0==NAME) ) { + alt25=1; + } + else if ( (LA25_0==44) ) { + int LA25_2 = input.LA(2); + + if ( (synpred42_Lua()) ) { + alt25=1; + } + else if ( (true) ) { + alt25=2; + } + else { + if (state.backtracking>0) {state.failed=true; return v;} + NoViableAltException nvae = + new NoViableAltException("", 25, 2, input); + + throw nvae; + } + } + else { + if (state.backtracking>0) {state.failed=true; return v;} + NoViableAltException nvae = + new NoViableAltException("", 25, 0, input); + + throw nvae; + } + switch (alt25) { + case 1 : + // Lua.g:141:4: var + { + pushFollow(FOLLOW_var_in_varOrExp1386); + var32=var(); + + state._fsp--; + if (state.failed) return v; + if ( state.backtracking==0 ) { + v =var32; + } + + } + break; + case 2 : + // Lua.g:142:4: '(' exp ')' + { + match(input,44,FOLLOW_44_in_varOrExp1401); if (state.failed) return v; + pushFollow(FOLLOW_exp_in_varOrExp1403); + exp33=exp(); + + state._fsp--; + if (state.failed) return v; + match(input,45,FOLLOW_45_in_varOrExp1405); if (state.failed) return v; + if ( state.backtracking==0 ) { + v =LSVariable.parenthesesVariable(exp33); + } + + } + break; + + } + } + catch (RecognitionException re) { + reportError(re); + recover(input,re); + } + finally { + } + return v; + } + // $ANTLR end "varOrExp" + + + // $ANTLR start "nameAndArgs" + // Lua.g:145:1: nameAndArgs[LSVariable vin] returns [LSVariable v] : ( ':' n= NAME )? a= args ; + public final LSVariable nameAndArgs(LSVariable vin) throws RecognitionException { + LSVariable v = null; + + Token n=null; + List a = null; + + + String method=null; + try { + // Lua.g:147:2: ( ( ':' n= NAME )? a= args ) + // Lua.g:147:4: ( ':' n= NAME )? a= args + { + // Lua.g:147:4: ( ':' n= NAME )? + int alt26=2; + int LA26_0 = input.LA(1); + + if ( (LA26_0==39) ) { + alt26=1; + } + switch (alt26) { + case 1 : + // Lua.g:147:5: ':' n= NAME + { + match(input,39,FOLLOW_39_in_nameAndArgs1431); if (state.failed) return v; + n=(Token)match(input,NAME,FOLLOW_NAME_in_nameAndArgs1435); if (state.failed) return v; + if ( state.backtracking==0 ) { + method=(n!=null?n.getText():null); + } + + } + break; + + } + + pushFollow(FOLLOW_args_in_nameAndArgs1452); + a=args(); + + state._fsp--; + if (state.failed) return v; + if ( state.backtracking==0 ) { + v =((method==null)? + vin.callFuncVariable(a): + vin.callMethVariable(method,a)); + } + + } + + } + catch (RecognitionException re) { + reportError(re); + recover(input,re); + } + finally { + } + return v; + } + // $ANTLR end "nameAndArgs" + + + // $ANTLR start "args" + // Lua.g:153:1: args returns [List exprs] : ( '(' (e= explist1 )? ')' | t= tableconstructor | s= string ); + public final List args() throws RecognitionException { + List exprs = null; + + List e = null; + + LSExpression t = null; + + LSExpression s = null; + + + exprs = new ArrayList(); + try { + // Lua.g:155:2: ( '(' (e= explist1 )? ')' | t= tableconstructor | s= string ) + int alt28=3; + switch ( input.LA(1) ) { + case 44: + { + alt28=1; + } + break; + case 48: + { + alt28=2; + } + break; + case NORMALSTRING: + case CHARSTRING: + case LONGSTRING: + { + alt28=3; + } + break; + default: + if (state.backtracking>0) {state.failed=true; return exprs;} + NoViableAltException nvae = + new NoViableAltException("", 28, 0, input); + + throw nvae; + } + + switch (alt28) { + case 1 : + // Lua.g:155:5: '(' (e= explist1 )? ')' + { + match(input,44,FOLLOW_44_in_args1476); if (state.failed) return exprs; + // Lua.g:155:9: (e= explist1 )? + int alt27=2; + int LA27_0 = input.LA(1); + + if ( ((LA27_0>=NAME && LA27_0<=LONGSTRING)||LA27_0==30||(LA27_0>=40 && LA27_0<=44)||LA27_0==48||LA27_0==51||(LA27_0>=65 && LA27_0<=66)) ) { + alt27=1; + } + switch (alt27) { + case 1 : + // Lua.g:155:10: e= explist1 + { + pushFollow(FOLLOW_explist1_in_args1481); + e=explist1(); + + state._fsp--; + if (state.failed) return exprs; + if ( state.backtracking==0 ) { + exprs =e; + } + + } + break; + + } + + match(input,45,FOLLOW_45_in_args1487); if (state.failed) return exprs; + + } + break; + case 2 : + // Lua.g:156:4: t= tableconstructor + { + pushFollow(FOLLOW_tableconstructor_in_args1495); + t=tableconstructor(); + + state._fsp--; + if (state.failed) return exprs; + if ( state.backtracking==0 ) { + exprs.add(t); + } + + } + break; + case 3 : + // Lua.g:157:4: s= string + { + pushFollow(FOLLOW_string_in_args1505); + s=string(); + + state._fsp--; + if (state.failed) return exprs; + if ( state.backtracking==0 ) { + exprs.add(s); + } + + } + break; + + } + } + catch (RecognitionException re) { + reportError(re); + recover(input,re); + } + finally { + } + return exprs; + } + // $ANTLR end "args" + + + // $ANTLR start "function" + // Lua.g:160:1: function returns [LSFunction f] : 'function' b= funcbody ; + public final LSFunction function() throws RecognitionException { + LSFunction f = null; + + LSFunction b = null; + + + try { + // Lua.g:161:2: ( 'function' b= funcbody ) + // Lua.g:161:4: 'function' b= funcbody + { + match(input,30,FOLLOW_30_in_function1532); if (state.failed) return f; + pushFollow(FOLLOW_funcbody_in_function1536); + b=funcbody(); + + state._fsp--; + if (state.failed) return f; + if ( state.backtracking==0 ) { + f = b; + } + + } + + } + catch (RecognitionException re) { + reportError(re); + recover(input,re); + } + finally { + } + return f; + } + // $ANTLR end "function" + + + // $ANTLR start "funcbody" + // Lua.g:164:1: funcbody returns [LSFunction f] : '(' ( parlist1[f] )? ')' funcblock[f] 'end' ; + public final LSFunction funcbody() throws RecognitionException { + LSFunction f = null; + + + f = new LSFunction(); + ((funcblock_scope)funcblock_stack.peek()).func.functions.add(f); + + try { + // Lua.g:169:2: ( '(' ( parlist1[f] )? ')' funcblock[f] 'end' ) + // Lua.g:169:4: '(' ( parlist1[f] )? ')' funcblock[f] 'end' + { + if ( state.backtracking==0 ) { + CHK.pushScope("func",true); + } + match(input,44,FOLLOW_44_in_funcbody1561); if (state.failed) return f; + // Lua.g:169:38: ( parlist1[f] )? + int alt29=2; + int LA29_0 = input.LA(1); + + if ( (LA29_0==NAME||LA29_0==43) ) { + alt29=1; + } + switch (alt29) { + case 1 : + // Lua.g:169:39: parlist1[f] + { + pushFollow(FOLLOW_parlist1_in_funcbody1564); + parlist1(f); + + state._fsp--; + if (state.failed) return f; + + } + break; + + } + + match(input,45,FOLLOW_45_in_funcbody1570); if (state.failed) return f; + pushFollow(FOLLOW_funcblock_in_funcbody1572); + funcblock(f); + + state._fsp--; + if (state.failed) return f; + match(input,23,FOLLOW_23_in_funcbody1575); if (state.failed) return f; + if ( state.backtracking==0 ) { + CHK.popScope("func"); + } + + } + + } + catch (RecognitionException re) { + reportError(re); + recover(input,re); + } + finally { + } + return f; + } + // $ANTLR end "funcbody" + + + // $ANTLR start "parlist1" + // Lua.g:172:1: parlist1[LSFunction f] : ( namelist ( ',' '...' )? | '...' ); + public final void parlist1(LSFunction f) throws RecognitionException { + List namelist34 = null; + + + try { + // Lua.g:173:2: ( namelist ( ',' '...' )? | '...' ) + int alt31=2; + int LA31_0 = input.LA(1); + + if ( (LA31_0==NAME) ) { + alt31=1; + } + else if ( (LA31_0==43) ) { + alt31=2; + } + else { + if (state.backtracking>0) {state.failed=true; return ;} + NoViableAltException nvae = + new NoViableAltException("", 31, 0, input); + + throw nvae; + } + switch (alt31) { + case 1 : + // Lua.g:173:4: namelist ( ',' '...' )? + { + pushFollow(FOLLOW_namelist_in_parlist11591); + namelist34=namelist(); + + state._fsp--; + if (state.failed) return ; + if ( state.backtracking==0 ) { + f.setParameterNames(CHK.declare(namelist34)); + } + // Lua.g:173:66: ( ',' '...' )? + int alt30=2; + int LA30_0 = input.LA(1); + + if ( (LA30_0==28) ) { + alt30=1; + } + switch (alt30) { + case 1 : + // Lua.g:173:67: ',' '...' + { + match(input,28,FOLLOW_28_in_parlist11596); if (state.failed) return ; + match(input,43,FOLLOW_43_in_parlist11598); if (state.failed) return ; + if ( state.backtracking==0 ) { + f.isvararg=true; + } + + } + break; + + } + + + } + break; + case 2 : + // Lua.g:174:4: '...' + { + match(input,43,FOLLOW_43_in_parlist11608); if (state.failed) return ; + if ( state.backtracking==0 ) { + f.isvararg=true; + } + + } + break; + + } + } + catch (RecognitionException re) { + reportError(re); + recover(input,re); + } + finally { + } + return ; + } + // $ANTLR end "parlist1" + + + // $ANTLR start "tableconstructor" + // Lua.g:177:1: tableconstructor returns [LSExpression e] : '{' ( fieldlist[fields] )? '}' ; + public final LSExpression tableconstructor() throws RecognitionException { + LSExpression e = null; + + List fields = new ArrayList(); + try { + // Lua.g:179:2: ( '{' ( fieldlist[fields] )? '}' ) + // Lua.g:179:4: '{' ( fieldlist[fields] )? '}' + { + match(input,48,FOLLOW_48_in_tableconstructor1631); if (state.failed) return e; + // Lua.g:179:8: ( fieldlist[fields] )? + int alt32=2; + int LA32_0 = input.LA(1); + + if ( ((LA32_0>=NAME && LA32_0<=LONGSTRING)||LA32_0==30||(LA32_0>=40 && LA32_0<=44)||LA32_0==46||LA32_0==48||LA32_0==51||(LA32_0>=65 && LA32_0<=66)) ) { + alt32=1; + } + switch (alt32) { + case 1 : + // Lua.g:179:9: fieldlist[fields] + { + pushFollow(FOLLOW_fieldlist_in_tableconstructor1634); + fieldlist(fields); + + state._fsp--; + if (state.failed) return e; + + } + break; + + } + + match(input,49,FOLLOW_49_in_tableconstructor1639); if (state.failed) return e; + if ( state.backtracking==0 ) { + e =LSExpression.tableConstructorExpression(fields); + } + + } + + } + catch (RecognitionException re) { + reportError(re); + recover(input,re); + } + finally { + } + return e; + } + // $ANTLR end "tableconstructor" + + + // $ANTLR start "fieldlist" + // Lua.g:182:1: fieldlist[List fields] : field[fields] ( fieldsep field[fields] )* ( fieldsep )? ; + public final void fieldlist(List fields) throws RecognitionException { + try { + // Lua.g:183:2: ( field[fields] ( fieldsep field[fields] )* ( fieldsep )? ) + // Lua.g:183:4: field[fields] ( fieldsep field[fields] )* ( fieldsep )? + { + pushFollow(FOLLOW_field_in_fieldlist1654); + field(fields); + + state._fsp--; + if (state.failed) return ; + // Lua.g:183:19: ( fieldsep field[fields] )* + loop33: + do { + int alt33=2; + int LA33_0 = input.LA(1); + + if ( (LA33_0==20||LA33_0==28) ) { + int LA33_1 = input.LA(2); + + if ( ((LA33_1>=NAME && LA33_1<=LONGSTRING)||LA33_1==30||(LA33_1>=40 && LA33_1<=44)||LA33_1==46||LA33_1==48||LA33_1==51||(LA33_1>=65 && LA33_1<=66)) ) { + alt33=1; + } + + + } + + + switch (alt33) { + case 1 : + // Lua.g:183:20: fieldsep field[fields] + { + pushFollow(FOLLOW_fieldsep_in_fieldlist1659); + fieldsep(); + + state._fsp--; + if (state.failed) return ; + pushFollow(FOLLOW_field_in_fieldlist1661); + field(fields); + + state._fsp--; + if (state.failed) return ; + + } + break; + + default : + break loop33; + } + } while (true); + + // Lua.g:183:46: ( fieldsep )? + int alt34=2; + int LA34_0 = input.LA(1); + + if ( (LA34_0==20||LA34_0==28) ) { + alt34=1; + } + switch (alt34) { + case 1 : + // Lua.g:183:47: fieldsep + { + pushFollow(FOLLOW_fieldsep_in_fieldlist1668); + fieldsep(); + + state._fsp--; + if (state.failed) return ; + + } + break; + + } + + + } + + } + catch (RecognitionException re) { + reportError(re); + recover(input,re); + } + finally { + } + return ; + } + // $ANTLR end "fieldlist" + + + // $ANTLR start "field" + // Lua.g:186:1: field[List fields] : ( '[' k= exp ']' '=' e= exp | n= NAME '=' e= exp | e= exp ); + public final void field(List fields) throws RecognitionException { + Token n=null; + LSExpression k = null; + + LSExpression e = null; + + + try { + // Lua.g:187:2: ( '[' k= exp ']' '=' e= exp | n= NAME '=' e= exp | e= exp ) + int alt35=3; + switch ( input.LA(1) ) { + case 46: + { + alt35=1; + } + break; + case NAME: + { + int LA35_2 = input.LA(2); + + if ( (LA35_2==21) ) { + alt35=2; + } + else if ( (LA35_2==EOF||(LA35_2>=NORMALSTRING && LA35_2<=LONGSTRING)||LA35_2==20||LA35_2==28||(LA35_2>=38 && LA35_2<=39)||LA35_2==44||LA35_2==46||(LA35_2>=48 && LA35_2<=64)) ) { + alt35=3; + } + else { + if (state.backtracking>0) {state.failed=true; return ;} + NoViableAltException nvae = + new NoViableAltException("", 35, 2, input); + + throw nvae; + } + } + break; + case INT: + case FLOAT1: + case FLOAT2: + case FLOAT3: + case EXP: + case HEX: + case NORMALSTRING: + case CHARSTRING: + case LONGSTRING: + case 30: + case 40: + case 41: + case 42: + case 43: + case 44: + case 48: + case 51: + case 65: + case 66: + { + alt35=3; + } + break; + default: + if (state.backtracking>0) {state.failed=true; return ;} + NoViableAltException nvae = + new NoViableAltException("", 35, 0, input); + + throw nvae; + } + + switch (alt35) { + case 1 : + // Lua.g:187:4: '[' k= exp ']' '=' e= exp + { + match(input,46,FOLLOW_46_in_field1683); if (state.failed) return ; + pushFollow(FOLLOW_exp_in_field1687); + k=exp(); + + state._fsp--; + if (state.failed) return ; + match(input,47,FOLLOW_47_in_field1689); if (state.failed) return ; + match(input,21,FOLLOW_21_in_field1691); if (state.failed) return ; + pushFollow(FOLLOW_exp_in_field1695); + e=exp(); + + state._fsp--; + if (state.failed) return ; + if ( state.backtracking==0 ) { + fields.add(LSField.keyValueField(k,e)); + } + + } + break; + case 2 : + // Lua.g:188:4: n= NAME '=' e= exp + { + n=(Token)match(input,NAME,FOLLOW_NAME_in_field1706); if (state.failed) return ; + match(input,21,FOLLOW_21_in_field1708); if (state.failed) return ; + pushFollow(FOLLOW_exp_in_field1712); + e=exp(); + + state._fsp--; + if (state.failed) return ; + if ( state.backtracking==0 ) { + fields.add(LSField.nameValueField((n!=null?n.getText():null),e)); + } + + } + break; + case 3 : + // Lua.g:189:4: e= exp + { + pushFollow(FOLLOW_exp_in_field1728); + e=exp(); + + state._fsp--; + if (state.failed) return ; + if ( state.backtracking==0 ) { + fields.add(LSField.valueField(e)); + } + + } + break; + + } + } + catch (RecognitionException re) { + reportError(re); + recover(input,re); + } + finally { + } + return ; + } + // $ANTLR end "field" + + + // $ANTLR start "fieldsep" + // Lua.g:192:1: fieldsep : ( ',' | ';' ); + public final void fieldsep() throws RecognitionException { + try { + // Lua.g:193:2: ( ',' | ';' ) + // Lua.g: + { + if ( input.LA(1)==20||input.LA(1)==28 ) { + input.consume(); + state.errorRecovery=false;state.failed=false; + } + else { + if (state.backtracking>0) {state.failed=true; return ;} + MismatchedSetException mse = new MismatchedSetException(null,input); + throw mse; + } + + + } + + } + catch (RecognitionException re) { + reportError(re); + recover(input,re); + } + finally { + } + return ; + } + // $ANTLR end "fieldsep" + + + // $ANTLR start "binop" + // Lua.g:197:1: binop returns [BinOp op] : ( '+' | '-' | '*' | '/' | '^' | '%' | '..' | '<' | '<=' | '>' | '>=' | '==' | '~=' | 'and' | 'or' ); + public final BinOp binop() throws RecognitionException { + BinOp op = null; + + try { + // Lua.g:198:2: ( '+' | '-' | '*' | '/' | '^' | '%' | '..' | '<' | '<=' | '>' | '>=' | '==' | '~=' | 'and' | 'or' ) + int alt36=15; + switch ( input.LA(1) ) { + case 50: + { + alt36=1; + } + break; + case 51: + { + alt36=2; + } + break; + case 52: + { + alt36=3; + } + break; + case 53: + { + alt36=4; + } + break; + case 54: + { + alt36=5; + } + break; + case 55: + { + alt36=6; + } + break; + case 56: + { + alt36=7; + } + break; + case 57: + { + alt36=8; + } + break; + case 58: + { + alt36=9; + } + break; + case 59: + { + alt36=10; + } + break; + case 60: + { + alt36=11; + } + break; + case 61: + { + alt36=12; + } + break; + case 62: + { + alt36=13; + } + break; + case 63: + { + alt36=14; + } + break; + case 64: + { + alt36=15; + } + break; + default: + if (state.backtracking>0) {state.failed=true; return op;} + NoViableAltException nvae = + new NoViableAltException("", 36, 0, input); + + throw nvae; + } + + switch (alt36) { + case 1 : + // Lua.g:198:4: '+' + { + match(input,50,FOLLOW_50_in_binop1781); if (state.failed) return op; + if ( state.backtracking==0 ) { + op =BinOp.ADD; + } + + } + break; + case 2 : + // Lua.g:199:4: '-' + { + match(input,51,FOLLOW_51_in_binop1790); if (state.failed) return op; + if ( state.backtracking==0 ) { + op =BinOp.SUB; + } + + } + break; + case 3 : + // Lua.g:200:4: '*' + { + match(input,52,FOLLOW_52_in_binop1800); if (state.failed) return op; + if ( state.backtracking==0 ) { + op =BinOp.MUL; + } + + } + break; + case 4 : + // Lua.g:201:4: '/' + { + match(input,53,FOLLOW_53_in_binop1810); if (state.failed) return op; + if ( state.backtracking==0 ) { + op =BinOp.DIV; + } + + } + break; + case 5 : + // Lua.g:202:4: '^' + { + match(input,54,FOLLOW_54_in_binop1820); if (state.failed) return op; + if ( state.backtracking==0 ) { + op =BinOp.POW; + } + + } + break; + case 6 : + // Lua.g:203:4: '%' + { + match(input,55,FOLLOW_55_in_binop1830); if (state.failed) return op; + if ( state.backtracking==0 ) { + op =BinOp.MOD; + } + + } + break; + case 7 : + // Lua.g:204:4: '..' + { + match(input,56,FOLLOW_56_in_binop1840); if (state.failed) return op; + if ( state.backtracking==0 ) { + op =BinOp.CONCAT; + } + + } + break; + case 8 : + // Lua.g:205:4: '<' + { + match(input,57,FOLLOW_57_in_binop1849); if (state.failed) return op; + if ( state.backtracking==0 ) { + op =BinOp.LT; + } + + } + break; + case 9 : + // Lua.g:206:4: '<=' + { + match(input,58,FOLLOW_58_in_binop1858); if (state.failed) return op; + if ( state.backtracking==0 ) { + op =BinOp.LTEQ; + } + + } + break; + case 10 : + // Lua.g:207:4: '>' + { + match(input,59,FOLLOW_59_in_binop1867); if (state.failed) return op; + if ( state.backtracking==0 ) { + op =BinOp.GT; + } + + } + break; + case 11 : + // Lua.g:208:4: '>=' + { + match(input,60,FOLLOW_60_in_binop1876); if (state.failed) return op; + if ( state.backtracking==0 ) { + op =BinOp.GTEQ; + } + + } + break; + case 12 : + // Lua.g:209:4: '==' + { + match(input,61,FOLLOW_61_in_binop1885); if (state.failed) return op; + if ( state.backtracking==0 ) { + op =BinOp.EQ; + } + + } + break; + case 13 : + // Lua.g:210:4: '~=' + { + match(input,62,FOLLOW_62_in_binop1894); if (state.failed) return op; + if ( state.backtracking==0 ) { + op =BinOp.NEQ; + } + + } + break; + case 14 : + // Lua.g:211:4: 'and' + { + match(input,63,FOLLOW_63_in_binop1903); if (state.failed) return op; + if ( state.backtracking==0 ) { + op =BinOp.AND; ((funcblock_scope)funcblock_stack.peek()).func.hasandlogic=true; + } + + } + break; + case 15 : + // Lua.g:212:4: 'or' + { + match(input,64,FOLLOW_64_in_binop1911); if (state.failed) return op; + if ( state.backtracking==0 ) { + op =BinOp.OR; ((funcblock_scope)funcblock_stack.peek()).func.hasorlogic=true; + } + + } + break; + + } + } + catch (RecognitionException re) { + reportError(re); + recover(input,re); + } + finally { + } + return op; + } + // $ANTLR end "binop" + + + // $ANTLR start "unop" + // Lua.g:215:1: unop returns [UnOp op] : ( '-' | 'not' | '#' ); + public final UnOp unop() throws RecognitionException { + UnOp op = null; + + try { + // Lua.g:216:2: ( '-' | 'not' | '#' ) + int alt37=3; + switch ( input.LA(1) ) { + case 51: + { + alt37=1; + } + break; + case 65: + { + alt37=2; + } + break; + case 66: + { + alt37=3; + } + break; + default: + if (state.backtracking>0) {state.failed=true; return op;} + NoViableAltException nvae = + new NoViableAltException("", 37, 0, input); + + throw nvae; + } + + switch (alt37) { + case 1 : + // Lua.g:216:4: '-' + { + match(input,51,FOLLOW_51_in_unop1930); if (state.failed) return op; + if ( state.backtracking==0 ) { + op =UnOp.NEG; + } + + } + break; + case 2 : + // Lua.g:217:4: 'not' + { + match(input,65,FOLLOW_65_in_unop1940); if (state.failed) return op; + if ( state.backtracking==0 ) { + op =UnOp.NOT; + } + + } + break; + case 3 : + // Lua.g:218:4: '#' + { + match(input,66,FOLLOW_66_in_unop1949); if (state.failed) return op; + if ( state.backtracking==0 ) { + op =UnOp.LEN; + } + + } + break; + + } + } + catch (RecognitionException re) { + reportError(re); + recover(input,re); + } + finally { + } + return op; + } + // $ANTLR end "unop" + + public static class number_return extends ParserRuleReturnScope { + }; + + // $ANTLR start "number" + // Lua.g:221:1: number : ( ( '-' )? INT | ( '-' )? FLOAT1 | ( '-' )? FLOAT2 | ( '-' )? FLOAT3 | ( '-' )? EXP | HEX ); + public final LuaParser.number_return number() throws RecognitionException { + LuaParser.number_return retval = new LuaParser.number_return(); + retval.start = input.LT(1); + + try { + // Lua.g:222:2: ( ( '-' )? INT | ( '-' )? FLOAT1 | ( '-' )? FLOAT2 | ( '-' )? FLOAT3 | ( '-' )? EXP | HEX ) + int alt43=6; + switch ( input.LA(1) ) { + case 51: + { + switch ( input.LA(2) ) { + case EXP: + { + alt43=5; + } + break; + case FLOAT1: + { + alt43=2; + } + break; + case FLOAT3: + { + alt43=4; + } + break; + case FLOAT2: + { + alt43=3; + } + break; + case INT: + { + alt43=1; + } + break; + default: + if (state.backtracking>0) {state.failed=true; return retval;} + NoViableAltException nvae = + new NoViableAltException("", 43, 1, input); + + throw nvae; + } + + } + break; + case INT: + { + alt43=1; + } + break; + case FLOAT1: + { + alt43=2; + } + break; + case FLOAT2: + { + alt43=3; + } + break; + case FLOAT3: + { + alt43=4; + } + break; + case EXP: + { + alt43=5; + } + break; + case HEX: + { + alt43=6; + } + break; + default: + if (state.backtracking>0) {state.failed=true; return retval;} + NoViableAltException nvae = + new NoViableAltException("", 43, 0, input); + + throw nvae; + } + + switch (alt43) { + case 1 : + // Lua.g:222:4: ( '-' )? INT + { + // Lua.g:222:4: ( '-' )? + int alt38=2; + int LA38_0 = input.LA(1); + + if ( (LA38_0==51) ) { + alt38=1; + } + switch (alt38) { + case 1 : + // Lua.g:222:5: '-' + { + match(input,51,FOLLOW_51_in_number1967); if (state.failed) return retval; + + } + break; + + } + + match(input,INT,FOLLOW_INT_in_number1971); if (state.failed) return retval; + + } + break; + case 2 : + // Lua.g:223:4: ( '-' )? FLOAT1 + { + // Lua.g:223:4: ( '-' )? + int alt39=2; + int LA39_0 = input.LA(1); + + if ( (LA39_0==51) ) { + alt39=1; + } + switch (alt39) { + case 1 : + // Lua.g:223:5: '-' + { + match(input,51,FOLLOW_51_in_number1978); if (state.failed) return retval; + + } + break; + + } + + match(input,FLOAT1,FOLLOW_FLOAT1_in_number1982); if (state.failed) return retval; + + } + break; + case 3 : + // Lua.g:224:4: ( '-' )? FLOAT2 + { + // Lua.g:224:4: ( '-' )? + int alt40=2; + int LA40_0 = input.LA(1); + + if ( (LA40_0==51) ) { + alt40=1; + } + switch (alt40) { + case 1 : + // Lua.g:224:5: '-' + { + match(input,51,FOLLOW_51_in_number1989); if (state.failed) return retval; + + } + break; + + } + + match(input,FLOAT2,FOLLOW_FLOAT2_in_number1993); if (state.failed) return retval; + + } + break; + case 4 : + // Lua.g:225:4: ( '-' )? FLOAT3 + { + // Lua.g:225:4: ( '-' )? + int alt41=2; + int LA41_0 = input.LA(1); + + if ( (LA41_0==51) ) { + alt41=1; + } + switch (alt41) { + case 1 : + // Lua.g:225:5: '-' + { + match(input,51,FOLLOW_51_in_number2000); if (state.failed) return retval; + + } + break; + + } + + match(input,FLOAT3,FOLLOW_FLOAT3_in_number2004); if (state.failed) return retval; + + } + break; + case 5 : + // Lua.g:226:4: ( '-' )? EXP + { + // Lua.g:226:4: ( '-' )? + int alt42=2; + int LA42_0 = input.LA(1); + + if ( (LA42_0==51) ) { + alt42=1; + } + switch (alt42) { + case 1 : + // Lua.g:226:5: '-' + { + match(input,51,FOLLOW_51_in_number2011); if (state.failed) return retval; + + } + break; + + } + + match(input,EXP,FOLLOW_EXP_in_number2015); if (state.failed) return retval; + + } + break; + case 6 : + // Lua.g:227:4: HEX + { + match(input,HEX,FOLLOW_HEX_in_number2021); if (state.failed) return retval; + + } + break; + + } + retval.stop = input.LT(-1); + + } + catch (RecognitionException re) { + reportError(re); + recover(input,re); + } + finally { + } + return retval; + } + // $ANTLR end "number" + + + // $ANTLR start "string" + // Lua.g:230:1: string returns [LSExpression e] : ( NORMALSTRING | CHARSTRING | LONGSTRING ); + public final LSExpression string() throws RecognitionException { + LSExpression e = null; + + Token NORMALSTRING35=null; + Token CHARSTRING36=null; + Token LONGSTRING37=null; + + try { + // Lua.g:231:2: ( NORMALSTRING | CHARSTRING | LONGSTRING ) + int alt44=3; + switch ( input.LA(1) ) { + case NORMALSTRING: + { + alt44=1; + } + break; + case CHARSTRING: + { + alt44=2; + } + break; + case LONGSTRING: + { + alt44=3; + } + break; + default: + if (state.backtracking>0) {state.failed=true; return e;} + NoViableAltException nvae = + new NoViableAltException("", 44, 0, input); + + throw nvae; + } + + switch (alt44) { + case 1 : + // Lua.g:231:4: NORMALSTRING + { + NORMALSTRING35=(Token)match(input,NORMALSTRING,FOLLOW_NORMALSTRING_in_string2037); if (state.failed) return e; + if ( state.backtracking==0 ) { + e =LSExpression.normalStringExpression((NORMALSTRING35!=null?NORMALSTRING35.getText():null)); + } + + } + break; + case 2 : + // Lua.g:232:4: CHARSTRING + { + CHARSTRING36=(Token)match(input,CHARSTRING,FOLLOW_CHARSTRING_in_string2044); if (state.failed) return e; + if ( state.backtracking==0 ) { + e =LSExpression.charStringExpression((CHARSTRING36!=null?CHARSTRING36.getText():null)); + } + + } + break; + case 3 : + // Lua.g:233:4: LONGSTRING + { + LONGSTRING37=(Token)match(input,LONGSTRING,FOLLOW_LONGSTRING_in_string2053); if (state.failed) return e; + if ( state.backtracking==0 ) { + e =LSExpression.longStringExpression((LONGSTRING37!=null?LONGSTRING37.getText():null)); + } + + } + break; + + } + } + catch (RecognitionException re) { + reportError(re); + recover(input,re); + } + finally { + } + return e; + } + // $ANTLR end "string" + + // $ANTLR start synpred5_Lua + public final void synpred5_Lua_fragment() throws RecognitionException { + // Lua.g:41:4: ( varlist1 '=' explist1 ) + // Lua.g:41:4: varlist1 '=' explist1 + { + pushFollow(FOLLOW_varlist1_in_synpred5_Lua159); + varlist1(); + + state._fsp--; + if (state.failed) return ; + match(input,21,FOLLOW_21_in_synpred5_Lua161); if (state.failed) return ; + pushFollow(FOLLOW_explist1_in_synpred5_Lua163); + explist1(); + + state._fsp--; + if (state.failed) return ; + + } + } + // $ANTLR end synpred5_Lua + + // $ANTLR start synpred6_Lua + public final void synpred6_Lua_fragment() throws RecognitionException { + // Lua.g:42:4: ( functioncall ) + // Lua.g:42:4: functioncall + { + pushFollow(FOLLOW_functioncall_in_synpred6_Lua183); + functioncall(); + + state._fsp--; + if (state.failed) return ; + + } + } + // $ANTLR end synpred6_Lua + + // $ANTLR start synpred12_Lua + public final void synpred12_Lua_fragment() throws RecognitionException { + LSExpression e1 = null; + + LSExpression e2 = null; + + LSExpression e3 = null; + + + // Lua.g:50:4: ( 'for' NAME '=' e1= exp ',' e2= exp ( ',' e3= exp )? 'do' block 'end' ) + // Lua.g:50:4: 'for' NAME '=' e1= exp ',' e2= exp ( ',' e3= exp )? 'do' block 'end' + { + match(input,27,FOLLOW_27_in_synpred12_Lua445); if (state.failed) return ; + match(input,NAME,FOLLOW_NAME_in_synpred12_Lua449); if (state.failed) return ; + match(input,21,FOLLOW_21_in_synpred12_Lua453); if (state.failed) return ; + pushFollow(FOLLOW_exp_in_synpred12_Lua457); + e1=exp(); + + state._fsp--; + if (state.failed) return ; + match(input,28,FOLLOW_28_in_synpred12_Lua459); if (state.failed) return ; + pushFollow(FOLLOW_exp_in_synpred12_Lua463); + e2=exp(); + + state._fsp--; + if (state.failed) return ; + // Lua.g:50:94: ( ',' e3= exp )? + int alt47=2; + int LA47_0 = input.LA(1); + + if ( (LA47_0==28) ) { + alt47=1; + } + switch (alt47) { + case 1 : + // Lua.g:50:95: ',' e3= exp + { + match(input,28,FOLLOW_28_in_synpred12_Lua466); if (state.failed) return ; + pushFollow(FOLLOW_exp_in_synpred12_Lua470); + e3=exp(); + + state._fsp--; + if (state.failed) return ; + + } + break; + + } + + match(input,22,FOLLOW_22_in_synpred12_Lua474); if (state.failed) return ; + pushFollow(FOLLOW_block_in_synpred12_Lua478); + block(); + + state._fsp--; + if (state.failed) return ; + match(input,23,FOLLOW_23_in_synpred12_Lua482); if (state.failed) return ; + + } + } + // $ANTLR end synpred12_Lua + + // $ANTLR start synpred13_Lua + public final void synpred13_Lua_fragment() throws RecognitionException { + // Lua.g:52:4: ( 'for' namelist 'in' explist1 'do' block 'end' ) + // Lua.g:52:4: 'for' namelist 'in' explist1 'do' block 'end' + { + match(input,27,FOLLOW_27_in_synpred13_Lua528); if (state.failed) return ; + pushFollow(FOLLOW_namelist_in_synpred13_Lua533); + namelist(); + + state._fsp--; + if (state.failed) return ; + match(input,29,FOLLOW_29_in_synpred13_Lua537); if (state.failed) return ; + pushFollow(FOLLOW_explist1_in_synpred13_Lua539); + explist1(); + + state._fsp--; + if (state.failed) return ; + match(input,22,FOLLOW_22_in_synpred13_Lua541); if (state.failed) return ; + pushFollow(FOLLOW_block_in_synpred13_Lua545); + block(); + + state._fsp--; + if (state.failed) return ; + match(input,23,FOLLOW_23_in_synpred13_Lua548); if (state.failed) return ; + + } + } + // $ANTLR end synpred13_Lua + + // $ANTLR start synpred15_Lua + public final void synpred15_Lua_fragment() throws RecognitionException { + // Lua.g:55:4: ( 'local' 'function' NAME funcbody ) + // Lua.g:55:4: 'local' 'function' NAME funcbody + { + match(input,31,FOLLOW_31_in_synpred15_Lua610); if (state.failed) return ; + match(input,30,FOLLOW_30_in_synpred15_Lua612); if (state.failed) return ; + match(input,NAME,FOLLOW_NAME_in_synpred15_Lua614); if (state.failed) return ; + pushFollow(FOLLOW_funcbody_in_synpred15_Lua618); + funcbody(); + + state._fsp--; + if (state.failed) return ; + + } + } + // $ANTLR end synpred15_Lua + + // $ANTLR start synpred25_Lua + public final void synpred25_Lua_fragment() throws RecognitionException { + LSExpression e1 = null; + + + // Lua.g:93:5: (e1= exp ',' ) + // Lua.g:93:5: e1= exp ',' + { + pushFollow(FOLLOW_exp_in_synpred25_Lua960); + e1=exp(); + + state._fsp--; + if (state.failed) return ; + match(input,28,FOLLOW_28_in_synpred25_Lua962); if (state.failed) return ; + + } + } + // $ANTLR end synpred25_Lua + + // $ANTLR start synpred29_Lua + public final void synpred29_Lua_fragment() throws RecognitionException { + // Lua.g:101:5: ( number ) + // Lua.g:101:5: number + { + pushFollow(FOLLOW_number_in_synpred29_Lua1048); + number(); + + state._fsp--; + if (state.failed) return ; + + } + } + // $ANTLR end synpred29_Lua + + // $ANTLR start synpred35_Lua + public final void synpred35_Lua_fragment() throws RecognitionException { + LSExpression e2 = null; + + + // Lua.g:108:6: ( binop e2= exp ) + // Lua.g:108:6: binop e2= exp + { + pushFollow(FOLLOW_binop_in_synpred35_Lua1160); + binop(); + + state._fsp--; + if (state.failed) return ; + pushFollow(FOLLOW_exp_in_synpred35_Lua1164); + e2=exp(); + + state._fsp--; + if (state.failed) return ; + + } + } + // $ANTLR end synpred35_Lua + + // $ANTLR start synpred37_Lua + public final void synpred37_Lua_fragment() throws RecognitionException { + // Lua.g:115:7: ( varSuffix ) + // Lua.g:115:7: varSuffix + { + pushFollow(FOLLOW_varSuffix_in_synpred37_Lua1225); + varSuffix(); + + state._fsp--; + if (state.failed) return ; + + } + } + // $ANTLR end synpred37_Lua + + // $ANTLR start synpred40_Lua + public final void synpred40_Lua_fragment() throws RecognitionException { + LSVariable n = null; + + + // Lua.g:129:4: (n= nameAndArgs[$prefixexp::current] ) + // Lua.g:129:4: n= nameAndArgs[$prefixexp::current] + { + pushFollow(FOLLOW_nameAndArgs_in_synpred40_Lua1321); + n=nameAndArgs(((prefixexp_scope)prefixexp_stack.peek()).current); + + state._fsp--; + if (state.failed) return ; + + } + } + // $ANTLR end synpred40_Lua + + // $ANTLR start synpred41_Lua + public final void synpred41_Lua_fragment() throws RecognitionException { + LSVariable n = null; + + + // Lua.g:136:4: (n= nameAndArgs[$functioncall::current] ) + // Lua.g:136:4: n= nameAndArgs[$functioncall::current] + { + pushFollow(FOLLOW_nameAndArgs_in_synpred41_Lua1362); + n=nameAndArgs(((functioncall_scope)functioncall_stack.peek()).current); + + state._fsp--; + if (state.failed) return ; + + } + } + // $ANTLR end synpred41_Lua + + // $ANTLR start synpred42_Lua + public final void synpred42_Lua_fragment() throws RecognitionException { + // Lua.g:141:4: ( var ) + // Lua.g:141:4: var + { + pushFollow(FOLLOW_var_in_synpred42_Lua1386); + var(); + + state._fsp--; + if (state.failed) return ; + + } + } + // $ANTLR end synpred42_Lua + + // Delegated rules + + public final boolean synpred25_Lua() { + state.backtracking++; + int start = input.mark(); + try { + synpred25_Lua_fragment(); // can never throw exception + } catch (RecognitionException re) { + System.err.println("impossible: "+re); + } + boolean success = !state.failed; + input.rewind(start); + state.backtracking--; + state.failed=false; + return success; + } + public final boolean synpred41_Lua() { + state.backtracking++; + int start = input.mark(); + try { + synpred41_Lua_fragment(); // can never throw exception + } catch (RecognitionException re) { + System.err.println("impossible: "+re); + } + boolean success = !state.failed; + input.rewind(start); + state.backtracking--; + state.failed=false; + return success; + } + public final boolean synpred40_Lua() { + state.backtracking++; + int start = input.mark(); + try { + synpred40_Lua_fragment(); // can never throw exception + } catch (RecognitionException re) { + System.err.println("impossible: "+re); + } + boolean success = !state.failed; + input.rewind(start); + state.backtracking--; + state.failed=false; + return success; + } + public final boolean synpred42_Lua() { + state.backtracking++; + int start = input.mark(); + try { + synpred42_Lua_fragment(); // can never throw exception + } catch (RecognitionException re) { + System.err.println("impossible: "+re); + } + boolean success = !state.failed; + input.rewind(start); + state.backtracking--; + state.failed=false; + return success; + } + public final boolean synpred13_Lua() { + state.backtracking++; + int start = input.mark(); + try { + synpred13_Lua_fragment(); // can never throw exception + } catch (RecognitionException re) { + System.err.println("impossible: "+re); + } + boolean success = !state.failed; + input.rewind(start); + state.backtracking--; + state.failed=false; + return success; + } + public final boolean synpred35_Lua() { + state.backtracking++; + int start = input.mark(); + try { + synpred35_Lua_fragment(); // can never throw exception + } catch (RecognitionException re) { + System.err.println("impossible: "+re); + } + boolean success = !state.failed; + input.rewind(start); + state.backtracking--; + state.failed=false; + return success; + } + public final boolean synpred5_Lua() { + state.backtracking++; + int start = input.mark(); + try { + synpred5_Lua_fragment(); // can never throw exception + } catch (RecognitionException re) { + System.err.println("impossible: "+re); + } + boolean success = !state.failed; + input.rewind(start); + state.backtracking--; + state.failed=false; + return success; + } + public final boolean synpred29_Lua() { + state.backtracking++; + int start = input.mark(); + try { + synpred29_Lua_fragment(); // can never throw exception + } catch (RecognitionException re) { + System.err.println("impossible: "+re); + } + boolean success = !state.failed; + input.rewind(start); + state.backtracking--; + state.failed=false; + return success; + } + public final boolean synpred12_Lua() { + state.backtracking++; + int start = input.mark(); + try { + synpred12_Lua_fragment(); // can never throw exception + } catch (RecognitionException re) { + System.err.println("impossible: "+re); + } + boolean success = !state.failed; + input.rewind(start); + state.backtracking--; + state.failed=false; + return success; + } + public final boolean synpred15_Lua() { + state.backtracking++; + int start = input.mark(); + try { + synpred15_Lua_fragment(); // can never throw exception + } catch (RecognitionException re) { + System.err.println("impossible: "+re); + } + boolean success = !state.failed; + input.rewind(start); + state.backtracking--; + state.failed=false; + return success; + } + public final boolean synpred6_Lua() { + state.backtracking++; + int start = input.mark(); + try { + synpred6_Lua_fragment(); // can never throw exception + } catch (RecognitionException re) { + System.err.println("impossible: "+re); + } + boolean success = !state.failed; + input.rewind(start); + state.backtracking--; + state.failed=false; + return success; + } + public final boolean synpred37_Lua() { + state.backtracking++; + int start = input.mark(); + try { + synpred37_Lua_fragment(); // can never throw exception + } catch (RecognitionException re) { + System.err.println("impossible: "+re); + } + boolean success = !state.failed; + input.rewind(start); + state.backtracking--; + state.failed=false; + return success; + } + + + protected DFA7 dfa7 = new DFA7(this); + protected DFA16 dfa16 = new DFA16(this); + protected DFA17 dfa17 = new DFA17(this); + protected DFA18 dfa18 = new DFA18(this); + protected DFA20 dfa20 = new DFA20(this); + protected DFA23 dfa23 = new DFA23(this); + protected DFA24 dfa24 = new DFA24(this); + static final String DFA7_eotS = + "\20\uffff"; + static final String DFA7_eofS = + "\20\uffff"; + static final String DFA7_minS = + "\1\4\2\0\4\uffff\1\0\1\uffff\1\0\6\uffff"; + static final String DFA7_maxS = + "\1\54\2\0\4\uffff\1\0\1\uffff\1\0\6\uffff"; + static final String DFA7_acceptS = + "\3\uffff\1\3\1\4\1\5\1\6\1\uffff\1\11\1\uffff\1\1\1\2\1\7\1\10"+ + "\1\12\1\13"; + static final String DFA7_specialS = + "\1\uffff\1\0\1\1\4\uffff\1\2\1\uffff\1\3\6\uffff}>"; + static final String[] DFA7_transitionS = { + "\1\1\21\uffff\1\3\1\uffff\1\4\1\5\1\uffff\1\7\2\uffff\1\10"+ + "\1\11\1\6\13\uffff\1\2", + "\1\uffff", + "\1\uffff", + "", + "", + "", + "", + "\1\uffff", + "", + "\1\uffff", + "", + "", + "", + "", + "", + "" + }; + + static final short[] DFA7_eot = DFA.unpackEncodedString(DFA7_eotS); + static final short[] DFA7_eof = DFA.unpackEncodedString(DFA7_eofS); + static final char[] DFA7_min = DFA.unpackEncodedStringToUnsignedChars(DFA7_minS); + static final char[] DFA7_max = DFA.unpackEncodedStringToUnsignedChars(DFA7_maxS); + static final short[] DFA7_accept = DFA.unpackEncodedString(DFA7_acceptS); + static final short[] DFA7_special = DFA.unpackEncodedString(DFA7_specialS); + static final short[][] DFA7_transition; + + static { + int numStates = DFA7_transitionS.length; + DFA7_transition = new short[numStates][]; + for (int i=0; i=0 ) return s; + break; + case 1 : + int LA16_2 = input.LA(1); + + + int index16_2 = input.index(); + input.rewind(); + s = -1; + if ( (synpred25_Lua()) ) {s = 21;} + + else if ( (true) ) {s = 22;} + + + input.seek(index16_2); + if ( s>=0 ) return s; + break; + case 2 : + int LA16_3 = input.LA(1); + + + int index16_3 = input.index(); + input.rewind(); + s = -1; + if ( (synpred25_Lua()) ) {s = 21;} + + else if ( (true) ) {s = 22;} + + + input.seek(index16_3); + if ( s>=0 ) return s; + break; + case 3 : + int LA16_4 = input.LA(1); + + + int index16_4 = input.index(); + input.rewind(); + s = -1; + if ( (synpred25_Lua()) ) {s = 21;} + + else if ( (true) ) {s = 22;} + + + input.seek(index16_4); + if ( s>=0 ) return s; + break; + case 4 : + int LA16_5 = input.LA(1); + + + int index16_5 = input.index(); + input.rewind(); + s = -1; + if ( (synpred25_Lua()) ) {s = 21;} + + else if ( (true) ) {s = 22;} + + + input.seek(index16_5); + if ( s>=0 ) return s; + break; + case 5 : + int LA16_6 = input.LA(1); + + + int index16_6 = input.index(); + input.rewind(); + s = -1; + if ( (synpred25_Lua()) ) {s = 21;} + + else if ( (true) ) {s = 22;} + + + input.seek(index16_6); + if ( s>=0 ) return s; + break; + case 6 : + int LA16_7 = input.LA(1); + + + int index16_7 = input.index(); + input.rewind(); + s = -1; + if ( (synpred25_Lua()) ) {s = 21;} + + else if ( (true) ) {s = 22;} + + + input.seek(index16_7); + if ( s>=0 ) return s; + break; + case 7 : + int LA16_8 = input.LA(1); + + + int index16_8 = input.index(); + input.rewind(); + s = -1; + if ( (synpred25_Lua()) ) {s = 21;} + + else if ( (true) ) {s = 22;} + + + input.seek(index16_8); + if ( s>=0 ) return s; + break; + case 8 : + int LA16_9 = input.LA(1); + + + int index16_9 = input.index(); + input.rewind(); + s = -1; + if ( (synpred25_Lua()) ) {s = 21;} + + else if ( (true) ) {s = 22;} + + + input.seek(index16_9); + if ( s>=0 ) return s; + break; + case 9 : + int LA16_10 = input.LA(1); + + + int index16_10 = input.index(); + input.rewind(); + s = -1; + if ( (synpred25_Lua()) ) {s = 21;} + + else if ( (true) ) {s = 22;} + + + input.seek(index16_10); + if ( s>=0 ) return s; + break; + case 10 : + int LA16_11 = input.LA(1); + + + int index16_11 = input.index(); + input.rewind(); + s = -1; + if ( (synpred25_Lua()) ) {s = 21;} + + else if ( (true) ) {s = 22;} + + + input.seek(index16_11); + if ( s>=0 ) return s; + break; + case 11 : + int LA16_12 = input.LA(1); + + + int index16_12 = input.index(); + input.rewind(); + s = -1; + if ( (synpred25_Lua()) ) {s = 21;} + + else if ( (true) ) {s = 22;} + + + input.seek(index16_12); + if ( s>=0 ) return s; + break; + case 12 : + int LA16_13 = input.LA(1); + + + int index16_13 = input.index(); + input.rewind(); + s = -1; + if ( (synpred25_Lua()) ) {s = 21;} + + else if ( (true) ) {s = 22;} + + + input.seek(index16_13); + if ( s>=0 ) return s; + break; + case 13 : + int LA16_14 = input.LA(1); + + + int index16_14 = input.index(); + input.rewind(); + s = -1; + if ( (synpred25_Lua()) ) {s = 21;} + + else if ( (true) ) {s = 22;} + + + input.seek(index16_14); + if ( s>=0 ) return s; + break; + case 14 : + int LA16_15 = input.LA(1); + + + int index16_15 = input.index(); + input.rewind(); + s = -1; + if ( (synpred25_Lua()) ) {s = 21;} + + else if ( (true) ) {s = 22;} + + + input.seek(index16_15); + if ( s>=0 ) return s; + break; + case 15 : + int LA16_16 = input.LA(1); + + + int index16_16 = input.index(); + input.rewind(); + s = -1; + if ( (synpred25_Lua()) ) {s = 21;} + + else if ( (true) ) {s = 22;} + + + input.seek(index16_16); + if ( s>=0 ) return s; + break; + case 16 : + int LA16_17 = input.LA(1); + + + int index16_17 = input.index(); + input.rewind(); + s = -1; + if ( (synpred25_Lua()) ) {s = 21;} + + else if ( (true) ) {s = 22;} + + + input.seek(index16_17); + if ( s>=0 ) return s; + break; + case 17 : + int LA16_18 = input.LA(1); + + + int index16_18 = input.index(); + input.rewind(); + s = -1; + if ( (synpred25_Lua()) ) {s = 21;} + + else if ( (true) ) {s = 22;} + + + input.seek(index16_18); + if ( s>=0 ) return s; + break; + case 18 : + int LA16_19 = input.LA(1); + + + int index16_19 = input.index(); + input.rewind(); + s = -1; + if ( (synpred25_Lua()) ) {s = 21;} + + else if ( (true) ) {s = 22;} + + + input.seek(index16_19); + if ( s>=0 ) return s; + break; + case 19 : + int LA16_20 = input.LA(1); + + + int index16_20 = input.index(); + input.rewind(); + s = -1; + if ( (synpred25_Lua()) ) {s = 21;} + + else if ( (true) ) {s = 22;} + + + input.seek(index16_20); + if ( s>=0 ) return s; + break; + } + if (state.backtracking>0) {state.failed=true; return -1;} + NoViableAltException nvae = + new NoViableAltException(getDescription(), 16, _s, input); + error(nvae); + throw nvae; + } + } + static final String DFA17_eotS = + "\14\uffff"; + static final String DFA17_eofS = + "\14\uffff"; + static final String DFA17_minS = + "\1\4\3\uffff\1\0\7\uffff"; + static final String DFA17_maxS = + "\1\102\3\uffff\1\0\7\uffff"; + static final String DFA17_acceptS = + "\1\uffff\1\1\1\2\1\3\1\uffff\1\4\1\5\1\6\1\7\1\10\1\11\1\12"; + static final String DFA17_specialS = + "\4\uffff\1\0\7\uffff}>"; + static final String[] DFA17_transitionS = { + "\1\11\6\5\3\6\20\uffff\1\10\11\uffff\1\1\1\2\1\3\1\7\1\11\3"+ + "\uffff\1\12\2\uffff\1\4\15\uffff\2\13", + "", + "", + "", + "\1\uffff", + "", + "", + "", + "", + "", + "", + "" + }; + + static final short[] DFA17_eot = DFA.unpackEncodedString(DFA17_eotS); + static final short[] DFA17_eof = DFA.unpackEncodedString(DFA17_eofS); + static final char[] DFA17_min = DFA.unpackEncodedStringToUnsignedChars(DFA17_minS); + static final char[] DFA17_max = DFA.unpackEncodedStringToUnsignedChars(DFA17_maxS); + static final short[] DFA17_accept = DFA.unpackEncodedString(DFA17_acceptS); + static final short[] DFA17_special = DFA.unpackEncodedString(DFA17_specialS); + static final short[][] DFA17_transition; + + static { + int numStates = DFA17_transitionS.length; + DFA17_transition = new short[numStates][]; + for (int i=0; i=0 ) return s; + break; + } + if (state.backtracking>0) {state.failed=true; return -1;} + NoViableAltException nvae = + new NoViableAltException(getDescription(), 17, _s, input); + error(nvae); + throw nvae; + } + } + static final String DFA18_eotS = + "\22\uffff"; + static final String DFA18_eofS = + "\1\1\21\uffff"; + static final String DFA18_minS = + "\1\4\1\uffff\17\0\1\uffff"; + static final String DFA18_maxS = + "\1\100\1\uffff\17\0\1\uffff"; + static final String DFA18_acceptS = + "\1\uffff\1\2\17\uffff\1\1"; + static final String DFA18_specialS = + "\2\uffff\1\7\1\2\1\15\1\10\1\4\1\5\1\12\1\16\1\0\1\13\1\6\1\3\1"+ + "\14\1\11\1\1\1\uffff}>"; + static final String[] DFA18_transitionS = { + "\1\1\17\uffff\1\1\1\uffff\7\1\1\uffff\10\1\6\uffff\2\1\1\uffff"+ + "\1\1\1\uffff\1\1\1\2\1\3\1\4\1\5\1\6\1\7\1\10\1\11\1\12\1\13"+ + "\1\14\1\15\1\16\1\17\1\20", + "", + "\1\uffff", + "\1\uffff", + "\1\uffff", + "\1\uffff", + "\1\uffff", + "\1\uffff", + "\1\uffff", + "\1\uffff", + "\1\uffff", + "\1\uffff", + "\1\uffff", + "\1\uffff", + "\1\uffff", + "\1\uffff", + "\1\uffff", + "" + }; + + static final short[] DFA18_eot = DFA.unpackEncodedString(DFA18_eotS); + static final short[] DFA18_eof = DFA.unpackEncodedString(DFA18_eofS); + static final char[] DFA18_min = DFA.unpackEncodedStringToUnsignedChars(DFA18_minS); + static final char[] DFA18_max = DFA.unpackEncodedStringToUnsignedChars(DFA18_maxS); + static final short[] DFA18_accept = DFA.unpackEncodedString(DFA18_acceptS); + static final short[] DFA18_special = DFA.unpackEncodedString(DFA18_specialS); + static final short[][] DFA18_transition; + + static { + int numStates = DFA18_transitionS.length; + DFA18_transition = new short[numStates][]; + for (int i=0; i=0 ) return s; + break; + case 1 : + int LA18_16 = input.LA(1); + + + int index18_16 = input.index(); + input.rewind(); + s = -1; + if ( (synpred35_Lua()) ) {s = 17;} + + else if ( (true) ) {s = 1;} + + + input.seek(index18_16); + if ( s>=0 ) return s; + break; + case 2 : + int LA18_3 = input.LA(1); + + + int index18_3 = input.index(); + input.rewind(); + s = -1; + if ( (synpred35_Lua()) ) {s = 17;} + + else if ( (true) ) {s = 1;} + + + input.seek(index18_3); + if ( s>=0 ) return s; + break; + case 3 : + int LA18_13 = input.LA(1); + + + int index18_13 = input.index(); + input.rewind(); + s = -1; + if ( (synpred35_Lua()) ) {s = 17;} + + else if ( (true) ) {s = 1;} + + + input.seek(index18_13); + if ( s>=0 ) return s; + break; + case 4 : + int LA18_6 = input.LA(1); + + + int index18_6 = input.index(); + input.rewind(); + s = -1; + if ( (synpred35_Lua()) ) {s = 17;} + + else if ( (true) ) {s = 1;} + + + input.seek(index18_6); + if ( s>=0 ) return s; + break; + case 5 : + int LA18_7 = input.LA(1); + + + int index18_7 = input.index(); + input.rewind(); + s = -1; + if ( (synpred35_Lua()) ) {s = 17;} + + else if ( (true) ) {s = 1;} + + + input.seek(index18_7); + if ( s>=0 ) return s; + break; + case 6 : + int LA18_12 = input.LA(1); + + + int index18_12 = input.index(); + input.rewind(); + s = -1; + if ( (synpred35_Lua()) ) {s = 17;} + + else if ( (true) ) {s = 1;} + + + input.seek(index18_12); + if ( s>=0 ) return s; + break; + case 7 : + int LA18_2 = input.LA(1); + + + int index18_2 = input.index(); + input.rewind(); + s = -1; + if ( (synpred35_Lua()) ) {s = 17;} + + else if ( (true) ) {s = 1;} + + + input.seek(index18_2); + if ( s>=0 ) return s; + break; + case 8 : + int LA18_5 = input.LA(1); + + + int index18_5 = input.index(); + input.rewind(); + s = -1; + if ( (synpred35_Lua()) ) {s = 17;} + + else if ( (true) ) {s = 1;} + + + input.seek(index18_5); + if ( s>=0 ) return s; + break; + case 9 : + int LA18_15 = input.LA(1); + + + int index18_15 = input.index(); + input.rewind(); + s = -1; + if ( (synpred35_Lua()) ) {s = 17;} + + else if ( (true) ) {s = 1;} + + + input.seek(index18_15); + if ( s>=0 ) return s; + break; + case 10 : + int LA18_8 = input.LA(1); + + + int index18_8 = input.index(); + input.rewind(); + s = -1; + if ( (synpred35_Lua()) ) {s = 17;} + + else if ( (true) ) {s = 1;} + + + input.seek(index18_8); + if ( s>=0 ) return s; + break; + case 11 : + int LA18_11 = input.LA(1); + + + int index18_11 = input.index(); + input.rewind(); + s = -1; + if ( (synpred35_Lua()) ) {s = 17;} + + else if ( (true) ) {s = 1;} + + + input.seek(index18_11); + if ( s>=0 ) return s; + break; + case 12 : + int LA18_14 = input.LA(1); + + + int index18_14 = input.index(); + input.rewind(); + s = -1; + if ( (synpred35_Lua()) ) {s = 17;} + + else if ( (true) ) {s = 1;} + + + input.seek(index18_14); + if ( s>=0 ) return s; + break; + case 13 : + int LA18_4 = input.LA(1); + + + int index18_4 = input.index(); + input.rewind(); + s = -1; + if ( (synpred35_Lua()) ) {s = 17;} + + else if ( (true) ) {s = 1;} + + + input.seek(index18_4); + if ( s>=0 ) return s; + break; + case 14 : + int LA18_9 = input.LA(1); + + + int index18_9 = input.index(); + input.rewind(); + s = -1; + if ( (synpred35_Lua()) ) {s = 17;} + + else if ( (true) ) {s = 1;} + + + input.seek(index18_9); + if ( s>=0 ) return s; + break; + } + if (state.backtracking>0) {state.failed=true; return -1;} + NoViableAltException nvae = + new NoViableAltException(getDescription(), 18, _s, input); + error(nvae); + throw nvae; + } + } + static final String DFA20_eotS = + "\56\uffff"; + static final String DFA20_eofS = + "\1\1\55\uffff"; + static final String DFA20_minS = + "\1\4\2\uffff\6\0\45\uffff"; + static final String DFA20_maxS = + "\1\100\2\uffff\6\0\45\uffff"; + static final String DFA20_acceptS = + "\1\uffff\1\2\52\uffff\1\1\1\uffff"; + static final String DFA20_specialS = + "\3\uffff\1\0\1\1\1\2\1\3\1\4\1\5\45\uffff}>"; + static final String[] DFA20_transitionS = { + "\1\1\6\uffff\1\6\1\7\1\10\6\uffff\11\1\1\uffff\10\1\1\54\1"+ + "\3\4\uffff\1\4\1\1\1\54\1\1\1\5\20\1", + "", + "", + "\1\uffff", + "\1\uffff", + "\1\uffff", + "\1\uffff", + "\1\uffff", + "\1\uffff", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "" + }; + + static final short[] DFA20_eot = DFA.unpackEncodedString(DFA20_eotS); + static final short[] DFA20_eof = DFA.unpackEncodedString(DFA20_eofS); + static final char[] DFA20_min = DFA.unpackEncodedStringToUnsignedChars(DFA20_minS); + static final char[] DFA20_max = DFA.unpackEncodedStringToUnsignedChars(DFA20_maxS); + static final short[] DFA20_accept = DFA.unpackEncodedString(DFA20_acceptS); + static final short[] DFA20_special = DFA.unpackEncodedString(DFA20_specialS); + static final short[][] DFA20_transition; + + static { + int numStates = DFA20_transitionS.length; + DFA20_transition = new short[numStates][]; + for (int i=0; i=0 ) return s; + break; + case 1 : + int LA20_4 = input.LA(1); + + + int index20_4 = input.index(); + input.rewind(); + s = -1; + if ( (synpred37_Lua()) ) {s = 44;} + + else if ( (true) ) {s = 1;} + + + input.seek(index20_4); + if ( s>=0 ) return s; + break; + case 2 : + int LA20_5 = input.LA(1); + + + int index20_5 = input.index(); + input.rewind(); + s = -1; + if ( (synpred37_Lua()) ) {s = 44;} + + else if ( (true) ) {s = 1;} + + + input.seek(index20_5); + if ( s>=0 ) return s; + break; + case 3 : + int LA20_6 = input.LA(1); + + + int index20_6 = input.index(); + input.rewind(); + s = -1; + if ( (synpred37_Lua()) ) {s = 44;} + + else if ( (true) ) {s = 1;} + + + input.seek(index20_6); + if ( s>=0 ) return s; + break; + case 4 : + int LA20_7 = input.LA(1); + + + int index20_7 = input.index(); + input.rewind(); + s = -1; + if ( (synpred37_Lua()) ) {s = 44;} + + else if ( (true) ) {s = 1;} + + + input.seek(index20_7); + if ( s>=0 ) return s; + break; + case 5 : + int LA20_8 = input.LA(1); + + + int index20_8 = input.index(); + input.rewind(); + s = -1; + if ( (synpred37_Lua()) ) {s = 44;} + + else if ( (true) ) {s = 1;} + + + input.seek(index20_8); + if ( s>=0 ) return s; + break; + } + if (state.backtracking>0) {state.failed=true; return -1;} + NoViableAltException nvae = + new NoViableAltException(getDescription(), 20, _s, input); + error(nvae); + throw nvae; + } + } + static final String DFA23_eotS = + "\53\uffff"; + static final String DFA23_eofS = + "\1\1\52\uffff"; + static final String DFA23_minS = + "\1\4\31\uffff\1\0\20\uffff"; + static final String DFA23_maxS = + "\1\100\31\uffff\1\0\20\uffff"; + static final String DFA23_acceptS = + "\1\uffff\1\2\44\uffff\1\1\4\uffff"; + static final String DFA23_specialS = + "\32\uffff\1\0\20\uffff}>"; + static final String[] DFA23_transitionS = { + "\1\1\6\uffff\3\46\6\uffff\1\1\1\uffff\7\1\1\uffff\10\1\1\uffff"+ + "\1\46\4\uffff\1\32\1\1\1\uffff\1\1\1\46\20\1", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "\1\uffff", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "" + }; + + static final short[] DFA23_eot = DFA.unpackEncodedString(DFA23_eotS); + static final short[] DFA23_eof = DFA.unpackEncodedString(DFA23_eofS); + static final char[] DFA23_min = DFA.unpackEncodedStringToUnsignedChars(DFA23_minS); + static final char[] DFA23_max = DFA.unpackEncodedStringToUnsignedChars(DFA23_maxS); + static final short[] DFA23_accept = DFA.unpackEncodedString(DFA23_acceptS); + static final short[] DFA23_special = DFA.unpackEncodedString(DFA23_specialS); + static final short[][] DFA23_transition; + + static { + int numStates = DFA23_transitionS.length; + DFA23_transition = new short[numStates][]; + for (int i=0; i=0 ) return s; + break; + } + if (state.backtracking>0) {state.failed=true; return -1;} + NoViableAltException nvae = + new NoViableAltException(getDescription(), 23, _s, input); + error(nvae); + throw nvae; + } + } + static final String DFA24_eotS = + "\27\uffff"; + static final String DFA24_eofS = + "\1\1\26\uffff"; + static final String DFA24_minS = + "\1\4\11\uffff\1\0\14\uffff"; + static final String DFA24_maxS = + "\1\60\11\uffff\1\0\14\uffff"; + static final String DFA24_acceptS = + "\1\uffff\1\2\20\uffff\1\1\4\uffff"; + static final String DFA24_specialS = + "\12\uffff\1\0\14\uffff}>"; + static final String[] DFA24_transitionS = { + "\1\1\6\uffff\3\22\6\uffff\1\1\1\uffff\6\1\2\uffff\3\1\1\uffff"+ + "\4\1\1\uffff\1\22\4\uffff\1\12\3\uffff\1\22", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "\1\uffff", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "" + }; + + static final short[] DFA24_eot = DFA.unpackEncodedString(DFA24_eotS); + static final short[] DFA24_eof = DFA.unpackEncodedString(DFA24_eofS); + static final char[] DFA24_min = DFA.unpackEncodedStringToUnsignedChars(DFA24_minS); + static final char[] DFA24_max = DFA.unpackEncodedStringToUnsignedChars(DFA24_maxS); + static final short[] DFA24_accept = DFA.unpackEncodedString(DFA24_acceptS); + static final short[] DFA24_special = DFA.unpackEncodedString(DFA24_specialS); + static final short[][] DFA24_transition; + + static { + int numStates = DFA24_transitionS.length; + DFA24_transition = new short[numStates][]; + for (int i=0; i=0 ) return s; + break; + } + if (state.backtracking>0) {state.failed=true; return -1;} + NoViableAltException nvae = + new NoViableAltException(getDescription(), 24, _s, input); + error(nvae); + throw nvae; + } + } + + + public static final BitSet FOLLOW_funcblock_in_chunk58 = new BitSet(new long[]{0x0000000000000002L}); + public static final BitSet FOLLOW_block_in_funcblock88 = new BitSet(new long[]{0x0000000000000002L}); + public static final BitSet FOLLOW_stat_in_block113 = new BitSet(new long[]{0x00001031CB500012L}); + public static final BitSet FOLLOW_20_in_block118 = new BitSet(new long[]{0x00001031CB500012L}); + public static final BitSet FOLLOW_laststat_in_block128 = new BitSet(new long[]{0x0000000000100002L}); + public static final BitSet FOLLOW_20_in_block133 = new BitSet(new long[]{0x0000000000000002L}); + public static final BitSet FOLLOW_varlist1_in_stat159 = new BitSet(new long[]{0x0000000000200000L}); + public static final BitSet FOLLOW_21_in_stat161 = new BitSet(new long[]{0x00091F0040003FF0L,0x0000000000000006L}); + public static final BitSet FOLLOW_explist1_in_stat163 = new BitSet(new long[]{0x0000000000000002L}); + public static final BitSet FOLLOW_functioncall_in_stat183 = new BitSet(new long[]{0x0000000000000002L}); + public static final BitSet FOLLOW_22_in_stat213 = new BitSet(new long[]{0x00001031CB500010L}); + public static final BitSet FOLLOW_block_in_stat217 = new BitSet(new long[]{0x0000000000800000L}); + public static final BitSet FOLLOW_23_in_stat221 = new BitSet(new long[]{0x0000000000000002L}); + public static final BitSet FOLLOW_24_in_stat286 = new BitSet(new long[]{0x00091F0040003FF0L,0x0000000000000006L}); + public static final BitSet FOLLOW_exp_in_stat288 = new BitSet(new long[]{0x0000000000400000L}); + public static final BitSet FOLLOW_22_in_stat290 = new BitSet(new long[]{0x00001031CB500010L}); + public static final BitSet FOLLOW_block_in_stat294 = new BitSet(new long[]{0x0000000000800000L}); + public static final BitSet FOLLOW_23_in_stat298 = new BitSet(new long[]{0x0000000000000002L}); + public static final BitSet FOLLOW_25_in_stat344 = new BitSet(new long[]{0x00001031CB500010L}); + public static final BitSet FOLLOW_block_in_stat349 = new BitSet(new long[]{0x0000000004000000L}); + public static final BitSet FOLLOW_26_in_stat354 = new BitSet(new long[]{0x00091F0040003FF0L,0x0000000000000006L}); + public static final BitSet FOLLOW_exp_in_stat356 = new BitSet(new long[]{0x0000000000000002L}); + public static final BitSet FOLLOW_ifstat_in_stat410 = new BitSet(new long[]{0x0000000000000002L}); + public static final BitSet FOLLOW_27_in_stat445 = new BitSet(new long[]{0x0000000000000010L}); + public static final BitSet FOLLOW_NAME_in_stat449 = new BitSet(new long[]{0x0000000000200000L}); + public static final BitSet FOLLOW_21_in_stat453 = new BitSet(new long[]{0x00091F0040003FF0L,0x0000000000000006L}); + public static final BitSet FOLLOW_exp_in_stat457 = new BitSet(new long[]{0x0000000010000000L}); + public static final BitSet FOLLOW_28_in_stat459 = new BitSet(new long[]{0x00091F0040003FF0L,0x0000000000000006L}); + public static final BitSet FOLLOW_exp_in_stat463 = new BitSet(new long[]{0x0000000010400000L}); + public static final BitSet FOLLOW_28_in_stat466 = new BitSet(new long[]{0x00091F0040003FF0L,0x0000000000000006L}); + public static final BitSet FOLLOW_exp_in_stat470 = new BitSet(new long[]{0x0000000000400000L}); + public static final BitSet FOLLOW_22_in_stat474 = new BitSet(new long[]{0x00001031CB500010L}); + public static final BitSet FOLLOW_block_in_stat478 = new BitSet(new long[]{0x0000000000800000L}); + public static final BitSet FOLLOW_23_in_stat482 = new BitSet(new long[]{0x0000000000000002L}); + public static final BitSet FOLLOW_27_in_stat528 = new BitSet(new long[]{0x0000000000000010L}); + public static final BitSet FOLLOW_namelist_in_stat533 = new BitSet(new long[]{0x0000000020000000L}); + public static final BitSet FOLLOW_29_in_stat537 = new BitSet(new long[]{0x00091F0040003FF0L,0x0000000000000006L}); + public static final BitSet FOLLOW_explist1_in_stat539 = new BitSet(new long[]{0x0000000000400000L}); + public static final BitSet FOLLOW_22_in_stat541 = new BitSet(new long[]{0x00001031CB500010L}); + public static final BitSet FOLLOW_block_in_stat545 = new BitSet(new long[]{0x0000000000800000L}); + public static final BitSet FOLLOW_23_in_stat548 = new BitSet(new long[]{0x0000000000000002L}); + public static final BitSet FOLLOW_30_in_stat593 = new BitSet(new long[]{0x0000000000000010L}); + public static final BitSet FOLLOW_funcname_in_stat595 = new BitSet(new long[]{0x0000100000000000L}); + public static final BitSet FOLLOW_funcbody_in_stat597 = new BitSet(new long[]{0x0000000000000002L}); + public static final BitSet FOLLOW_31_in_stat610 = new BitSet(new long[]{0x0000000040000000L}); + public static final BitSet FOLLOW_30_in_stat612 = new BitSet(new long[]{0x0000000000000010L}); + public static final BitSet FOLLOW_NAME_in_stat614 = new BitSet(new long[]{0x0000100000000000L}); + public static final BitSet FOLLOW_funcbody_in_stat618 = new BitSet(new long[]{0x0000000000000002L}); + public static final BitSet FOLLOW_31_in_stat666 = new BitSet(new long[]{0x0000000000000010L}); + public static final BitSet FOLLOW_namelist_in_stat668 = new BitSet(new long[]{0x0000000000200002L}); + public static final BitSet FOLLOW_21_in_stat671 = new BitSet(new long[]{0x00091F0040003FF0L,0x0000000000000006L}); + public static final BitSet FOLLOW_explist1_in_stat673 = new BitSet(new long[]{0x0000000000000002L}); + public static final BitSet FOLLOW_32_in_ifstat699 = new BitSet(new long[]{0x00091F0040003FF0L,0x0000000000000006L}); + public static final BitSet FOLLOW_exp_in_ifstat703 = new BitSet(new long[]{0x0000000200000000L}); + public static final BitSet FOLLOW_33_in_ifstat705 = new BitSet(new long[]{0x00001031CB500010L}); + public static final BitSet FOLLOW_block_in_ifstat712 = new BitSet(new long[]{0x0000000C00800000L}); + public static final BitSet FOLLOW_34_in_ifstat721 = new BitSet(new long[]{0x00091F0040003FF0L,0x0000000000000006L}); + public static final BitSet FOLLOW_exp_in_ifstat725 = new BitSet(new long[]{0x0000000200000000L}); + public static final BitSet FOLLOW_33_in_ifstat727 = new BitSet(new long[]{0x00001031CB500010L}); + public static final BitSet FOLLOW_block_in_ifstat734 = new BitSet(new long[]{0x0000000C00800000L}); + public static final BitSet FOLLOW_35_in_ifstat745 = new BitSet(new long[]{0x00001031CB500010L}); + public static final BitSet FOLLOW_block_in_ifstat752 = new BitSet(new long[]{0x0000000000800000L}); + public static final BitSet FOLLOW_23_in_ifstat762 = new BitSet(new long[]{0x0000000000000002L}); + public static final BitSet FOLLOW_36_in_laststat782 = new BitSet(new long[]{0x00091F0040003FF2L,0x0000000000000006L}); + public static final BitSet FOLLOW_explist1_in_laststat787 = new BitSet(new long[]{0x0000000000000002L}); + public static final BitSet FOLLOW_37_in_laststat797 = new BitSet(new long[]{0x0000000000000002L}); + public static final BitSet FOLLOW_NAME_in_funcname831 = new BitSet(new long[]{0x000000C000000002L}); + public static final BitSet FOLLOW_38_in_funcname839 = new BitSet(new long[]{0x0000000000000010L}); + public static final BitSet FOLLOW_NAME_in_funcname843 = new BitSet(new long[]{0x000000C000000002L}); + public static final BitSet FOLLOW_39_in_funcname854 = new BitSet(new long[]{0x0000000000000010L}); + public static final BitSet FOLLOW_NAME_in_funcname858 = new BitSet(new long[]{0x0000000000000002L}); + public static final BitSet FOLLOW_var_in_varlist1887 = new BitSet(new long[]{0x0000000010000002L}); + public static final BitSet FOLLOW_28_in_varlist1894 = new BitSet(new long[]{0x0000100000000010L}); + public static final BitSet FOLLOW_var_in_varlist1898 = new BitSet(new long[]{0x0000000010000002L}); + public static final BitSet FOLLOW_NAME_in_namelist919 = new BitSet(new long[]{0x0000000010000002L}); + public static final BitSet FOLLOW_28_in_namelist927 = new BitSet(new long[]{0x0000000000000010L}); + public static final BitSet FOLLOW_NAME_in_namelist931 = new BitSet(new long[]{0x0000000010000002L}); + public static final BitSet FOLLOW_exp_in_explist1960 = new BitSet(new long[]{0x0000000010000000L}); + public static final BitSet FOLLOW_28_in_explist1962 = new BitSet(new long[]{0x00091F0040003FF0L,0x0000000000000006L}); + public static final BitSet FOLLOW_exp_in_explist1973 = new BitSet(new long[]{0x0000000000000002L}); + public static final BitSet FOLLOW_40_in_exp991 = new BitSet(new long[]{0xFFFC000000000002L,0x0000000000000001L}); + public static final BitSet FOLLOW_41_in_exp1013 = new BitSet(new long[]{0xFFFC000000000002L,0x0000000000000001L}); + public static final BitSet FOLLOW_42_in_exp1030 = new BitSet(new long[]{0xFFFC000000000002L,0x0000000000000001L}); + public static final BitSet FOLLOW_number_in_exp1048 = new BitSet(new long[]{0xFFFC000000000002L,0x0000000000000001L}); + public static final BitSet FOLLOW_string_in_exp1066 = new BitSet(new long[]{0xFFFC000000000002L,0x0000000000000001L}); + public static final BitSet FOLLOW_43_in_exp1084 = new BitSet(new long[]{0xFFFC000000000002L,0x0000000000000001L}); + public static final BitSet FOLLOW_function_in_exp1103 = new BitSet(new long[]{0xFFFC000000000002L,0x0000000000000001L}); + public static final BitSet FOLLOW_prefixexp_in_exp1119 = new BitSet(new long[]{0xFFFC000000000002L,0x0000000000000001L}); + public static final BitSet FOLLOW_tableconstructor_in_exp1134 = new BitSet(new long[]{0xFFFC000000000002L,0x0000000000000001L}); + public static final BitSet FOLLOW_unop_in_exp1142 = new BitSet(new long[]{0x00091F0040003FF0L,0x0000000000000006L}); + public static final BitSet FOLLOW_exp_in_exp1146 = new BitSet(new long[]{0xFFFC000000000002L,0x0000000000000001L}); + public static final BitSet FOLLOW_binop_in_exp1160 = new BitSet(new long[]{0x00091F0040003FF0L,0x0000000000000006L}); + public static final BitSet FOLLOW_exp_in_exp1164 = new BitSet(new long[]{0xFFFC000000000002L,0x0000000000000001L}); + public static final BitSet FOLLOW_NAME_in_var1195 = new BitSet(new long[]{0x000150C000003802L}); + public static final BitSet FOLLOW_44_in_var1207 = new BitSet(new long[]{0x00091F0040003FF0L,0x0000000000000006L}); + public static final BitSet FOLLOW_exp_in_var1209 = new BitSet(new long[]{0x0000200000000000L}); + public static final BitSet FOLLOW_45_in_var1211 = new BitSet(new long[]{0x000150C000003800L}); + public static final BitSet FOLLOW_varSuffix_in_var1215 = new BitSet(new long[]{0x000150C000003802L}); + public static final BitSet FOLLOW_varSuffix_in_var1225 = new BitSet(new long[]{0x000150C000003802L}); + public static final BitSet FOLLOW_nameAndArgs_in_varSuffix1245 = new BitSet(new long[]{0x000150C000003800L}); + public static final BitSet FOLLOW_46_in_varSuffix1256 = new BitSet(new long[]{0x00091F0040003FF0L,0x0000000000000006L}); + public static final BitSet FOLLOW_exp_in_varSuffix1260 = new BitSet(new long[]{0x0000800000000000L}); + public static final BitSet FOLLOW_47_in_varSuffix1262 = new BitSet(new long[]{0x0000000000000002L}); + public static final BitSet FOLLOW_38_in_varSuffix1275 = new BitSet(new long[]{0x0000000000000010L}); + public static final BitSet FOLLOW_NAME_in_varSuffix1279 = new BitSet(new long[]{0x0000000000000002L}); + public static final BitSet FOLLOW_varOrExp_in_prefixexp1311 = new BitSet(new long[]{0x0001108000003802L}); + public static final BitSet FOLLOW_nameAndArgs_in_prefixexp1321 = new BitSet(new long[]{0x0001108000003802L}); + public static final BitSet FOLLOW_varOrExp_in_functioncall1352 = new BitSet(new long[]{0x0001108000003800L}); + public static final BitSet FOLLOW_nameAndArgs_in_functioncall1362 = new BitSet(new long[]{0x0001108000003802L}); + public static final BitSet FOLLOW_var_in_varOrExp1386 = new BitSet(new long[]{0x0000000000000002L}); + public static final BitSet FOLLOW_44_in_varOrExp1401 = new BitSet(new long[]{0x00091F0040003FF0L,0x0000000000000006L}); + public static final BitSet FOLLOW_exp_in_varOrExp1403 = new BitSet(new long[]{0x0000200000000000L}); + public static final BitSet FOLLOW_45_in_varOrExp1405 = new BitSet(new long[]{0x0000000000000002L}); + public static final BitSet FOLLOW_39_in_nameAndArgs1431 = new BitSet(new long[]{0x0000000000000010L}); + public static final BitSet FOLLOW_NAME_in_nameAndArgs1435 = new BitSet(new long[]{0x0001108000003800L}); + public static final BitSet FOLLOW_args_in_nameAndArgs1452 = new BitSet(new long[]{0x0000000000000002L}); + public static final BitSet FOLLOW_44_in_args1476 = new BitSet(new long[]{0x00093F0040003FF0L,0x0000000000000006L}); + public static final BitSet FOLLOW_explist1_in_args1481 = new BitSet(new long[]{0x0000200000000000L}); + public static final BitSet FOLLOW_45_in_args1487 = new BitSet(new long[]{0x0000000000000002L}); + public static final BitSet FOLLOW_tableconstructor_in_args1495 = new BitSet(new long[]{0x0000000000000002L}); + public static final BitSet FOLLOW_string_in_args1505 = new BitSet(new long[]{0x0000000000000002L}); + public static final BitSet FOLLOW_30_in_function1532 = new BitSet(new long[]{0x0000100000000000L}); + public static final BitSet FOLLOW_funcbody_in_function1536 = new BitSet(new long[]{0x0000000000000002L}); + public static final BitSet FOLLOW_44_in_funcbody1561 = new BitSet(new long[]{0x0000280000000010L}); + public static final BitSet FOLLOW_parlist1_in_funcbody1564 = new BitSet(new long[]{0x0000200000000000L}); + public static final BitSet FOLLOW_45_in_funcbody1570 = new BitSet(new long[]{0x00001031CB500010L}); + public static final BitSet FOLLOW_funcblock_in_funcbody1572 = new BitSet(new long[]{0x0000000000800000L}); + public static final BitSet FOLLOW_23_in_funcbody1575 = new BitSet(new long[]{0x0000000000000002L}); + public static final BitSet FOLLOW_namelist_in_parlist11591 = new BitSet(new long[]{0x0000000010000002L}); + public static final BitSet FOLLOW_28_in_parlist11596 = new BitSet(new long[]{0x0000080000000000L}); + public static final BitSet FOLLOW_43_in_parlist11598 = new BitSet(new long[]{0x0000000000000002L}); + public static final BitSet FOLLOW_43_in_parlist11608 = new BitSet(new long[]{0x0000000000000002L}); + public static final BitSet FOLLOW_48_in_tableconstructor1631 = new BitSet(new long[]{0x000B5F0040003FF0L,0x0000000000000006L}); + public static final BitSet FOLLOW_fieldlist_in_tableconstructor1634 = new BitSet(new long[]{0x0002000000000000L}); + public static final BitSet FOLLOW_49_in_tableconstructor1639 = new BitSet(new long[]{0x0000000000000002L}); + public static final BitSet FOLLOW_field_in_fieldlist1654 = new BitSet(new long[]{0x0000000010100002L}); + public static final BitSet FOLLOW_fieldsep_in_fieldlist1659 = new BitSet(new long[]{0x00095F0040003FF0L,0x0000000000000006L}); + public static final BitSet FOLLOW_field_in_fieldlist1661 = new BitSet(new long[]{0x0000000010100002L}); + public static final BitSet FOLLOW_fieldsep_in_fieldlist1668 = new BitSet(new long[]{0x0000000000000002L}); + public static final BitSet FOLLOW_46_in_field1683 = new BitSet(new long[]{0x00091F0040003FF0L,0x0000000000000006L}); + public static final BitSet FOLLOW_exp_in_field1687 = new BitSet(new long[]{0x0000800000000000L}); + public static final BitSet FOLLOW_47_in_field1689 = new BitSet(new long[]{0x0000000000200000L}); + public static final BitSet FOLLOW_21_in_field1691 = new BitSet(new long[]{0x00091F0040003FF0L,0x0000000000000006L}); + public static final BitSet FOLLOW_exp_in_field1695 = new BitSet(new long[]{0x0000000000000002L}); + public static final BitSet FOLLOW_NAME_in_field1706 = new BitSet(new long[]{0x0000000000200000L}); + public static final BitSet FOLLOW_21_in_field1708 = new BitSet(new long[]{0x00091F0040003FF0L,0x0000000000000006L}); + public static final BitSet FOLLOW_exp_in_field1712 = new BitSet(new long[]{0x0000000000000002L}); + public static final BitSet FOLLOW_exp_in_field1728 = new BitSet(new long[]{0x0000000000000002L}); + public static final BitSet FOLLOW_set_in_fieldsep0 = new BitSet(new long[]{0x0000000000000002L}); + public static final BitSet FOLLOW_50_in_binop1781 = new BitSet(new long[]{0x0000000000000002L}); + public static final BitSet FOLLOW_51_in_binop1790 = new BitSet(new long[]{0x0000000000000002L}); + public static final BitSet FOLLOW_52_in_binop1800 = new BitSet(new long[]{0x0000000000000002L}); + public static final BitSet FOLLOW_53_in_binop1810 = new BitSet(new long[]{0x0000000000000002L}); + public static final BitSet FOLLOW_54_in_binop1820 = new BitSet(new long[]{0x0000000000000002L}); + public static final BitSet FOLLOW_55_in_binop1830 = new BitSet(new long[]{0x0000000000000002L}); + public static final BitSet FOLLOW_56_in_binop1840 = new BitSet(new long[]{0x0000000000000002L}); + public static final BitSet FOLLOW_57_in_binop1849 = new BitSet(new long[]{0x0000000000000002L}); + public static final BitSet FOLLOW_58_in_binop1858 = new BitSet(new long[]{0x0000000000000002L}); + public static final BitSet FOLLOW_59_in_binop1867 = new BitSet(new long[]{0x0000000000000002L}); + public static final BitSet FOLLOW_60_in_binop1876 = new BitSet(new long[]{0x0000000000000002L}); + public static final BitSet FOLLOW_61_in_binop1885 = new BitSet(new long[]{0x0000000000000002L}); + public static final BitSet FOLLOW_62_in_binop1894 = new BitSet(new long[]{0x0000000000000002L}); + public static final BitSet FOLLOW_63_in_binop1903 = new BitSet(new long[]{0x0000000000000002L}); + public static final BitSet FOLLOW_64_in_binop1911 = new BitSet(new long[]{0x0000000000000002L}); + public static final BitSet FOLLOW_51_in_unop1930 = new BitSet(new long[]{0x0000000000000002L}); + public static final BitSet FOLLOW_65_in_unop1940 = new BitSet(new long[]{0x0000000000000002L}); + public static final BitSet FOLLOW_66_in_unop1949 = new BitSet(new long[]{0x0000000000000002L}); + public static final BitSet FOLLOW_51_in_number1967 = new BitSet(new long[]{0x0000000000000020L}); + public static final BitSet FOLLOW_INT_in_number1971 = new BitSet(new long[]{0x0000000000000002L}); + public static final BitSet FOLLOW_51_in_number1978 = new BitSet(new long[]{0x0000000000000040L}); + public static final BitSet FOLLOW_FLOAT1_in_number1982 = new BitSet(new long[]{0x0000000000000002L}); + public static final BitSet FOLLOW_51_in_number1989 = new BitSet(new long[]{0x0000000000000080L}); + public static final BitSet FOLLOW_FLOAT2_in_number1993 = new BitSet(new long[]{0x0000000000000002L}); + public static final BitSet FOLLOW_51_in_number2000 = new BitSet(new long[]{0x0000000000000100L}); + public static final BitSet FOLLOW_FLOAT3_in_number2004 = new BitSet(new long[]{0x0000000000000002L}); + public static final BitSet FOLLOW_51_in_number2011 = new BitSet(new long[]{0x0000000000000200L}); + public static final BitSet FOLLOW_EXP_in_number2015 = new BitSet(new long[]{0x0000000000000002L}); + public static final BitSet FOLLOW_HEX_in_number2021 = new BitSet(new long[]{0x0000000000000002L}); + public static final BitSet FOLLOW_NORMALSTRING_in_string2037 = new BitSet(new long[]{0x0000000000000002L}); + public static final BitSet FOLLOW_CHARSTRING_in_string2044 = new BitSet(new long[]{0x0000000000000002L}); + public static final BitSet FOLLOW_LONGSTRING_in_string2053 = new BitSet(new long[]{0x0000000000000002L}); + public static final BitSet FOLLOW_varlist1_in_synpred5_Lua159 = new BitSet(new long[]{0x0000000000200000L}); + public static final BitSet FOLLOW_21_in_synpred5_Lua161 = new BitSet(new long[]{0x00091F0040003FF0L,0x0000000000000006L}); + public static final BitSet FOLLOW_explist1_in_synpred5_Lua163 = new BitSet(new long[]{0x0000000000000002L}); + public static final BitSet FOLLOW_functioncall_in_synpred6_Lua183 = new BitSet(new long[]{0x0000000000000002L}); + public static final BitSet FOLLOW_27_in_synpred12_Lua445 = new BitSet(new long[]{0x0000000000000010L}); + public static final BitSet FOLLOW_NAME_in_synpred12_Lua449 = new BitSet(new long[]{0x0000000000200000L}); + public static final BitSet FOLLOW_21_in_synpred12_Lua453 = new BitSet(new long[]{0x00091F0040003FF0L,0x0000000000000006L}); + public static final BitSet FOLLOW_exp_in_synpred12_Lua457 = new BitSet(new long[]{0x0000000010000000L}); + public static final BitSet FOLLOW_28_in_synpred12_Lua459 = new BitSet(new long[]{0x00091F0040003FF0L,0x0000000000000006L}); + public static final BitSet FOLLOW_exp_in_synpred12_Lua463 = new BitSet(new long[]{0x0000000010400000L}); + public static final BitSet FOLLOW_28_in_synpred12_Lua466 = new BitSet(new long[]{0x00091F0040003FF0L,0x0000000000000006L}); + public static final BitSet FOLLOW_exp_in_synpred12_Lua470 = new BitSet(new long[]{0x0000000000400000L}); + public static final BitSet FOLLOW_22_in_synpred12_Lua474 = new BitSet(new long[]{0x00001031CB500010L}); + public static final BitSet FOLLOW_block_in_synpred12_Lua478 = new BitSet(new long[]{0x0000000000800000L}); + public static final BitSet FOLLOW_23_in_synpred12_Lua482 = new BitSet(new long[]{0x0000000000000002L}); + public static final BitSet FOLLOW_27_in_synpred13_Lua528 = new BitSet(new long[]{0x0000000000000010L}); + public static final BitSet FOLLOW_namelist_in_synpred13_Lua533 = new BitSet(new long[]{0x0000000020000000L}); + public static final BitSet FOLLOW_29_in_synpred13_Lua537 = new BitSet(new long[]{0x00091F0040003FF0L,0x0000000000000006L}); + public static final BitSet FOLLOW_explist1_in_synpred13_Lua539 = new BitSet(new long[]{0x0000000000400000L}); + public static final BitSet FOLLOW_22_in_synpred13_Lua541 = new BitSet(new long[]{0x00001031CB500010L}); + public static final BitSet FOLLOW_block_in_synpred13_Lua545 = new BitSet(new long[]{0x0000000000800000L}); + public static final BitSet FOLLOW_23_in_synpred13_Lua548 = new BitSet(new long[]{0x0000000000000002L}); + public static final BitSet FOLLOW_31_in_synpred15_Lua610 = new BitSet(new long[]{0x0000000040000000L}); + public static final BitSet FOLLOW_30_in_synpred15_Lua612 = new BitSet(new long[]{0x0000000000000010L}); + public static final BitSet FOLLOW_NAME_in_synpred15_Lua614 = new BitSet(new long[]{0x0000100000000000L}); + public static final BitSet FOLLOW_funcbody_in_synpred15_Lua618 = new BitSet(new long[]{0x0000000000000002L}); + public static final BitSet FOLLOW_exp_in_synpred25_Lua960 = new BitSet(new long[]{0x0000000010000000L}); + public static final BitSet FOLLOW_28_in_synpred25_Lua962 = new BitSet(new long[]{0x0000000000000002L}); + public static final BitSet FOLLOW_number_in_synpred29_Lua1048 = new BitSet(new long[]{0x0000000000000002L}); + public static final BitSet FOLLOW_binop_in_synpred35_Lua1160 = new BitSet(new long[]{0x00091F0040003FF0L,0x0000000000000006L}); + public static final BitSet FOLLOW_exp_in_synpred35_Lua1164 = new BitSet(new long[]{0x0000000000000002L}); + public static final BitSet FOLLOW_varSuffix_in_synpred37_Lua1225 = new BitSet(new long[]{0x0000000000000002L}); + public static final BitSet FOLLOW_nameAndArgs_in_synpred40_Lua1321 = new BitSet(new long[]{0x0000000000000002L}); + public static final BitSet FOLLOW_nameAndArgs_in_synpred41_Lua1362 = new BitSet(new long[]{0x0000000000000002L}); + public static final BitSet FOLLOW_var_in_synpred42_Lua1386 = new BitSet(new long[]{0x0000000000000002L}); + +} \ No newline at end of file diff --git a/src/jse/org/luaj/vm2/luajc/lst/BinOp.java b/src/jse/org/luaj/vm2/luajc/lst/BinOp.java new file mode 100644 index 00000000..56b059af --- /dev/null +++ b/src/jse/org/luaj/vm2/luajc/lst/BinOp.java @@ -0,0 +1,63 @@ +/******************************************************************************* +* Copyright (c) 2009 Luaj.org. All rights reserved. +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +* THE SOFTWARE. +******************************************************************************/ +package org.luaj.vm2.luajc.lst; + + +public class BinOp { + // unary precedence is between POW and MUL + public static final int UNARY_PRECEDENCE = 7; + + public enum Type { + pow,mul,div,mod,add,sub,concat,lt,lteq,gt,gteq,eq,neq,and,or, + } + + public static final BinOp POW = new BinOp(Type.pow, 8, true, "^"); + public static final BinOp MUL = new BinOp(Type.mul, 6, false, "*"); + public static final BinOp DIV = new BinOp(Type.div, 6, false, "/"); + public static final BinOp MOD = new BinOp(Type.mod, 6, false, "%"); + public static final BinOp ADD = new BinOp(Type.add, 5, false, "+"); + public static final BinOp SUB = new BinOp(Type.sub, 5, false, "-"); + public static final BinOp CONCAT = new BinOp(Type.concat, 4, true, ".."); + public static final BinOp LT = new BinOp(Type.lt, 3, false, "<"); + public static final BinOp LTEQ = new BinOp(Type.lteq, 3, false, "<="); + public static final BinOp GT = new BinOp(Type.gt, 3, false, ">"); + public static final BinOp GTEQ = new BinOp(Type.gteq, 3, false, ">="); + public static final BinOp EQ = new BinOp(Type.eq, 3, false, "=="); + public static final BinOp NEQ = new BinOp(Type.neq, 3, false, "~="); + public static final BinOp AND = new BinOp(Type.and, 2, true, "and"); + public static final BinOp OR = new BinOp(Type.or, 1, true, "or"); + + public final Type type; + public final int precedence; + public final boolean isrightassoc; + public final String luaop; + + private BinOp(Type type, int precedence, boolean isrightassoc, String luaop) { + super(); + this.type = type; + this.precedence = precedence; + this.isrightassoc = isrightassoc; + this.luaop = luaop; + } + + public String toString() { return luaop; } +} diff --git a/src/jse/org/luaj/vm2/luajc/lst/LSChunk.java b/src/jse/org/luaj/vm2/luajc/lst/LSChunk.java new file mode 100644 index 00000000..2924da0b --- /dev/null +++ b/src/jse/org/luaj/vm2/luajc/lst/LSChunk.java @@ -0,0 +1,106 @@ +/******************************************************************************* +* Copyright (c) 2009 Luaj.org. All rights reserved. +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +* THE SOFTWARE. +******************************************************************************/ +package org.luaj.vm2.luajc.lst; + +import java.util.ArrayList; +import java.util.List; + + +/* A Lua Source Chunk */ +public class LSChunk { + public static final boolean SCOPES = System.getProperty("SCOPES")!=null; + + public final String chunkname; + public final LSFunction function; + public Scope scope; + + public LSChunk( String chunkname ) { + this.chunkname = chunkname; + this.function = new LSFunction( true ); + this.scope = null; + } + + public String toString() { + return "@"+chunkname+": "+function; + } + + /** push a block scope onto the name scope stack */ + public void pushScope(String name) { + scope = new Scope(scope, false); + if(SCOPES)System.out.println(space(scope)+"push "+name+" scope="+scope); + + } + + /** push a function scope onto the name scope stack */ + public void pushScope(String name,boolean isFunction) { + scope = new Scope(scope, isFunction); + if(SCOPES)System.out.println(space(scope)+"push "+name+" scope="+scope); + } + + /** pop a scope from the scope stack */ + public Scope popScope(String name) { + Scope s = scope; + scope = scope.parent; + if(SCOPES)System.out.println(space(s)+"pop "+name+" scope="+scope); + return s; + } + + /** return the current scope */ + public Scope peekScope() { + return scope; + } + + /** Declare a single name in the current scope, and return the Name element for it */ + public Name declare(String name) { + Name n = scope.declare( name ); + if(SCOPES)System.out.println(space(scope)+" declared "+n+" scope="+scope); + return n; + } + + /** Declare a list of names in the current scope, and return List of Name for them */ + public List declare(List names) { + List results = new ArrayList(names.size()); + for ( String s : names ) + results.add( declare(s) ); + return results; + } + + /** Reference a name, and find either the local scope within the function, the upvalue, or a global name + * @param func */ + public Name reference(String name, LSFunction func) { + Name n = scope.reference(name); + if ("arg".equals(name) && n.isGlobal() && func.isvararg && !scope.isMainChunkScope()) { + n = scope.declare(name); + func.setHasArg(); + } + if(SCOPES)System.out.println(space(scope)+" reference "+n+" scope="+scope); + return n; + } + + /** Print out indentation for a scope */ + private static final String ws = " "; + private String space(Scope i) { + return ws.substring(0,i.level*2); + } + + +} diff --git a/src/jse/org/luaj/vm2/luajc/lst/LSExpression.java b/src/jse/org/luaj/vm2/luajc/lst/LSExpression.java new file mode 100644 index 00000000..038b4bfe --- /dev/null +++ b/src/jse/org/luaj/vm2/luajc/lst/LSExpression.java @@ -0,0 +1,267 @@ +/******************************************************************************* +* Copyright (c) 2009 Luaj.org. All rights reserved. +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +* THE SOFTWARE. +******************************************************************************/ +package org.luaj.vm2.luajc.lst; + +import java.io.ByteArrayOutputStream; +import java.util.List; + +public class LSExpression { + public enum Type { + unop, + binop, + functionExpr, + tableConstructor, + nilConstant, + trueConstant, + falseConstant, + varargsRef, + numberConstant, + stringConstant, + + // variable types + nameVariable, + fieldVariable, + methodVariable, + parenthesesVariable, + indexVariable, + callFunctionVariable, + callMethodVariable, + } + + public static final LSExpression ENIL = new LSExpression(Type.nilConstant); // nil + public static final LSExpression EFALSE = new LSExpression(Type.falseConstant); // false + public static final LSExpression ETRUE = new LSExpression(Type.trueConstant); // true + + public final Type type; + + LSExpression(Type type) { + this.type = type; + } + + public static LSExpression functionExpression(LSFunction function) { + return new FunctionExpr(function); + } + + public static LSExpression unopExpression(UnOp unop, LSExpression rhs, Scope scope) { + if ( rhs instanceof BinopExpr ) { + BinopExpr b = (BinopExpr) rhs; + if ( BinOp.UNARY_PRECEDENCE > b.op.precedence ) + return binopExpression( unopExpression( unop, b.lhs, scope ), b.op, b.rhs, scope ); + } + return new UnopExpr( unop, rhs ); + } + + public static LSExpression binopExpression(LSExpression lhs, BinOp binop, LSExpression rhs, Scope scope) { + if ( lhs instanceof UnopExpr ) { + UnopExpr u = (UnopExpr) lhs; + if ( binop.precedence > BinOp.UNARY_PRECEDENCE ) + return unopExpression( u.op, binopExpression( u.rhs, binop, rhs, scope ), scope ); + } + // TODO: cumulate string concatenations together + // TODO: constant folding + if ( lhs instanceof BinopExpr ) { + BinopExpr b = (BinopExpr) lhs; + if ( (binop.precedence > b.op.precedence) || + ((binop.precedence == b.op.precedence) && binop.isrightassoc) ) + return binopExpression( b.lhs, b.op, binopExpression( b.rhs, binop, rhs, scope ), scope ); + } + if ( rhs instanceof BinopExpr ) { + BinopExpr b = (BinopExpr) rhs; + if ( (binop.precedence > b.op.precedence) || + ((binop.precedence == b.op.precedence) && ! binop.isrightassoc) ) + return binopExpression( binopExpression( lhs, binop, b.lhs, scope ), b.op, b.rhs, scope ); + } + return new BinopExpr( lhs, binop, rhs, scope ); + } + + public static LSExpression numberExpression(String number) { + return new NumberConstant(number); + } + + public static LSExpression tableConstructorExpression(List fields) { + return new TableConstructor( fields ); + } + + public static LSExpression normalStringExpression(String luaSourceString) { + return new StringConstant(luaSourceString.substring(1,luaSourceString.length()-1)); + } + + public static LSExpression charStringExpression(String luaSourceString) { + return new StringConstant(luaSourceString.substring(1,luaSourceString.length()-1)); + } + + public static LSExpression longStringExpression(String luaSourceString) { + luaSourceString = luaSourceString.substring(1,luaSourceString.length()-1); + luaSourceString = luaSourceString.substring(luaSourceString.indexOf('[',1)+1, luaSourceString.lastIndexOf(']')); + return new StringConstant(luaSourceString); + } + + public static LSExpression varargsRef() { + return new VarargsRef(); + } + + /** varargs such as "..." */ + public static class VarargsRef extends LSExpression { + public int numReturns = -1; + public VarargsRef() { + super( Type.varargsRef ); + } + public void setNumReturns(int i) { + this.numReturns = i; + } + public int getNumReturns() { + return numReturns; + } + public String toString() { return "..."; } + } + + /** prefix expression such as "(foo)(bar)" ? */ + public static class FunctionExpr extends LSExpression { + public final LSFunction function; + public FunctionExpr( LSFunction function) { + super( Type.functionExpr ); + this.function = function; + } + public String toString() { return function.toString(); } + } + + /** unary operator such as "not foo" */ + public static class UnopExpr extends LSExpression { + public final UnOp op; + public final LSExpression rhs; + public UnopExpr( UnOp op, LSExpression rhs ) { + super( Type.unop ); + this.op = op; + this.rhs = rhs; + } + public String toString() { return op.luaop+rhs; } + } + + /** binary operator such as "a + b" */ + public static class BinopExpr extends LSExpression { + public final LSExpression lhs; + public final BinOp op; + public final LSExpression rhs; + public final Scope scope; + public BinopExpr( LSExpression lhs, BinOp op, LSExpression rhs, Scope scope ) { + super( Type.binop ); + this.lhs = lhs; + this.op = op; + this.rhs = rhs; + this.scope = scope; + } + public String toString() { return lhs+op.luaop+rhs; } + } + + /** table constructor such as "{ 'foo', [0]='bar' }" */ + public static class TableConstructor extends LSExpression { + public final List fields; + public TableConstructor( List fields ) { + super( Type.tableConstructor ); + this.fields = fields; + int n = fields.size(); + for ( int i=0; i0 ) + fields.get(n-1).setNumReturns(-1); + } + public String toString() { return "{"+fields+"}"; } + } + + /** number constants such as '123', "4.56", "0x11fe */ + public static class NumberConstant extends LSExpression { + public final Number value; + public NumberConstant( String number ) { + super( Type.numberConstant ); + number = number.toLowerCase(); + if ( number.startsWith("0x") ) { + Long l = Long.parseLong(number.substring(2), 16); + value = (l.intValue()==l.longValue()? (Number) Integer.valueOf(l.intValue()): (Number) l); + } else { + Double d = Double.parseDouble(number); + value = (d.doubleValue()==(double)d.intValue()? (Number) Integer.valueOf(d.intValue()): (Number) d); + } + } + public String toString() { return value.toString(); } + } + + /** string constants such as 'abc', "def", [[ghi]], and [==[pqr]==] */ + public static class StringConstant extends LSExpression { + public final byte[] bytes; + public StringConstant( String luaSourceChars ) { + super( Type.stringConstant ); + this.bytes = unquoteLua( luaSourceChars ); + } + public String toString() { return "\""+new String(bytes)+"\""; } + } + + /** Unquote lua quoted sequences, and convert to the bytes represented by the source string. */ + public static byte[] unquoteLua( String luaSourceChars ) { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + char[] c = luaSourceChars.toCharArray(); + int n = c.length; + for ( int i=0; i='0' && c[i]<='9'; i++, j++ ) + d = d * 10 + (int) (c[i]-'0'); + baos.write( (byte) d ); + --i; + continue; + case 'a': baos.write( (byte) 7 ); continue; + case 'b': baos.write( (byte) '\b' ); continue; + case 'f': baos.write( (byte) '\f' ); continue; + case 'n': baos.write( (byte) '\n' ); continue; + case 'r': baos.write( (byte) '\r' ); continue; + case 't': baos.write( (byte) '\t' ); continue; + case 'v': baos.write( (byte) 11 ); continue; + case '"': baos.write( (byte) '"' ); continue; + case '\'': baos.write( (byte) '\'' ); continue; + case '\\': baos.write( (byte) '\\' ); continue; + default: baos.write( (byte) c[i] ); break; + } + } else { + baos.write( (byte) c[i] ); + } + } + return baos.toByteArray(); + } + + /** Set number of return values, return actual number of returns in practice. + * + * @param i desired number of returns, or -1 for varargs. + */ + public void setNumReturns(int i) { + } + + /** Get actual number of returns for this subexpression, or -1 for varargs. + * + * @return actual number of returns, or -1 for varargs. + */ + public int getNumReturns() { + return 1; + } + +} diff --git a/src/jse/org/luaj/vm2/luajc/lst/LSField.java b/src/jse/org/luaj/vm2/luajc/lst/LSField.java new file mode 100644 index 00000000..136296f4 --- /dev/null +++ b/src/jse/org/luaj/vm2/luajc/lst/LSField.java @@ -0,0 +1,92 @@ +/******************************************************************************* +* Copyright (c) 2009 Luaj.org. All rights reserved. +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +* THE SOFTWARE. +******************************************************************************/ +package org.luaj.vm2.luajc.lst; + +public class LSField { + public enum Type { + keyValue, + nameValue, + listValue, + } + + public final Type type; + + LSField(Type type) { + this.type = type; + } + + public static LSField keyValueField(LSExpression key, LSExpression value) { + return new KeyValue(key, value); + } + + public static LSField nameValueField(String name, LSExpression value) { + return new NameValue(name, value); + } + + public static LSField valueField(LSExpression value) { + return new ListValue(value); + } + + /** table constructor field with an explicit key index value */ + public static class KeyValue extends LSField { + public final LSExpression key; + public final LSExpression value; + public KeyValue(LSExpression key, LSExpression value) { + super( Type.keyValue ); + this.key = key; + this.value = value; + value.setNumReturns(1); + } + public String toString() { return "["+key+"]="+value; } + } + + + /** table constructor field with an named field for key */ + public static class NameValue extends LSField { + public final String name; + public final LSExpression value; + public NameValue(String name, LSExpression value) { + super( Type.nameValue ); + this.name = name; + this.value = value; + value.setNumReturns(1); + } + public String toString() { return name+"="+value; } + } + + /** table constructor field with an implied index key */ + public static class ListValue extends LSField { + public final LSExpression value; + public ListValue(LSExpression value) { + super( Type.listValue ); + this.value = value; + } + public void setNumReturns(int i) { + value.setNumReturns(i); + } + public String toString() { return value.toString(); } + } + + public void setNumReturns(int i) { + } + +} diff --git a/src/jse/org/luaj/vm2/luajc/lst/LSFunction.java b/src/jse/org/luaj/vm2/luajc/lst/LSFunction.java new file mode 100644 index 00000000..102bd455 --- /dev/null +++ b/src/jse/org/luaj/vm2/luajc/lst/LSFunction.java @@ -0,0 +1,75 @@ +/******************************************************************************* +* Copyright (c) 2009 Luaj.org. All rights reserved. +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +* THE SOFTWARE. +******************************************************************************/ +package org.luaj.vm2.luajc.lst; + +import java.util.ArrayList; +import java.util.List; + +public class LSFunction { + + public final List stats = new ArrayList(); + public final List functions = new ArrayList(); + public final List paramnames = new ArrayList(); + + /** set if this is a vararg function */ + public boolean isvararg; + + /** needsarg is set if the code is vararg and needs the "arg" table to be created. */ + public boolean hasarg,needsarg; + + /** max number of returns, or -1 for varargs */ + public int maxReturns = 0; + + /** set if there are logical subexpressions, or varargs assignment */ + public boolean hasandlogic, hasorlogic, hasvarargassign, usesvarargs; + + public LSFunction() { + } + + public LSFunction(boolean isvararg) { + this.isvararg = isvararg; + } + + public void setStatements(List stats) { + this.stats.clear(); + this.stats.addAll(stats); + } + + public void setParameterNames(List list) { + this.paramnames.clear(); + this.paramnames.addAll( list ); + } + + public String toString() { return "function("+paramnames+") "+stats+" end"; } + + public void setUsesVarargs() { + this.usesvarargs = true; + if ( this.hasarg ) + this.needsarg = false; + } + + public void setHasArg() { + this.hasarg = true; + if ( ! this.usesvarargs ) + this.needsarg = true; + } +} diff --git a/src/jse/org/luaj/vm2/luajc/lst/LSIfStatement.java b/src/jse/org/luaj/vm2/luajc/lst/LSIfStatement.java new file mode 100644 index 00000000..eae05bcf --- /dev/null +++ b/src/jse/org/luaj/vm2/luajc/lst/LSIfStatement.java @@ -0,0 +1,76 @@ +/******************************************************************************* +* Copyright (c) 2009 Luaj.org. All rights reserved. +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +* THE SOFTWARE. +******************************************************************************/ +package org.luaj.vm2.luajc.lst; + +import java.util.ArrayList; +import java.util.List; + +public class LSIfStatement extends LSStatement { + + public final LSExpression condition; + public final List statements; + public List elseifs; + public List elsestatements; + + public static class ElseIf { + public final LSExpression condition; + public final List statements; + public ElseIf(LSExpression condition, List statements) { + this.condition = condition; + this.statements = statements; + } + } + + public LSIfStatement(LSExpression condition, List statements) { + super( Type.ifStat ); + this.condition = condition; + this.statements = statements; + } + + public void addElseif(LSExpression condition, List statements) { + if ( elseifs == null ) + elseifs = new ArrayList(); + elseifs.add( new ElseIf( condition, statements ) ); + } + + public void addElse(List statements) { + elsestatements = statements; + } + + public String toString() { + return "if "+condition+" then "+statements+ + (elseifs!=null? elseifs.toString(): "")+ + (elsestatements!=null? " else "+elsestatements: ""); + } + public boolean isNextStatementReachable() { + if ( isNextStatementReachable(statements) ) + return true; + if ( elseifs != null ) + for ( ElseIf e : elseifs ) + if ( isNextStatementReachable(statements) ) + return true; + if ( isNextStatementReachable(elsestatements) ) + return true; + return false; + } + +} diff --git a/src/jse/org/luaj/vm2/luajc/lst/LSStatement.java b/src/jse/org/luaj/vm2/luajc/lst/LSStatement.java new file mode 100644 index 00000000..87625ffb --- /dev/null +++ b/src/jse/org/luaj/vm2/luajc/lst/LSStatement.java @@ -0,0 +1,327 @@ +/******************************************************************************* +* Copyright (c) 2009 Luaj.org. All rights reserved. +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +* THE SOFTWARE. +******************************************************************************/ +package org.luaj.vm2.luajc.lst; + +import java.util.List; + +public class LSStatement { + + public enum Type { + functionCall, + doBlock, + whileLoop, + repeatUntil, + varAssign, + forLoop, + forList, + varNamedFunction, + localFunction, + localAssign, + returnStat, + breakStat, + ifStat, + } + + public final Type type; + + LSStatement( Type type ) { + this.type = type; + } + + public static LSStatement functionCallStatement(LSVariable variable) { + return new FunctionCall( variable ); + } + + public static LSStatement doBlockStatement(List statements) { + return new DoBlock( statements ); + } + + public static LSStatement whileLoopStatement(LSExpression condition, List statements) { + return new WhileLoop( condition, statements ); + } + + public static LSStatement repeatUntilStatement(List statements, LSExpression condition) { + return new RepeatUntil( statements, condition ); + } + + public static LSStatement forLoopStatement(Name name, LSExpression initial, + LSExpression limit, LSExpression step, List statements, Scope scope) { + return new ForLoop( name, initial, limit, step, statements, scope ); + } + + public static LSStatement forListStatement(List names, + List expressions, List statements, Scope scope, LSFunction function) { + return new ForList( names, expressions, statements, scope, function ); + } + + public static LSStatement varFunctionStatement(LSVariable funcname, LSFunction funcbody) { + return new VarNamedFunction( funcname, funcbody ); + } + + public static LSStatement localFunctionStatement(Name name, LSFunction funcbody) { + return new LocalFunction( name, funcbody ); + } + + public static LSStatement varAssignStatement(List variables, List expressions, Scope scope, LSFunction function) { + setExprNumReturns( variables.size(), expressions, function ); + return new VarAssign( variables, expressions, scope ); + } + + public static LSStatement localAssignStatement(List names, List expressions, Scope scope, LSFunction function) { + setExprNumReturns( names.size(), expressions, function ); + return new LocalAssign( names, expressions, scope ); + } + + public static void setExprNumReturns( int nassign, List expressions, LSFunction function ) { + int nexpr = expressions!=null? expressions.size(): 0; + for ( int i=0; i nexpr && nexpr > 0 && expressions.get(nexpr-1).getNumReturns() == -1 ) + function.hasvarargassign = true; + } + + public static LSStatement returnStatement(LSFunction function, List expressions) { + int n=expressions!=null? expressions.size(): 0; + + // set num returns of subexpressions + for ( int i=0; i 0 ) { + LSExpression last = expressions.get(n-1); + int lastreturns = last.getNumReturns(); + nreturns = (lastreturns<0? -1: lastreturns+n-1); + } + + // change the containing function to varargs if necessary. + if ( function.maxReturns != -1 && function.maxReturns < nreturns ) + function.maxReturns = nreturns; + if ( function.maxReturns == -1 || function.maxReturns > 1 || nreturns < 0 ) + function.isvararg = true; + + return new ReturnStat( function, expressions ); + } + + public static LSStatement breakStatement() { + return new BreakStat(); + } + + /** Statement representing a function call on a variable, such as "foo.bar()" */ + public static final class FunctionCall extends LSStatement { + public final LSVariable variable; + FunctionCall(LSVariable variable) { + super( Type.functionCall ); + this.variable = variable; + } + public String toString() { return variable.toString()+"();"; } + } + + /** do block, such as "do foo = bar end" */ + public static final class DoBlock extends LSStatement { + public final List statements; + DoBlock(List statements) { + super( Type.doBlock ); + this.statements = statements; + } + public String toString() { return "do "+statements+" end;"; } + public boolean isNextStatementReachable() { + return isNextStatementReachable(statements); + } + } + + /** while loop, such as "while foo = true do bar() end" */ + public static final class WhileLoop extends LSStatement { + public final LSExpression condition; + public final List statements; + WhileLoop(LSExpression condition, List statements) { + super( Type.whileLoop ); + this.condition = condition; + this.statements = statements; + } + public String toString() { return "while "+condition+" do "+statements+" end;"; } + public boolean isNextStatementReachable() { + return isNextStatementReachable(statements); + } + } + + /** repeat loop, such as "repeat foo() until bar == true" */ + public static final class RepeatUntil extends LSStatement { + public final List statements; + public final LSExpression condition; + RepeatUntil(List statements, LSExpression condition) { + super( Type.repeatUntil ); + this.statements = statements; + this.condition = condition; + } + public String toString() { return "repeat "+statements+" until "+condition+";"; } + public boolean isNextStatementReachable() { + return isNextStatementReachable(statements); + } + } + + /** assignment to variables, such as "x.a,y.b = foo,bar" */ + public static final class VarAssign extends LSStatement { + public final List variables; + public final List expressions; + public final Scope scope; + VarAssign(List variables, List expressions, Scope scope) { + super( Type.varAssign ); + this.variables = variables; + this.expressions = expressions; + this.scope = scope; + } + public String toString() { return variables+"="+expressions+";"; } + } + + /** for loop with index, such as "for i=1,10,2 do ... end" */ + public static final class ForLoop extends LSStatement { + public final Name index; + public final LSExpression initial; + public final LSExpression limit; + public final LSExpression step; + public final List statements; + public final Scope scope; + ForLoop(Name name, LSExpression initial, LSExpression limit, LSExpression step, List statements, Scope scope) { + super( Type.forLoop ); + this.index = name; + this.initial = initial; + this.limit = limit; + this.step = step; + this.statements = statements; + this.scope = scope; + initial.setNumReturns(1); + limit.setNumReturns(1); + if ( step != null ) + step.setNumReturns(1); + } + public String toString() { return "for "+index+"="+initial+","+limit+","+step+" do "+statements+" end;"; } + public boolean isNextStatementReachable() { + return isNextStatementReachable(statements); + } + } + + /** for loop with variable, such as "for i,j,k in foo() do ... end" */ + public static final class ForList extends LSStatement { + public final List names; + public final List expressions; + public final List statements; + public final Scope scope; + ForList(List names, List expressions, List statements, Scope scope, LSFunction function) { + super( Type.forList ); + this.names = names; + this.expressions = expressions; + this.statements = statements; + this.scope = scope; + + // set return value count for each expression in list; + int ne = expressions.size(); + for ( int i=0; i names; + public final List expressions; + public final Scope scope; + LocalAssign(List list, List expressions, Scope scope) { + super( Type.localAssign ); + this.names = list; + this.expressions = expressions; + this.scope = scope; + } + public String toString() { return names+"="+expressions+";"; } + } + + /** return statement, such as "return foo,bar" */ + public static final class ReturnStat extends LSStatement { + public final LSFunction function; + public final List expressions; + ReturnStat(LSFunction function, List expressions) { + super( Type.returnStat ); + this.function = function; + this.expressions = expressions; + } + public String toString() { return "return "+expressions+";"; } + public boolean isNextStatementReachable() { + return false; + } + } + + /** break statement */ + public static final class BreakStat extends LSStatement { + BreakStat() { + super( Type.breakStat ); + } + public String toString() { return "break;"; } + } + + /** True of this statment could return and therefore the next statement is reachable. */ + public boolean isNextStatementReachable() { + return true; + } + + public static boolean isNextStatementReachable(List stats) { + if ( stats == null ) + return true; + for ( LSStatement s : stats ) + if ( ! s.isNextStatementReachable() ) + return false; + return true; + } +} diff --git a/src/jse/org/luaj/vm2/luajc/lst/LSVariable.java b/src/jse/org/luaj/vm2/luajc/lst/LSVariable.java new file mode 100644 index 00000000..c36f96cf --- /dev/null +++ b/src/jse/org/luaj/vm2/luajc/lst/LSVariable.java @@ -0,0 +1,172 @@ +/******************************************************************************* +* Copyright (c) 2009 Luaj.org. All rights reserved. +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +* THE SOFTWARE. +******************************************************************************/ +package org.luaj.vm2.luajc.lst; + +import java.util.List; + +public class LSVariable extends LSExpression { + + LSVariable(Type type) { + super(type); + } + + /** name, such as 'foo' */ + public static LSVariable nameVariable(Name name) { + return new NameReference(name); + } + + /** table field, such as 'a.b' */ + public LSVariable fieldVariable(String field) { + return new Field(this, field); + } + + /** method reference, such as foo:bar */ + public LSVariable methodVariable(String method) { + return new Method(this, method); + } + + /** parenthetical reference, such as '(foo())' */ + public static LSVariable parenthesesVariable(LSExpression expression) { + if ( expression != null ) + expression.setNumReturns(1); + return new Parentheses(expression); + } + + /** table index, such as 'a[b]' */ + public LSVariable indexVariable(LSExpression expression) { + return new Index(this, expression); + } + + /** Variable is a method, such as 'a(x,y)' */ + public LSVariable callFuncVariable(List parameters) { + int n = parameters.size(); + for ( int i=0; i parameters) { + int n = parameters.size(); + for ( int i=0; i parameters; + public int numReturns = 0; + public CallFunction(LSVariable variable, List parameters) { + super( Type.callFunctionVariable ); + this.variable = variable; + this.parameters = parameters; + } + public void setNumReturns(int i) { + this.numReturns = i; + } + public int getNumReturns() { + return numReturns; + } + public String toString() { return variable+"("+parameters+")"; } + } + + /** Variable is a method invocation, such as 'a:bc()' */ + public static class CallMethod extends LSVariable { + public final LSVariable variable; + public final String method; + public final List parameters; + public int numReturns = 0; + public CallMethod(LSVariable variable, String method, List parameters) { + super( Type.callMethodVariable ); + this.variable = variable; + this.method = method; + this.parameters = parameters; + } + public void setNumReturns(int i) { + this.numReturns = i; + } + public int getNumReturns() { + return numReturns; + } + public String toString() { return variable+":"+method+"("+parameters+")"; } + } + +} diff --git a/src/jse/org/luaj/vm2/luajc/lst/Name.java b/src/jse/org/luaj/vm2/luajc/lst/Name.java new file mode 100644 index 00000000..f94242a4 --- /dev/null +++ b/src/jse/org/luaj/vm2/luajc/lst/Name.java @@ -0,0 +1,73 @@ +/******************************************************************************* +* Copyright (c) 2009 Luaj.org. All rights reserved. +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +* THE SOFTWARE. +******************************************************************************/ +package org.luaj.vm2.luajc.lst; + +/** A name in lua source, which resolves to one of: + * - global reference + * - local that is not an upvalue + * - local that is an upvalue + */ +public class Name { + + /** Name in lua source file */ + public final String luaname; + + /** 0 if in outermost scope, 1 if in next inner scope, ect. */ + public final int scopelevel; + + /** 0 if first declaration in global program, 1 if second use, etc. */ + public final int outerrevision; + + /** 0 if first declaration in nearest enclosing function, 1 if second use, etc */ + public final int innerrevision; + + /** true if used as an upvalue by some enclosed function */ + public boolean isupvalue; + + /** Construct a name instance */ + public Name(String luaname, int scopelevel, int outerrevision, int innterrevision) { + super(); + this.luaname = luaname; + this.scopelevel = scopelevel; + this.outerrevision = outerrevision; + this.innerrevision = innterrevision; + } + + /** Construct a name reference representing a global reference */ + public Name(String name) { + this.luaname = name; + this.scopelevel = -1; + this.outerrevision = -1; + this.innerrevision = -1; + } + + public String toString() { + return scopelevel<0? + "_G$"+luaname: + luaname+"$s"+scopelevel+"$v"+outerrevision+"$f"+innerrevision; + } + + public boolean isGlobal() { + return -1 == outerrevision; + } + +} diff --git a/src/jse/org/luaj/vm2/luajc/lst/Scope.java b/src/jse/org/luaj/vm2/luajc/lst/Scope.java new file mode 100644 index 00000000..77114a37 --- /dev/null +++ b/src/jse/org/luaj/vm2/luajc/lst/Scope.java @@ -0,0 +1,103 @@ +/******************************************************************************* +* Copyright (c) 2009 Luaj.org. All rights reserved. +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +* THE SOFTWARE. +******************************************************************************/ +package org.luaj.vm2.luajc.lst; + +import java.util.HashMap; +import java.util.Map; + +/** A name scope in lua source terms. + * Combination of function scope and block scope. + */ +public class Scope { + public final Scope parent; + public final int level; + public final boolean isFunctionScope; + public boolean hasAndOrLogic; + public final Map names = new HashMap(); + + /** Construct a new default scope for a chunk */ + public Scope() { + this.parent = null; + this.isFunctionScope = true; + this.level = 0; + } + + /** Construct an inner scope + * @param parent the outer scope to fall back to, or null if a global scope + * @param isFunctionScope true if this is a function scope, false otherwise + */ + public Scope(Scope parent, boolean isFunctionScope) { + this.parent = parent; + this.isFunctionScope = isFunctionScope; + this.level = parent!=null? parent.level + 1: 0; + } + + /** Declare a single name in the current scope, and return the Name element for it */ + public Name declare(String name) { + boolean crossesFunctionBoundary = false; + for ( Scope s=this; s!=null; s=s.parent ) { + Name n = s.names.get(name); + if ( n != null ) { + Name result = new Name(name, + level, + n.outerrevision+1, + crossesFunctionBoundary? 0: n.innerrevision+1); + names.put(name, result); + return result; + } + if ( s.isFunctionScope ) + crossesFunctionBoundary = true; + } + Name result = new Name(name, level, 0, 0); + names.put(name, result); + return result; + } + + /** Reference a name, and find either the local scope within the function, the upvalue, or a global name */ + public Name reference(String name) { + boolean crossesFunctionBoundary = false; + for ( Scope s=this; s!=null; s=s.parent ) { + Name n = s.names.get(name); + if ( n != null ) { + if ( crossesFunctionBoundary ) { + n.isupvalue = true; + } + return n; + } + if ( s.isFunctionScope ) + crossesFunctionBoundary = true; + } + // globally scoped name + return new Name(name); + } + + public String toString() { + String ours = (isFunctionScope? "F": "")+names; + return (parent==null? ours: ours+"->"+parent.toString()); + } + + /** Return true iff this scope is part the main chunk */ + public boolean isMainChunkScope() { + return isFunctionScope? false: parent==null? true: parent.isMainChunkScope(); + } +} + diff --git a/src/jse/org/luaj/vm2/luajc/lst/UnOp.java b/src/jse/org/luaj/vm2/luajc/lst/UnOp.java new file mode 100644 index 00000000..2fc2b44c --- /dev/null +++ b/src/jse/org/luaj/vm2/luajc/lst/UnOp.java @@ -0,0 +1,43 @@ +/******************************************************************************* +* Copyright (c) 2009 Luaj.org. All rights reserved. +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +* THE SOFTWARE. +******************************************************************************/ +package org.luaj.vm2.luajc.lst; + +public class UnOp { + public enum Type { + neg,not,len + } + + public static final UnOp NEG = new UnOp(Type.neg, "-"); + public static final UnOp NOT = new UnOp(Type.not, "~"); + public static final UnOp LEN = new UnOp(Type.len, "#"); + + public final Type type; + public final String luaop; + + private UnOp(Type type, String luaop) { + super(); + this.type = type; + this.luaop = luaop; + } + + public String toString() { return luaop; } +} diff --git a/src/jse/org/luaj/vm2/script/LuaScriptEngine.java b/src/jse/org/luaj/vm2/script/LuaScriptEngine.java new file mode 100644 index 00000000..eb90cf8a --- /dev/null +++ b/src/jse/org/luaj/vm2/script/LuaScriptEngine.java @@ -0,0 +1,303 @@ +/******************************************************************************* +* Copyright (c) 2008 LuaJ. All rights reserved. +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +* THE SOFTWARE. +******************************************************************************/ +package org.luaj.vm2.script; + +import java.io.IOException; +import java.io.InputStream; +import java.io.Reader; +import java.io.StringReader; +import java.util.Collection; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; + +import javax.script.Bindings; +import javax.script.Compilable; +import javax.script.CompiledScript; +import javax.script.ScriptContext; +import javax.script.ScriptEngine; +import javax.script.ScriptEngineFactory; +import javax.script.ScriptException; +import javax.script.SimpleScriptContext; + +import org.luaj.vm2.LoadState; +import org.luaj.vm2.Lua; +import org.luaj.vm2.LuaClosure; +import org.luaj.vm2.LuaError; +import org.luaj.vm2.LuaValue; +import org.luaj.vm2.Prototype; +import org.luaj.vm2.compiler.LuaC; +import org.luaj.vm2.lib.JsePlatform; + +/** + * + * @author jim_roseborough + */ +public class LuaScriptEngine implements ScriptEngine, Compilable { + + private static final String __ENGINE_VERSION__ = Lua._VERSION; + private static final String __NAME__ = "Luaj"; + private static final String __SHORT_NAME__ = "Luaj"; + private static final String __LANGUAGE__ = "lua"; + private static final String __LANGUAGE_VERSION__ = "5.1"; + private static final String __ARGV__ = "arg"; + private static final String __FILENAME__ = "?"; + + static { + LuaC.install(); + } + + private static final ScriptEngineFactory myFactory = new LuaScriptEngineFactory(); + + private ScriptContext defaultContext; + + private final LuaValue _G; + + public LuaScriptEngine() { + + // create globals + _G = JsePlatform.standardGlobals(); + + // set up context + ScriptContext ctx = new SimpleScriptContext(); + ctx.setBindings(createBindings(), ScriptContext.ENGINE_SCOPE); + setContext(ctx); + + // set special values + put(LANGUAGE_VERSION, __LANGUAGE_VERSION__); + put(LANGUAGE, __LANGUAGE__); + put(ENGINE, __NAME__); + put(ENGINE_VERSION, __ENGINE_VERSION__); + put(ARGV, __ARGV__); + put(FILENAME, __FILENAME__); + put(NAME, __SHORT_NAME__); + put("THREADING", null); + } + + + public Object eval(String script) throws ScriptException { + return eval(new StringReader(script)); + } + + public Object eval(String script, ScriptContext context) throws ScriptException { + return eval(new StringReader(script), context); + } + + public Object eval(String script, Bindings bindings) throws ScriptException { + return eval(new StringReader(script), bindings); + } + + public Object eval(Reader reader) throws ScriptException { + return eval(reader, getContext()); + } + + public Object eval(Reader reader, ScriptContext scriptContext) throws ScriptException { + return compile(reader).eval(scriptContext); + } + + public Object eval(Reader reader, Bindings bindings) throws ScriptException { + ScriptContext c = getContext(); + Bindings current = c.getBindings(ScriptContext.ENGINE_SCOPE); + c.setBindings(bindings, ScriptContext.ENGINE_SCOPE); + Object result = eval(reader); + c.setBindings(current, ScriptContext.ENGINE_SCOPE); + return result; + } + + public void put(String key, Object value) { + Bindings b = getBindings(ScriptContext.ENGINE_SCOPE); + ((LuaBindings)b).put(key, value); + } + + public Object get(String key) { + Bindings b = getBindings(ScriptContext.ENGINE_SCOPE); + return ((LuaBindings)b).get(key); + } + + public Bindings getBindings(int scope) { + return getContext().getBindings(scope); + } + + public void setBindings(Bindings bindings, int scope) { + getContext().setBindings(bindings, scope); + } + + public Bindings createBindings() { + return new LuaBindings( _G ); + } + + public ScriptContext getContext() { + return defaultContext; + } + + public void setContext(ScriptContext context) { + defaultContext = context; + } + + public ScriptEngineFactory getFactory() { + return myFactory; + } + + public CompiledScript compile(String script) throws ScriptException { + return compile(new StringReader(script)); + } + + public CompiledScript compile(Reader reader) throws ScriptException { + try { + InputStream ris = new Utf8Encoder(reader); + try { + final Prototype p = LoadState.undump(ris, "script"); + return new CompiledScript() { + public Object eval(ScriptContext context) throws ScriptException { + Bindings b = context.getBindings(ScriptContext.ENGINE_SCOPE); + LuaBindings lb = (LuaBindings) b; + LuaClosure c = new LuaClosure( p, lb.env ); + return c.call(); + } + public ScriptEngine getEngine() { + return LuaScriptEngine.this; + } + }; + } catch ( LuaError lee ) { + throw new ScriptException(lee.getMessage() ); + } finally { + ris.close(); + } + } catch ( Throwable t ) { + throw new ScriptException("eval threw "+t.toString()); + } + } + + // ------ lua bindings ----- + private static final class LuaBindings implements Bindings { + private LuaValue env; + private LuaValue mt; + private LuaBindings( LuaValue _G ) { + mt = LuaValue.tableOf(); + mt.set( LuaValue.INDEX, _G ); + clear(); + } + public void clear() { + env = LuaValue.tableOf(); + env.setmetatable(mt); + } + private Object toJava(LuaValue v) { + switch ( v.type() ) { + case LuaValue.TNIL: return null; + case LuaValue.TSTRING: return v.toString(); + case LuaValue.TUSERDATA: return v.checkuserdata(Object.class); + case LuaValue.TNUMBER: return v.isinttype()? new Integer(v.toint()): new Double(v.todouble()); + default: + throw new java.lang.UnsupportedOperationException( + "LuaBindings cannot convert lua type '"+v.typename()+"' to Java"); + } + } + private LuaValue toLua(Object javaValue) { + if ( javaValue instanceof Number ) { + return LuaValue.valueOf(((Number)javaValue).doubleValue()); + } else if ( javaValue instanceof String ) { + return LuaValue.valueOf(javaValue.toString()); + } else if ( javaValue == null ) { + return LuaValue.NIL; + } else { + return LuaValue.userdataOf(javaValue); + } + } + public boolean containsKey(Object key) { + return ! env.get(toLua(key)).isnil(); + } + public Object get(Object key) { + return toJava(env.get(toLua(key))); + } + public Object put(String name, Object value) { + LuaValue key = toLua(name); + Object result = toJava(env.get(key)); + env.set(key, toLua(value)); + return result; + } + public void putAll(Map toMerge) { + for ( Iterator it=toMerge.entrySet().iterator(); it.hasNext(); ) { + Map.Entry e = (Map.Entry) it.next(); + put( e.getKey(), e.getValue() ); + } + } + public Object remove(Object javakey) { + LuaValue key = toLua(javakey); + Object result = toJava(env.get(key)); + env.set(key, LuaValue.NIL); + return result; + } + public boolean containsValue(Object value) { + throw new java.lang.UnsupportedOperationException( + "containsValue() not supported for LuaBindings"); + } + public Set> entrySet() { + throw new java.lang.UnsupportedOperationException( + "entrySet() not supported for LuaBindings"); + } + public boolean isEmpty() { + throw new java.lang.UnsupportedOperationException( + "isEmpty() not supported for LuaBindings"); + } + public Set keySet() { + throw new java.lang.UnsupportedOperationException( + "keySet() not supported for LuaBindings"); + } + public int size() { + throw new java.lang.UnsupportedOperationException( + "size() not supported for LuaBindings"); + } + public Collection values() { + throw new java.lang.UnsupportedOperationException( + "values() not supported for LuaBindings"); + } + } + + // ------ convert char stream to byte stream for lua compiler ----- + + private final class Utf8Encoder extends InputStream { + private final Reader r; + private final int[] buf = new int[2]; + private int n; + + private Utf8Encoder(Reader r) { + this.r = r; + } + + public int read() throws IOException { + if ( n > 0 ) + return buf[--n]; + int c = r.read(); + if ( c < 0x80 ) + return c; + n = 0; + if ( c < 0x800 ) { + buf[n++] = (0x80 | ( c & 0x3f)); + return (0xC0 | ((c>>6) & 0x1f)); + } else { + buf[n++] = (0x80 | ( c & 0x3f)); + buf[n++] = (0x80 | ((c>>6) & 0x3f)); + return (0xE0 | ((c>>12) & 0x0f)); + } + } + } +} diff --git a/src/jse/org/luaj/vm2/script/LuaScriptEngineFactory.java b/src/jse/org/luaj/vm2/script/LuaScriptEngineFactory.java new file mode 100644 index 00000000..8f48d63e --- /dev/null +++ b/src/jse/org/luaj/vm2/script/LuaScriptEngineFactory.java @@ -0,0 +1,127 @@ +/******************************************************************************* +* Copyright (c) 2008 LuaJ. All rights reserved. +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +* THE SOFTWARE. +******************************************************************************/ +package org.luaj.vm2.script; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import javax.script.ScriptEngine; +import javax.script.ScriptEngineFactory; + +/** + * + * @author jim_roseborough + */ +public class LuaScriptEngineFactory implements ScriptEngineFactory { + + private static final String FILEEXT = ".lua"; + + private static final String [] MIMETYPES = { + "text/plain", + "text/lua", + "application/lua" + }; + + private static final String [] NAMES = { + "lua", + "luaj", + }; + + private ScriptEngine myScriptEngine; + private List extensions; + private List mimeTypes; + private List names; + + public LuaScriptEngineFactory() { + myScriptEngine = new LuaScriptEngine(); + extensions = Collections.nCopies(1, FILEEXT); + mimeTypes = Arrays.asList(MIMETYPES); + names = Arrays.asList(NAMES); + } + + public String getEngineName() { + return getScriptEngine().get(ScriptEngine.ENGINE).toString(); + } + + public String getEngineVersion() { + return getScriptEngine().get(ScriptEngine.ENGINE_VERSION).toString(); + } + + public List getExtensions() { + return extensions; + } + + public List getMimeTypes() { + return mimeTypes; + } + + public List getNames() { + return names; + } + + public String getLanguageName() { + return getScriptEngine().get(ScriptEngine.LANGUAGE).toString(); + } + + public String getLanguageVersion() { + return getScriptEngine().get(ScriptEngine.LANGUAGE_VERSION).toString(); + } + + public Object getParameter(String key) { + return getScriptEngine().get(key).toString(); + } + + public String getMethodCallSyntax(String obj, String m, String... args) { + StringBuffer sb = new StringBuffer(); + sb.append(obj + ":" + m + "("); + int len = args.length; + for (int i = 0; i < len; i++) { + if (i > 0) { + sb.append(','); + } + sb.append(args[i]); + } + sb.append(")"); + return sb.toString(); + } + + public String getOutputStatement(String toDisplay) { + return "print(" + toDisplay + ")"; + } + + public String getProgram(String ... statements) { + StringBuffer sb = new StringBuffer(); + int len = statements.length; + for (int i = 0; i < len; i++) { + if (i > 0) { + sb.append('\n'); + } + sb.append(statements[i]); + } + return sb.toString(); + } + + public ScriptEngine getScriptEngine() { + return myScriptEngine; + } +} diff --git a/test/java/org/luaj/luajc/TestLuaJ.java b/test/java/org/luaj/luajc/TestLuaJ.java new file mode 100644 index 00000000..2b98eb93 --- /dev/null +++ b/test/java/org/luaj/luajc/TestLuaJ.java @@ -0,0 +1,74 @@ +/******************************************************************************* + * Copyright (c) 2009 Luaj.org. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + ******************************************************************************/ +package org.luaj.luajc; + +import java.io.ByteArrayInputStream; +import java.io.InputStream; + +import org.luaj.vm2.LuaClosure; +import org.luaj.vm2.Print; +import org.luaj.vm2.Prototype; +import org.luaj.vm2.LuaTable; +import org.luaj.vm2.LuaValue; +import org.luaj.vm2.compiler.LuaC; +import org.luaj.vm2.lib.JsePlatform; + +/** Test the plain old bytecode interpreter */ +public class TestLuaJ { + // create the script + public static String name = "script"; + public static String script = + "function r(q,...)\n"+ + " local a=arg\n"+ + " return a and a[2]\n"+ + "end\n" + + "function s(q,...)\n"+ + " local a=arg\n"+ + " local b=...\n"+ + " return a and a[2],b\n"+ + "end\n" + + "print( r(111,222,333),s(111,222,333) )"; + + public static void main(String[] args) throws Exception { + System.out.println(script); + + // create an environment to run in + LuaTable _G = JsePlatform.standardGlobals(); + LuaC.install(); + + // compile into a chunk, or load as a class + InputStream is = new ByteArrayInputStream( script.getBytes() ); + Prototype p = LuaC.compile(is, "script"); + print( p ); + LuaValue chunk = new LuaClosure(p,_G); + chunk.call(); + } + + private static void print(Prototype p) { + System.out.println("--- "+p.is_vararg); + Print.printCode(p); + if (p.p!=null) + for ( int i=0,n=p.p.length; i0 && args[0].equals("nocompile")) ) { + InputStream is = new ByteArrayInputStream( script.getBytes() ); + String java =LuaJCompiler.compileToJava(is, "script"); + System.out.println("java:\n"+java); + chunk = LuaJCompiler.javaCompile(java, "script"); + } else { + chunk = (LuaValue) Class.forName("script").newInstance(); + } + chunk.setfenv(_G); + + // call with arguments + LuaValue[] vargs = new LuaValue[args.length]; + for ( int i=0; i= 0) { + os.write(buf, 0, r); + } + } + +} diff --git a/test/junit/org/luaj/vm2/TableArrayTest.java b/test/junit/org/luaj/vm2/TableArrayTest.java new file mode 100644 index 00000000..d7a112eb --- /dev/null +++ b/test/junit/org/luaj/vm2/TableArrayTest.java @@ -0,0 +1,355 @@ +/******************************************************************************* + * Copyright (c) 2009 Luaj.org. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + ******************************************************************************/ +package org.luaj.vm2; + +import java.util.Vector; + +import org.luaj.vm2.LuaDouble; +import org.luaj.vm2.LuaInteger; +import org.luaj.vm2.LuaString; +import org.luaj.vm2.LuaTable; +import org.luaj.vm2.LuaValue; + +import junit.framework.TestCase; + +/** + * Tests for tables used as lists. + */ +public class TableArrayTest extends TestCase { + + protected LuaTable new_Table() { + return new LuaTable(); + } + + protected LuaTable new_Table(int n,int m) { + return new LuaTable(n,m); + } + + public void testInOrderIntegerKeyInsertion() { + LuaTable t = new_Table(); + + for ( int i = 1; i <= 32; ++i ) { + t.set( i, LuaString.valueOf( "Test Value! "+i ) ); + } + + // Ensure all keys are still there. + for ( int i = 1; i <= 32; ++i ) { + assertEquals( "Test Value! " + i, t.get( i ).toString() ); + } + + // Ensure capacities make sense + assertEquals( 0, t.hashCapacity() ); + + assertTrue( t.arrayCapacity() >= 32 ); + assertTrue( t.arrayCapacity() <= 64 ); + + } + + public void testResize() { + LuaTable t = new_Table(); + + // NOTE: This order of insertion is important. + t.set(3, LuaInteger.valueOf(3)); + t.set(1, LuaInteger.valueOf(1)); + t.set(5, LuaInteger.valueOf(5)); + t.set(4, LuaInteger.valueOf(4)); + t.set(6, LuaInteger.valueOf(6)); + t.set(2, LuaInteger.valueOf(2)); + + for ( int i = 1; i < 6; ++i ) { + assertEquals(LuaInteger.valueOf(i), t.get(i)); + } + + assertTrue( t.arrayCapacity() >= 0 && t.arrayCapacity() <= 2 ); + assertTrue( t.hashCapacity() >= 4 ); + } + + public void testOutOfOrderIntegerKeyInsertion() { + LuaTable t = new_Table(); + + for ( int i = 32; i > 0; --i ) { + t.set( i, LuaString.valueOf( "Test Value! "+i ) ); + } + + // Ensure all keys are still there. + for ( int i = 1; i <= 32; ++i ) { + assertEquals( "Test Value! "+i, t.get( i ).toString() ); + } + + // Ensure capacities make sense + assertTrue( t.arrayCapacity() >= 0 ); + assertTrue( t.arrayCapacity() <= 6 ); + + assertTrue( t.hashCapacity() >= 16 ); + assertTrue( t.hashCapacity() <= 64 ); + + } + + public void testStringAndIntegerKeys() { + LuaTable t = new_Table(); + + for ( int i = 0; i < 10; ++i ) { + LuaString str = LuaString.valueOf( String.valueOf( i ) ); + t.set( i, str ); + t.set( str, LuaInteger.valueOf( i ) ); + } + + assertTrue( t.arrayCapacity() >= 9 ); // 1, 2, ..., 9 + assertTrue( t.arrayCapacity() <= 18 ); + assertTrue( t.hashCapacity() >= 11 ); // 0, "0", "1", ..., "9" + assertTrue( t.hashCapacity() <= 33 ); + + LuaValue[] keys = t.keys(); + + int intKeys = 0; + int stringKeys = 0; + + assertEquals( 20, keys.length ); + for ( int i = 0; i < keys.length; ++i ) { + LuaValue k = keys[i]; + + if ( k instanceof LuaInteger ) { + final int ik = k.toint(); + assertTrue( ik >= 0 && ik < 10 ); + final int mask = 1 << ik; + assertTrue( ( intKeys & mask ) == 0 ); + intKeys |= mask; + } else if ( k instanceof LuaString ) { + final int ik = Integer.parseInt( k.toString() ); + assertEquals( String.valueOf( ik ), k.toString() ); + assertTrue( ik >= 0 && ik < 10 ); + final int mask = 1 << ik; + assertTrue( "Key \""+ik+"\" found more than once", ( stringKeys & mask ) == 0 ); + stringKeys |= mask; + } else { + fail( "Unexpected type of key found" ); + } + } + + assertEquals( 0x03FF, intKeys ); + assertEquals( 0x03FF, stringKeys ); + } + + public void testBadInitialCapacity() { + LuaTable t = new_Table(0, 1); + + t.set( "test", LuaString.valueOf("foo") ); + t.set( "explode", LuaString.valueOf("explode") ); + assertEquals( 2, t.keyCount() ); + } + + public void testRemove0() { + LuaTable t = new_Table(2, 0); + + t.set( 1, LuaString.valueOf("foo") ); + t.set( 2, LuaString.valueOf("bah") ); + assertNotSame(LuaValue.NIL, t.get(1)); + assertNotSame(LuaValue.NIL, t.get(2)); + assertEquals(LuaValue.NIL, t.get(3)); + + t.set( 1, LuaValue.NIL ); + t.set( 2, LuaValue.NIL ); + t.set( 3, LuaValue.NIL ); + assertEquals(LuaValue.NIL, t.get(1)); + assertEquals(LuaValue.NIL, t.get(2)); + assertEquals(LuaValue.NIL, t.get(3)); + } + + public void testRemove1() { + LuaTable t = new_Table(0, 1); + assertEquals( 0, t.keyCount() ); + + t.set( "test", LuaString.valueOf("foo") ); + assertEquals( 1, t.keyCount() ); + t.set( "explode", LuaValue.NIL ); + assertEquals( 1, t.keyCount() ); + t.set( 42, LuaValue.NIL ); + assertEquals( 1, t.keyCount() ); + t.set( new_Table(), LuaValue.NIL ); + assertEquals( 1, t.keyCount() ); + t.set( "test", LuaValue.NIL ); + assertEquals( 0, t.keyCount() ); + + t.set( 10, LuaInteger.valueOf( 5 ) ); + t.set( 10, LuaValue.NIL ); + assertEquals( 0, t.keyCount() ); + } + + public void testRemove2() { + LuaTable t = new_Table(0, 1); + + t.set( "test", LuaString.valueOf("foo") ); + t.set( "string", LuaInteger.valueOf( 10 ) ); + assertEquals( 2, t.keyCount() ); + + t.set( "string", LuaValue.NIL ); + t.set( "three", LuaDouble.valueOf( 3.14 ) ); + assertEquals( 2, t.keyCount() ); + + t.set( "test", LuaValue.NIL ); + assertEquals( 1, t.keyCount() ); + + t.set( 10, LuaInteger.valueOf( 5 ) ); + assertEquals( 2, t.keyCount() ); + + t.set( 10, LuaValue.NIL ); + assertEquals( 1, t.keyCount() ); + + t.set( "three", LuaValue.NIL ); + assertEquals( 0, t.keyCount() ); + } + + public void testInOrderlen() { + LuaTable t = new_Table(); + + for ( int i = 1; i <= 32; ++i ) { + LuaValue v = LuaString.valueOf( "Test Value! "+i ); + t.set( i, v ); + assertEquals( i, t.length() ); + assertEquals( i, t.maxn() ); + } + } + + public void testOutOfOrderlen() { + LuaTable t = new_Table(); + + for ( int j=8; j<32; j+=8 ) { + for ( int i = j; i > 0; --i ) { + t.set( i, LuaString.valueOf( "Test Value! "+i ) ); + } + assertEquals( j, t.length() ); + assertEquals( j, t.maxn() ); + } + } + + public void testStringKeyslen() { + LuaTable t = new_Table(); + + for ( int i = 1; i <= 32; ++i ) { + t.set( "str-"+i, LuaString.valueOf( "String Key Test Value! "+i ) ); + assertEquals( 0, t.length() ); + assertEquals( 0, t.maxn() ); + } + } + + public void testMixedKeyslen() { + LuaTable t = new_Table(); + + for ( int i = 1; i <= 32; ++i ) { + t.set( "str-"+i, LuaString.valueOf( "String Key Test Value! "+i ) ); + t.set( i, LuaString.valueOf( "Int Key Test Value! "+i ) ); + assertEquals( i, t.length() ); + assertEquals( i, t.maxn() ); + } + } + + private static final void compareLists(LuaTable t,Vector v) { + int n = v.size(); + assertEquals(v.size(),t.length()); + for ( int j=0; j= 32 ); + assertTrue( t.arrayCapacity() <= 64 ); + + } + + public void testRekeyCount() { + LuaTable t = new_Table(); + + // NOTE: This order of insertion is important. + t.set(3, LuaInteger.valueOf(3)); + t.set(1, LuaInteger.valueOf(1)); + t.set(5, LuaInteger.valueOf(5)); + t.set(4, LuaInteger.valueOf(4)); + t.set(6, LuaInteger.valueOf(6)); + t.set(2, LuaInteger.valueOf(2)); + + for ( int i = 1; i < 6; ++i ) { + assertEquals(LuaInteger.valueOf(i), t.get(i)); + } + + assertTrue( t.arrayCapacity() >= 0 && t.arrayCapacity() <= 2 ); + assertTrue( t.hashCapacity() >= 4 ); + } + + public void testOutOfOrderIntegerKeyInsertion() { + LuaTable t = new_Table(); + + for ( int i = 32; i > 0; --i ) { + t.set( i, LuaValue.valueOf( "Test Value! "+i ) ); + } + + // Ensure all keys are still there. + for ( int i = 1; i <= 32; ++i ) { + assertEquals( "Test Value! "+i, t.get( i ).toString() ); + } + + // Ensure capacities make sense + assertTrue( t.arrayCapacity() >= 0 ); + assertTrue( t.arrayCapacity() <= 6 ); + + assertTrue( t.hashCapacity() >= 16 ); + assertTrue( t.hashCapacity() <= 64 ); + + } + + public void testStringAndIntegerKeys() { + LuaTable t = new_Table(); + + for ( int i = 0; i < 10; ++i ) { + LuaString str = LuaValue.valueOf( String.valueOf( i ) ); + t.set( i, str ); + t.set( str, LuaInteger.valueOf( i ) ); + } + + assertTrue( t.arrayCapacity() >= 9 ); // 1, 2, ..., 9 + assertTrue( t.arrayCapacity() <= 18 ); + assertTrue( t.hashCapacity() >= 11 ); // 0, "0", "1", ..., "9" + assertTrue( t.hashCapacity() <= 33 ); + + LuaValue[] keys = t.keys(); + + int intKeys = 0; + int stringKeys = 0; + + assertEquals( 20, keys.length ); + for ( int i = 0; i < keys.length; ++i ) { + LuaValue k = keys[i]; + + if ( k instanceof LuaInteger ) { + final int ik = k.toint(); + assertTrue( ik >= 0 && ik < 10 ); + final int mask = 1 << ik; + assertTrue( ( intKeys & mask ) == 0 ); + intKeys |= mask; + } else if ( k instanceof LuaString ) { + final int ik = Integer.parseInt( k.strvalue().toString() ); + assertEquals( String.valueOf( ik ), k.strvalue().toString() ); + assertTrue( ik >= 0 && ik < 10 ); + final int mask = 1 << ik; + assertTrue( "Key \""+ik+"\" found more than once", ( stringKeys & mask ) == 0 ); + stringKeys |= mask; + } else { + fail( "Unexpected type of key found" ); + } + } + + assertEquals( 0x03FF, intKeys ); + assertEquals( 0x03FF, stringKeys ); + } + + public void testBadInitialCapacity() { + LuaTable t = new_Table(0, 1); + + t.set( "test", LuaValue.valueOf("foo") ); + t.set( "explode", LuaValue.valueOf("explode") ); + assertEquals( 2, t.keyCount() ); + } + + public void testRemove0() { + LuaTable t = new_Table(2, 0); + + t.set( 1, LuaValue.valueOf("foo") ); + t.set( 2, LuaValue.valueOf("bah") ); + assertNotSame(LuaValue.NIL, t.get(1)); + assertNotSame(LuaValue.NIL, t.get(2)); + assertEquals(LuaValue.NIL, t.get(3)); + + t.set( 1, LuaValue.NIL ); + t.set( 2, LuaValue.NIL ); + t.set( 3, LuaValue.NIL ); + assertEquals(LuaValue.NIL, t.get(1)); + assertEquals(LuaValue.NIL, t.get(2)); + assertEquals(LuaValue.NIL, t.get(3)); + } + + public void testRemove1() { + LuaTable t = new_Table(0, 1); + + t.set( "test", LuaValue.valueOf("foo") ); + t.set( "explode", LuaValue.NIL ); + t.set( 42, LuaValue.NIL ); + t.set( new_Table(), LuaValue.NIL ); + t.set( "test", LuaValue.NIL ); + assertEquals( 0, t.keyCount() ); + + t.set( 10, LuaInteger.valueOf( 5 ) ); + t.set( 10, LuaValue.NIL ); + assertEquals( 0, t.keyCount() ); + } + + public void testRemove2() { + LuaTable t = new_Table(0, 1); + + t.set( "test", LuaValue.valueOf("foo") ); + t.set( "string", LuaInteger.valueOf( 10 ) ); + assertEquals( 2, t.keyCount() ); + + t.set( "string", LuaValue.NIL ); + t.set( "three", LuaValue.valueOf( 3.14 ) ); + assertEquals( 2, t.keyCount() ); + + t.set( "test", LuaValue.NIL ); + assertEquals( 1, t.keyCount() ); + + t.set( 10, LuaInteger.valueOf( 5 ) ); + assertEquals( 2, t.keyCount() ); + + t.set( 10, LuaValue.NIL ); + assertEquals( 1, t.keyCount() ); + + t.set( "three", LuaValue.NIL ); + assertEquals( 0, t.keyCount() ); + } + + public void testInOrderLuaLength() { + LuaTable t = new_Table(); + + for ( int i = 1; i <= 32; ++i ) { + t.set( i, LuaValue.valueOf( "Test Value! "+i ) ); + assertEquals( i, t.length() ); + assertEquals( i, t.maxn() ); + } + } + + public void testOutOfOrderLuaLength() { + LuaTable t = new_Table(); + + for ( int j=8; j<32; j+=8 ) { + for ( int i = j; i > 0; --i ) { + t.set( i, LuaValue.valueOf( "Test Value! "+i ) ); + } + assertEquals( j, t.length() ); + assertEquals( j, t.maxn() ); + } + } + + public void testStringKeysLuaLength() { + LuaTable t = new_Table(); + + for ( int i = 1; i <= 32; ++i ) { + t.set( "str-"+i, LuaValue.valueOf( "String Key Test Value! "+i ) ); + assertEquals( 0, t.length() ); + assertEquals( 0, t.maxn() ); + } + } + + public void testMixedKeysLuaLength() { + LuaTable t = new_Table(); + + for ( int i = 1; i <= 32; ++i ) { + t.set( "str-"+i, LuaValue.valueOf( "String Key Test Value! "+i ) ); + t.set( i, LuaValue.valueOf( "Int Key Test Value! "+i ) ); + assertEquals( i, t.length() ); + assertEquals( i, t.maxn() ); + } + } + + private static final void compareLists(LuaTable t,Vector v) { + int n = v.size(); + assertEquals(v.size(),t.length()); + for ( int j=0; j4., ia.gt(ib).toboolean()); + assertEquals(.25>.5, da.gt(db).toboolean()); + assertEquals(3.>4., ia.gt_b(ib)); + assertEquals(.25>.5, da.gt_b(db)); + + // unlike kinds + assertEquals(3.>.25, ia.gt(da).toboolean()); + assertEquals(.25>3., da.gt(ia).toboolean()); + assertEquals(3.>.25, ia.gt_b(da)); + assertEquals(.25>3., da.gt_b(ia)); + } + + public void testGtEq() { + LuaValue ia=LuaValue.valueOf(3), ib=LuaValue.valueOf(4); + LuaValue da=LuaValue.valueOf(.25), db=LuaValue.valueOf(.5); + + // like kinds + assertEquals(3.>=4., ia.gteq(ib).toboolean()); + assertEquals(.25>=.5, da.gteq(db).toboolean()); + assertEquals(3.>=4., ia.gteq_b(ib)); + assertEquals(.25>=.5, da.gteq_b(db)); + + // unlike kinds + assertEquals(3.>=.25, ia.gteq(da).toboolean()); + assertEquals(.25>=3., da.gteq(ia).toboolean()); + assertEquals(3.>=.25, ia.gteq_b(da)); + assertEquals(.25>=3., da.gteq_b(ia)); + } + + public void testNotEq() { + LuaValue ia=LuaValue.valueOf(3), ib=LuaValue.valueOf(4); + LuaValue da=LuaValue.valueOf(.25), db=LuaValue.valueOf(.5); + LuaValue sa=LuaValue.valueOf("1.5"), sb=LuaValue.valueOf("2.0"); + + // like kinds + assertEquals(3.!=4., ia.neq(ib).toboolean()); + assertEquals(.25!=.5, da.neq(db).toboolean()); + assertEquals(1.5!=2., sa.neq(sb).toboolean()); + assertEquals(3.!=4., ia.neq_b(ib)); + assertEquals(.25!=.5, da.neq_b(db)); + assertEquals(1.5!=2., sa.neq_b(sb)); + + // unlike kinds + assertEquals(3.!=.25, ia.neq(da).toboolean()); + assertEquals(.25!=3., da.neq(ia).toboolean()); + assertEquals(3.!=1.5, ia.neq(sa).toboolean()); + assertEquals(1.5!=3., sa.neq(ia).toboolean()); + assertEquals(.25!=1.5, da.neq(sa).toboolean()); + assertEquals(1.5!=.25, sa.neq(da).toboolean()); + assertEquals(3.!=.25, ia.neq_b(da)); + assertEquals(.25!=3., da.neq_b(ia)); + assertEquals(3.!=1.5, ia.neq_b(sa)); + assertEquals(1.5!=3., sa.neq_b(ia)); + assertEquals(.25!=1.5, da.neq_b(sa)); + assertEquals(1.5!=.25, sa.neq_b(da)); + } + + public void testAnd() { + LuaValue ia=LuaValue.valueOf(3), ib=LuaValue.valueOf(4); + LuaValue da=LuaValue.valueOf(.25), db=LuaValue.valueOf(.5); + LuaValue sa=LuaValue.valueOf("1.5"), sb=LuaValue.valueOf("2.0"); + LuaValue ba=LuaValue.TRUE, bb=LuaValue.FALSE; + + // like kinds + assertSame(ib, ia.and(ib)); + assertSame(db, da.and(db)); + assertSame(sb, sa.and(sb)); + + // unlike kinds + assertSame(da, ia.and(da)); + assertSame(ia, da.and(ia)); + assertSame(sa, ia.and(sa)); + assertSame(ia, sa.and(ia)); + assertSame(sa, da.and(sa)); + assertSame(da, sa.and(da)); + + // boolean values + assertSame(bb, ba.and(bb)); + assertSame(bb, bb.and(ba)); + assertSame(ia, ba.and(ia)); + assertSame(bb, bb.and(ia)); + } + + + public void testOr() { + LuaValue ia=LuaValue.valueOf(3), ib=LuaValue.valueOf(4); + LuaValue da=LuaValue.valueOf(.25), db=LuaValue.valueOf(.5); + LuaValue sa=LuaValue.valueOf("1.5"), sb=LuaValue.valueOf("2.0"); + LuaValue ba=LuaValue.TRUE, bb=LuaValue.FALSE; + + // like kinds + assertSame(ia, ia.or(ib)); + assertSame(da, da.or(db)); + assertSame(sa, sa.or(sb)); + + // unlike kinds + assertSame(ia, ia.or(da)); + assertSame(da, da.or(ia)); + assertSame(ia, ia.or(sa)); + assertSame(sa, sa.or(ia)); + assertSame(da, da.or(sa)); + assertSame(sa, sa.or(da)); + + // boolean values + assertSame(ba, ba.or(bb)); + assertSame(ba, bb.or(ba)); + assertSame(ba, ba.or(ia)); + assertSame(ia, bb.or(ia)); + } + + public void testLexicalComparison() { + LuaValue aaa = LuaValue.valueOf("aaa"); + LuaValue baa = LuaValue.valueOf("baa"); + LuaValue Aaa = LuaValue.valueOf("Aaa"); + LuaValue aba = LuaValue.valueOf("aba"); + LuaValue aaaa = LuaValue.valueOf("aaaa"); + LuaValue t = LuaValue.TRUE; + LuaValue f = LuaValue.FALSE; + + // basics + assertEquals(t, aaa.eq(aaa)); + assertEquals(t, aaa.lt(baa)); + assertEquals(t, aaa.lteq(baa)); + assertEquals(f, aaa.gt(baa)); + assertEquals(f, aaa.gteq(baa)); + assertEquals(f, baa.lt(aaa)); + assertEquals(f, baa.lteq(aaa)); + assertEquals(t, baa.gt(aaa)); + assertEquals(t, baa.gteq(aaa)); + assertEquals(t, aaa.lteq(aaa)); + assertEquals(t, aaa.gteq(aaa)); + + // different case + assertEquals(t, Aaa.eq(Aaa)); + assertEquals(t, Aaa.lt(aaa)); + assertEquals(t, Aaa.lteq(aaa)); + assertEquals(f, Aaa.gt(aaa)); + assertEquals(f, Aaa.gteq(aaa)); + assertEquals(f, aaa.lt(Aaa)); + assertEquals(f, aaa.lteq(Aaa)); + assertEquals(t, aaa.gt(Aaa)); + assertEquals(t, aaa.gteq(Aaa)); + assertEquals(t, Aaa.lteq(Aaa)); + assertEquals(t, Aaa.gteq(Aaa)); + + // second letter differs + assertEquals(t, aaa.eq(aaa)); + assertEquals(t, aaa.lt(aba)); + assertEquals(t, aaa.lteq(aba)); + assertEquals(f, aaa.gt(aba)); + assertEquals(f, aaa.gteq(aba)); + assertEquals(f, aba.lt(aaa)); + assertEquals(f, aba.lteq(aaa)); + assertEquals(t, aba.gt(aaa)); + assertEquals(t, aba.gteq(aaa)); + assertEquals(t, aaa.lteq(aaa)); + assertEquals(t, aaa.gteq(aaa)); + + // longer + assertEquals(t, aaa.eq(aaa)); + assertEquals(t, aaa.lt(aaaa)); + assertEquals(t, aaa.lteq(aaaa)); + assertEquals(f, aaa.gt(aaaa)); + assertEquals(f, aaa.gteq(aaaa)); + assertEquals(f, aaaa.lt(aaa)); + assertEquals(f, aaaa.lteq(aaa)); + assertEquals(t, aaaa.gt(aaa)); + assertEquals(t, aaaa.gteq(aaa)); + assertEquals(t, aaa.lteq(aaa)); + assertEquals(t, aaa.gteq(aaa)); + } +} diff --git a/test/junit/org/luaj/vm2/WeakTableTest.java b/test/junit/org/luaj/vm2/WeakTableTest.java new file mode 100644 index 00000000..27965de1 --- /dev/null +++ b/test/junit/org/luaj/vm2/WeakTableTest.java @@ -0,0 +1,188 @@ +/******************************************************************************* + * Copyright (c) 2009 Luaj.org. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + ******************************************************************************/ +package org.luaj.vm2; + +import java.lang.ref.WeakReference; +import java.util.Random; + +public class WeakTableTest extends TableTest { + + protected LuaTable new_Table() { return new WeakTable(false, true); } + protected LuaTable new_Table(int n,int m) { return new WeakTable(false, true); } + + public static class WeakKeyTableTest extends TableTest { + protected LuaTable new_Table() { return new WeakTable(true, false); } + protected LuaTable new_Table(int n,int m) { return new WeakTable(true, false); } + } + + public static class WeakKeyValueTableTest extends TableTest { + protected LuaTable new_Table() { return new WeakTable(true, true); } + protected LuaTable new_Table(int n,int m) { return new WeakTable(true, true); } + } + + + public void testWeakValuesTable() { + LuaTable t = new_Table(); + + Object obj = new Object(); + LuaTable tableValue = new LuaTable(); + LuaString stringValue = LuaString.valueOf("this is a test"); + + t.set("table", tableValue); + t.set("userdata", LuaValue.userdataOf(obj, null)); + t.set("string", stringValue); + t.set("string2", LuaString.valueOf("another string")); + assertTrue("table must have at least 4 elements", t.hashKeys.length > 4); + + // check that table can be used to get elements + assertEquals(tableValue, t.get("table")); + assertEquals(stringValue, t.get("string")); + assertEquals(obj, t.get("userdata").checkuserdata()); + + // nothing should be collected, since we have strong references here + System.gc(); + + // check that elements are still there + assertEquals(tableValue, t.get("table")); + assertEquals(stringValue, t.get("string")); + assertEquals(obj, t.get("userdata").checkuserdata()); + + // drop our strong references + obj = null; + tableValue = null; + stringValue = null; + + // Garbage collection should cause weak entries to be dropped. + System.gc(); + + // check that they are dropped + assertEquals(LuaValue.NIL, t.get("table")); + assertEquals(LuaValue.NIL, t.get("userdata")); + assertFalse("strings should not be in weak references", t.get("string").isnil()); + } + + public static class MyData { + public final int value; + public MyData( int value ) { + this.value = value; + } + public int hashCode() { + return value; + } + public boolean equals( Object o ) { + return (o instanceof MyData) && ((MyData)o).value == value; + } + public String toSting() { + return "mydata-"+value; + } + } + + public void testWeakKeysTable() { + LuaTable t = new WeakTable(true, false); + + LuaValue key = LuaValue.userdataOf(new MyData(111)); + LuaValue val = LuaValue.userdataOf(new MyData(222)); + + // set up the table + t.set( key, val ); + assertEquals( val, t.get(key) ); + System.gc(); + assertEquals( val, t.get(key) ); + + // drop key and value references, replace them with new ones + WeakReference origkey = new WeakReference(key); + WeakReference origval = new WeakReference(val); + key = LuaValue.userdataOf(new MyData(111)); + val = LuaValue.userdataOf(new MyData(222)); + + // new key and value should be interchangeable (feature of this test class + assertEquals( key, origkey.get() ); + assertEquals( val, origval.get() ); + assertEquals( val, t.get(key) ); + assertEquals( val, t.get((LuaValue) origkey.get()) ); + assertEquals( origval.get(), t.get(key) ); + + + // value should not be reachable after gc + System.gc(); + assertEquals( null, origkey.get() ); + assertEquals( LuaValue.NIL, t.get(key) ); + + // value should also be gone after gc after access! + System.gc(); + assertEquals( null, origkey.get() ); + assertEquals( null, origval.get() ); + } + + public void testWeakKeysValuesTable() { + LuaTable t = new WeakTable(true, true); + + LuaValue key = LuaValue.userdataOf(new MyData(111)); + LuaValue val = LuaValue.userdataOf(new MyData(222)); + LuaValue key2 = LuaValue.userdataOf(new MyData(333)); + LuaValue val2 = LuaValue.userdataOf(new MyData(444)); + LuaValue key3 = LuaValue.userdataOf(new MyData(555)); + LuaValue val3 = LuaValue.userdataOf(new MyData(666)); + + // set up the table + t.set( key, val ); + t.set( key2, val2 ); + t.set( key3, val3 ); + assertEquals( val, t.get(key) ); + assertEquals( val2, t.get(key2) ); + assertEquals( val3, t.get(key3) ); + System.gc(); + assertEquals( val, t.get(key) ); + assertEquals( val2, t.get(key2) ); + assertEquals( val3, t.get(key3) ); + + // drop key and value references, replace them with new ones + WeakReference origkey = new WeakReference(key); + WeakReference origval = new WeakReference(val); + WeakReference origkey2 = new WeakReference(key2); + WeakReference origval2 = new WeakReference(val2); + WeakReference origkey3 = new WeakReference(key3); + WeakReference origval3 = new WeakReference(val3); + key = LuaValue.userdataOf(new MyData(111)); + val = LuaValue.userdataOf(new MyData(222)); + key2 = LuaValue.userdataOf(new MyData(333)); + // don't drop val2, or key3 + val3 = LuaValue.userdataOf(new MyData(666)); + + // no values should be reachable after gc + System.gc(); + assertEquals( null, origkey.get() ); + assertEquals( null, origval.get() ); + assertEquals( null, origkey2.get() ); + assertEquals( null, origval3.get() ); + assertEquals( LuaValue.NIL, t.get(key) ); + assertEquals( LuaValue.NIL, t.get(key2) ); + assertEquals( LuaValue.NIL, t.get(key3) ); + + // all originals should be gone after gc, then access + val2 = null; + key3 = null; + System.gc(); + assertEquals( null, origval2.get() ); + assertEquals( null, origkey3.get() ); +} +} diff --git a/test/lua/baselib.lua b/test/lua/baselib.lua new file mode 100644 index 00000000..2b99756a --- /dev/null +++ b/test/lua/baselib.lua @@ -0,0 +1,288 @@ + +-- tostring replacement that assigns ids +local ts,id,nid,types = tostring,{},0,{table='tbl',thread='thr',userdata='uda',['function']='func'} +tostring = function(x) + if not x or not types[type(x)] then return ts(x) end + if not id[x] then nid=nid+1; id[x]=types[type(x)]..'.'..nid end + return id[x] +end + +-- wrap pcall to return one result +-- error message are tested elsewhere +local pc = pcall +local pcall = function(...) + local s,e = pc(...) + if s then return e end + return false, type(e) +end + +-- print +print() +print(11) +print("abc",123,nil,"pqr") +print( nil and 'T' or 'F' ) +print( false and 'T' or 'F' ) +print( 0 and 'T' or 'F' ) + +-- assert +print( 'assert(true)', assert(true) ) +print( 'pcall(assert,true)', pcall(assert,true) ) +print( 'pcall(assert,false)', pcall(assert,false) ) +print( 'pcall(assert,nil)', pcall(assert,nil) ) +print( 'pcall(assert,true,"msg")', pcall(assert,true,"msg") ) +print( 'pcall(assert,false,"msg")', pcall(assert,false,"msg") ) +print( 'pcall(assert,nil,"msg")', pcall(assert,nil,"msg") ) +print( 'pcall(assert,false,"msg","msg2")', pcall(assert,false,"msg","msg2") ) + +-- collectgarbage (not supported) +print( 'collectgarbage("count")', type(collectgarbage("count"))) +print( 'collectgarbage("collect")', type(collectgarbage("collect"))) +print( 'collectgarbage("count")', type(collectgarbage("count"))) + +-- dofile (not supported) +-- ipairs +print( 'pcall(ipairs)', pcall(ipairs) ) +print( 'pcall(ipairs,nil)', pcall(ipairs,nil) ) +print( 'pcall(ipairs,"a")', pcall(ipairs,"a") ) +print( 'pcall(ipairs,1)', pcall(ipairs,1) ) +for k,v in ipairs({}) do print('ipairs1',k,v)end +for k,v in ipairs({'one','two'}) do print('ipairs2',k,v)end +for k,v in ipairs({aa='aaa',bb='bbb'}) do print('ipairs3',k,v)end +for k,v in ipairs({aa='aaa',bb='bbb','one','two'}) do print('ipairs4',k,v)end +for k,v in ipairs({[30]='30',[20]='20'}) do print('ipairs5',k,v)end + +-- load +-- loadfile +-- loadstring +local lst = "print(3+4); return 8" +local chunk, err = loadstring( lst ) +print( 'loadstring("'..lst..'")', chunk, err ) +print( 'loadstring("'..lst..'")()', chunk() ) + +-- pairs +print( 'pcall(pairs)', pcall(pairs) ) +print( 'pcall(pairs,nil)', pcall(pairs,nil) ) +print( 'pcall(pairs,"a")', pcall(pairs,"a") ) +print( 'pcall(pairs,1)', pcall(pairs,1) ) +for k,v in pairs({}) do print('pairs1',k,v)end +for k,v in pairs({'one','two'}) do print('pairs2',k,v)end +for k,v in pairs({aa='aaa',bb='bbb'}) do print('pairs3',k,v)end +for k,v in pairs({aa='aaa',bb='bbb','one','two'}) do print('pairs4',k,v)end +for k,v in pairs({[20]='30',[30]='20'}) do print('pairs5',k,v)end + +-- _G +print( '_G["abc"] (before)', _G["abc"] ) +abc='def' +print( '_G["abc"] (after)', _G["abc"] ) + +-- type +print( 'type(nil)', type(nil) ) +print( 'type("a")', type("a") ) +print( 'type(1)', type(1) ) +print( 'type(1.5)', type(1.5) ) +print( 'type(function() end)', type(function() end) ) +print( 'type({})', type({}) ) +print( 'type(true)', type(true) ) +print( 'type(false)', type(false) ) +print( 'pcall(type,type)', pcall(type,type) ) +print( 'pcall(type)', pcall(type) ) +print( '(function() return pcall(type) end)()', (function() return pcall(type) end)() ) +local function la() return pcall(type) end +print( 'la()', la() ) +function ga() return pcall(type) end +print( 'ga()', ga() ) + +-- getfenv, setfenv: tested in setfenv.lua +-- getmetatable, setmetatable +ta = { aa1="aaa1", aa2="aaa2" } +tb = { bb1="bbb1", bb2="bbb2" } +print( 'getmetatable(ta)', getmetatable(ta) ) +print( 'getmetatable(tb)', getmetatable(tb) ) +print( 'setmetatable(ta),{cc1="ccc1"}', type( setmetatable(ta,{cc1="ccc1"}) ) ) +print( 'setmetatable(tb),{dd1="ddd1"}', type( setmetatable(tb,{dd1="ddd1"}) ) ) +print( 'getmetatable(ta)["cc1"]', getmetatable(ta)["cc1"] ) +print( 'getmetatable(tb)["dd1"]', getmetatable(tb)["dd1"] ) +print( 'getmetatable(1)', getmetatable(1) ) +print( 'pcall(setmetatable,1)', pcall(setmetatable,1) ) +print( 'pcall(setmetatable,nil)', pcall(setmetatable,nil) ) +print( 'pcall(setmetatable,"ABC")', pcall(setmetatable,"ABC") ) +print( 'pcall(setmetatable,function() end)', pcall(setmetatable,function() end) ) + +-- rawget,rawset +local mt = {aa="aaa", bb="bbb"} +mt.__index = mt +mt.__newindex = mt +local s = {cc="ccc", dd="ddd", } +local t = {cc="ccc", dd="ddd"} +setmetatable(t,mt) +print( 'pcall(rawget)', pcall(rawget)) +print( 'pcall(rawget,"a")', pcall(rawget,"a")) +print( 'pcall(rawget,s)', pcall(rawget,s)) +print( 'pcall(rawget,t)', pcall(rawget,t)) + +function printtables() + function printtable(name,t) + print( ' '..name, t["aa"], t["bb"], t["cc"], t["dd"], t["ee"], t["ff"], t["gg"] ) + print( ' '..name, + rawget(t,"aa"), + rawget(t,"bb"), + rawget(t,"cc"), + rawget(t,"dd"), + rawget(t,"ee"), + rawget(t,"ff"), + rawget(t,"gg") ) + end + printtable( 's', s ) + printtable( 't', t ) + printtable( 'mt', mt ) +end +printtables() +print( 'pcall(rawset,s,"aa","www")', rawset(s,"aa","www")) +printtables() +print( 'pcall(rawset,s,"cc","xxx")', rawset(s,"cc","xxx")) +printtables() +print( 'pcall(rawset,t,"aa","yyy")', rawset(t,"aa","yyy")) +printtables() +print( 'pcall(rawset,t,"dd","zzz")', rawset(t,"dd","zzz")) +printtables() + +printtables() +print( 's["ee"]="ppp"' ); s["ee"]="ppp" +printtables() +print( 's["cc"]="qqq"' ); s["cc"]="qqq" +printtables() +print( 't["ff"]="rrr"' ); t["ff"]="rrr" +printtables() +print( 't["dd"]="sss"' ); t["dd"]="sss" +printtables() +print( 'mt["gg"]="ttt"' ); mt["gg"]="ttt" +printtables() + + +-- select +print( 'pcall(select)', pcall(select) ) +print( 'select(1,11,22,33,44,55)', select(1,11,22,33,44,55) ) +print( 'select(2,11,22,33,44,55)', select(2,11,22,33,44,55) ) +print( 'select(3,11,22,33,44,55)', select(3,11,22,33,44,55) ) +print( 'select(4,11,22,33,44,55)', select(4,11,22,33,44,55) ) +print( 'pcall(select,5,11,22,33,44,55)', pcall(select,5,11,22,33,44,55) ) +print( 'pcall(select,6,11,22,33,44,55)', pcall(select,6,11,22,33,44,55) ) +print( 'pcall(select,7,11,22,33,44,55)', pcall(select,7,11,22,33,44,55) ) +print( 'pcall(select,0,11,22,33,44,55)', pcall(select,0,11,22,33,44,55) ) +print( 'pcall(select,-1,11,22,33,44,55)', pcall(select,-1,11,22,33,44,55) ) +print( 'pcall(select,-2,11,22,33,44,55)', pcall(select,-2,11,22,33,44,55) ) +print( 'pcall(select,-4,11,22,33,44,55)', pcall(select,-4,11,22,33,44,55) ) +print( 'pcall(select,-5,11,22,33,44,55)', pcall(select,-5,11,22,33,44,55) ) +print( 'pcall(select,-6,11,22,33,44,55)', pcall(select,-6,11,22,33,44,55) ) +print( 'pcall(select,1)', pcall(select,1) ) +print( 'pcall(select,select)', pcall(select,select) ) +print( 'pcall(select,{})', pcall(select,{}) ) +print( 'pcall(select,"2",11,22,33)', pcall(select,"2",11,22,33) ) +print( 'pcall(select,"abc",11,22,33)', pcall(select,"abc",11,22,33) ) + + +-- tonumber +print( 'pcall(tonumber)', pcall(tostring) ) +print( 'pcall(tonumber,nil)', pcall(tonumber,nil) ) +print( 'pcall(tonumber,"abc")', pcall(tonumber,"abc") ) +print( 'pcall(tonumber,"123")', pcall(tonumber,"123") ) +print( 'pcall(tonumber,"123",10)', pcall(tonumber,"123", 10) ) +print( 'pcall(tonumber,"123",8)', pcall(tonumber,"123", 8) ) +print( 'pcall(tonumber,"123",6)', pcall(tonumber,"123", 6) ) +print( 'pcall(tonumber,"10101",4)', pcall(tonumber,"10101", 4) ) +print( 'pcall(tonumber,"10101",3)', pcall(tonumber,"10101", 3) ) +print( 'pcall(tonumber,"10101",2)', pcall(tonumber,"10101", 2) ) +print( 'pcall(tonumber,"1a1",16)', pcall(tonumber,"1a1", 16) ) +print( 'pcall(tonumber,"1a1",32)', pcall(tonumber,"1a1", 32) ) +print( 'pcall(tonumber,"1a1",54)', pcall(tonumber,"1a1", 54) ) +print( 'pcall(tonumber,"1a1",1)', pcall(tonumber,"1a1", 1) ) +print( 'pcall(tonumber,"1a1",0)', pcall(tonumber,"1a1", 0) ) +print( 'pcall(tonumber,"1a1",-1)', pcall(tonumber,"1a1", -1) ) +print( 'pcall(tonumber,"1a1","32")', pcall(tonumber,"1a1", "32") ) +print( 'pcall(tonumber,"123","456")', pcall(tonumber,"123","456") ) +print( 'pcall(tonumber,"1a1",10)', pcall(tonumber,"1a1", 10) ) +print( 'pcall(tonumber,"151",4)', pcall(tonumber,"151", 4) ) +print( 'pcall(tonumber,"151",3)', pcall(tonumber,"151", 3) ) +print( 'pcall(tonumber,"151",2)', pcall(tonumber,"151", 2) ) +print( 'pcall(tonumber,"123",8,8)', pcall(tonumber,"123", 8, 8) ) +print( 'pcall(tonumber,123)', pcall(tonumber,123) ) +print( 'pcall(tonumber,true)', pcall(tonumber,true) ) +print( 'pcall(tonumber,false)', pcall(tonumber,false) ) +print( 'pcall(tonumber,tonumber)', pcall(tonumber,tonumber) ) +print( 'pcall(tonumber,function() end)', pcall(tonumber,function() end) ) +print( 'pcall(tonumber,{"one","two",a="aa",b="bb"})', pcall(tonumber,{"one","two",a="aa",b="bb"}) ) +print( 'pcall(tonumber,"123.456")', pcall(tonumber,"123.456") ) +print( 'pcall(tonumber," 123.456")', pcall(tonumber," 123.456") ) +print( 'pcall(tonumber," 234qwer")', pcall(tonumber," 234qwer") ) +print( 'pcall(tonumber,"0x20")', pcall(tonumber,"0x20") ) +print( 'pcall(tonumber," 0x20")', pcall(tonumber," 0x20") ) +print( 'pcall(tonumber,"0x20 ")', pcall(tonumber,"0x20 ") ) +print( 'pcall(tonumber," 0x20 ")', pcall(tonumber," 0x20 ") ) +print( 'pcall(tonumber,"0X20")', pcall(tonumber,"0X20") ) +print( 'pcall(tonumber," 0X20")', pcall(tonumber," 0X20") ) +print( 'pcall(tonumber,"0X20 ")', pcall(tonumber,"0X20 ") ) +print( 'pcall(tonumber," 0X20 ")', pcall(tonumber," 0X20 ") ) +print( 'pcall(tonumber,"0x20",10)', pcall(tonumber,"0x20",10) ) +print( 'pcall(tonumber,"0x20",16)', pcall(tonumber,"0x20",16) ) +print( 'pcall(tonumber,"0x20",8)', pcall(tonumber,"0x20",8) ) + +-- tostring +print( 'pcall(tostring)', pcall(tostring) ) +print( 'pcall(tostring,nil)', pcall(tostring,nil) ) +print( 'pcall(tostring,"abc")', pcall(tostring,"abc") ) +print( 'pcall(tostring,"abc","def")', pcall(tostring,"abc","def") ) +print( 'pcall(tostring,123)', pcall(tostring,123) ) +print( 'pcall(tostring,true)', pcall(tostring,true) ) +print( 'pcall(tostring,false)', pcall(tostring,false) ) +print( 'tostring(tostring)', type(tostring(tostring)) ) +print( 'tostring(function() end)', type(tostring(function() end)) ) +print( 'tostring({"one","two",a="aa",b="bb"})', type(tostring({"one","two",a="aa",b="bb"})) ) + +-- unpack +print( 'pcall(unpack)', pcall(unpack) ); +print( 'pcall(unpack,nil)', pcall(unpack,nil) ); +print( 'pcall(unpack,"abc")', pcall(unpack,"abc") ); +print( 'pcall(unpack,1)', pcall(unpack,1) ); +print( 'unpack({"aa"})', unpack({"aa"}) ); +print( 'unpack({"aa","bb"})', unpack({"aa","bb"}) ); +print( 'unpack({"aa","bb","cc"})', unpack({"aa","bb","cc"}) ); +local t = {"aa","bb","cc","dd","ee","ff"} +print( 'pcall(unpack,t)', pcall(unpack,t) ); +print( 'pcall(unpack,t,2)', pcall(unpack,t,2) ); +print( 'pcall(unpack,t,2,5)', pcall(unpack,t,2,5) ); +print( 'pcall(unpack,t,2,6)', pcall(unpack,t,2,6) ); +print( 'pcall(unpack,t,2,7)', pcall(unpack,t,2,7) ); +print( 'pcall(unpack,t,1)', pcall(unpack,t,1) ); +print( 'pcall(unpack,t,1,5)', pcall(unpack,t,1,5) ); +print( 'pcall(unpack,t,1,6)', pcall(unpack,t,1,6) ); +print( 'pcall(unpack,t,1,7)', pcall(unpack,t,1,7) ); +print( 'pcall(unpack,t,0)', pcall(unpack,t,0) ); +print( 'pcall(unpack,t,0,5)', pcall(unpack,t,0,5) ); +print( 'pcall(unpack,t,0,6)', pcall(unpack,t,0,6) ); +print( 'pcall(unpack,t,0,7)', pcall(unpack,t,0,7) ); +print( 'pcall(unpack,t,-1)', pcall(unpack,t,-1) ); +print( 'pcall(unpack,t,-1,5)', pcall(unpack,t,-1,5) ); +print( 'pcall(unpack,t,-1,6)', pcall(unpack,t,-1,6) ); +print( 'pcall(unpack,t,-1,7)', pcall(unpack,t,-1,7) ); +print( 'pcall(unpack,t,2,4)', pcall(unpack,t,2,4) ); +print( 'pcall(unpack,t,2,5)', pcall(unpack,t,2,5) ); +print( 'pcall(unpack,t,2,6)', pcall(unpack,t,2,6) ); +print( 'pcall(unpack,t,2,7)', pcall(unpack,t,2,7) ); +print( 'pcall(unpack,t,2,8)', pcall(unpack,t,2,8) ); +print( 'pcall(unpack,t,2,2)', pcall(unpack,t,2,0) ); +print( 'pcall(unpack,t,2,1)', pcall(unpack,t,2,0) ); +print( 'pcall(unpack,t,2,0)', pcall(unpack,t,2,0) ); +print( 'pcall(unpack,t,2,-1)', pcall(unpack,t,2,-1) ); +t[0] = 'zz' +t[-1] = 'yy' +t[-2] = 'xx' +print( 'pcall(unpack,t,0)', pcall(unpack,t,0) ); +print( 'pcall(unpack,t,2,0)', pcall(unpack,t,2,0) ); +print( 'pcall(unpack,t,2,-1)', pcall(unpack,t,2,-1) ); +print( 'pcall(unpack,t,"3")', pcall(unpack,t,"3") ); +print( 'pcall(unpack,t,"a")', pcall(unpack,t,"a") ); +print( 'pcall(unpack,t,function() end)', pcall(unpack,t,function() end) ); + +-- _VERSION +print( '_VERSION', type(_VERSION) ) + diff --git a/test/lua/coroutinelib.lua b/test/lua/coroutinelib.lua new file mode 100644 index 00000000..7b92dfca --- /dev/null +++ b/test/lua/coroutinelib.lua @@ -0,0 +1,97 @@ +function printrunning() + if coroutine.running() == nil then + print("running is nil"); + else + print("running is not nil") + end +end + +function foo (a) + print("foo", a) + return coroutine.yield(2*a) +end + +co = coroutine.create(function (a,b) + print("co-body", a, b) + local r = foo(a+1) + print("co-body", r) + local r, s = coroutine.yield(a+b, a-b) + print("co-body", r, s) + + printrunning() + print("co.status.inside",coroutine.status(co)); + local co2 = coroutine.create(function() + print("co.status.inside2",coroutine.status(co)); + end) + print("co.status.inside",coroutine.status(co)); + coroutine.resume(co2); + + return b, "end" +end) + +function exercise() + printrunning() + print("co.status",coroutine.status(co)); + print("main", coroutine.resume(co, 1, 10)) + print("co.status",coroutine.status(co)); + print("main", coroutine.resume(co, "r")) + print("co.status",coroutine.status(co)); + print("main", coroutine.resume(co, "x", "y")) + print("co.status",coroutine.status(co)); + print("main", coroutine.resume(co, "x", "y")) + print("co.status",coroutine.status(co)); +end + +exercise(); + +co = coroutine.create(function (a,b) + print("co-body", a, b) +-- TODO: make java and C behave the same for yielding in pcalls +-- local status,r = pcall( foo, a+1 ) +foo(a+1) + print("co-body", status,r) + local r, s = coroutine.yield(a+b, a-b) + print("co-body", r, s) + return b, "end" +end) + +exercise(); + + +-- wrap test +local g = coroutine.wrap(function (a,b) + print("co-body", a, b) + local r = foo(a+1) + print("co-body", r) + local r, s = coroutine.yield(a+b, a-b) + print("co-body", r, s) + return b, "end" +end ) + +print("g", g(1, 10)) +print("g", g("r")) +print("g", g("x", "y")) +local s,e = pcall( g, "x", "y" ) +print("g", string.match(e,'cannot resume dead coroutine') or 'badmessage: '..tostring(e)) + +-- varargs passing +local echo = function(msg,...) + print( msg, ...) + return ... +end +local echocr = function(...) + echo('(echocr) first args', unpack(arg,1,arg.n)) + local a = arg + while true do + a = { echo( '(echoch) yield returns', coroutine.yield( unpack(a) ) ) } + end +end +local c = coroutine.create( echocr ) +local step = function(...) + echo( '(main) resume returns', + coroutine.resume(c, echo('(main) sending args', ...)) ) +end +step(111,222,333) +step() +step(111) +step(111,222,333) diff --git a/test/lua/debuglib.lua b/test/lua/debuglib.lua new file mode 100644 index 00000000..8fd18b0e --- /dev/null +++ b/test/lua/debuglib.lua @@ -0,0 +1,236 @@ + +local print,tostring,_G,pcall,ipairs,isnumber = print,tostring,_G,pcall,ipairs,isnumber +local e,f,g,h,s +print( 'has debug', debug~=nil ) +if not debug then error( 'no debug' ) end + +print( '----- debug.getfenv, debug.setfenv' ) +f = function(a) + return 'f:'..tostring(a)..'|'..tostring(b) +end +s,e,g = pcall( debug.getfenv, f ) +print( s, type(e), type(g), (e==G), pcall( f, 'abc' ) ) +s,e,g = pcall( debug.setfenv, f, {b='def'} ) +print( s, type(e), type(g), (e==G), pcall( f, 'abc' ) ) +s,e,g = pcall( debug.getfenv, f ) +print( s, type(e), type(g), (e==G), pcall( f, 'abc' ) ) + + +print( '----- debug.getlocal, debug.setlocal' ) +h = function(v,i,n) + s = 'h-'..v..'-'..i + local x1,y1 = debug.getlocal(v,i) + local x2,y2 = debug.setlocal(v,i,n) + local x3,y3 = debug.getlocal(v,i) + return s..' -> '..v..'-'..i..' '.. + 'get='..tostring(x1)..','..tostring(y1)..' '.. + 'set='..tostring(x2)..','..tostring(y2)..' '.. + 'get='..tostring(x3)..','..tostring(y3)..' ' +end +g = function(...) + local p,q,r=7,8,9 + local t = h(...) + local b = table.concat({...},',') + return t..'\tg locals='..p..','..q..','..r..' tbl={'..b..'}' +end +f = function(a,b,c) + local d,e,f = 4,5,6 + local t = g(a,b,c) + return t..'\tf locals='..','..a..','..b..','..c..','..d..','..e..','..f +end +for lvl=3,2,-1 do + for lcl=0,7 do + print( pcall( f, lvl, lcl, '#' ) ) + end +end +for lvl=1,1 do + for lcl=3,7 do + print( pcall( f, lvl, lcl, '#' ) ) + end +end + + +print( '----- debug.getupvalue, debug.setupvalue' ) +local m,n,o = 101,102,103 +f = function(p,q,r) + local p,q,r = 104,105,106 + local g = function(s,t,u) + local v,w,x = 107,108,109 + return function() + return m,n,o,p,q,r,v,w,x + end + end + return g +end +g = f() +h = g() +local callh = function() + local t = {} + for i,v in ipairs( { pcall(h) } ) do + t[i] = tostring(v) + end + return table.concat(t,',') +end +print( 'h', h() ) +local funs = { f, g, h } +local names = { 'f', 'g', 'h' } +for i=1,3 do + local fun,name = funs[i],names[i] + for index=0,10 do + local s1,x1,y1 = pcall( debug.getupvalue, fun, index ) + local s2,x2,y2 = pcall( debug.setupvalue, fun, index, 666000+i*111000+index ) + local s3,x3,y3 = pcall( debug.getupvalue, fun, index ) + print( name..' -> '..i..'-'..index..' '.. + 'get='..tostring(s1)..','..tostring(x1)..','..tostring(y1)..' '.. + 'set='..tostring(s2)..','..tostring(x2)..','..tostring(y2)..' '.. + 'get='..tostring(s3)..','..tostring(x3)..','..tostring(y3)..' '.. + 'tbl='..callh() ) + end +end + +print( '----- debug.setmetatable, debug.getmetatable' ) +local a = {a='bbb'} +local b = {} +local mt = {__index={b='ccc'}} +print( 'a.a='..tostring(a.a)..' a.b='..tostring(a.b)..' b.a='..tostring(b.a)..' b.b='..tostring(b.b)) +local s1,x1,y1 = pcall( debug.getmetatable, a ) +local s2,x2,y2 = pcall( debug.setmetatable, a, mt ) +print( 'a.a='..tostring(a.a)..' a.b='..tostring(a.b)..' b.a='..tostring(b.a)..' b.b='..tostring(b.b)) +local s3,x3,y3 = pcall( debug.getmetatable, a ) +local s4,x4,y4 = pcall( debug.getmetatable, b ) +local s5,x5,y5 = pcall( debug.setmetatable, a, nil ) +print( 'a.a='..tostring(a.a)..' a.b='..tostring(a.b)..' b.a='..tostring(b.a)..' b.b='..tostring(b.b)) +local s6,x6,y6 = pcall( debug.getmetatable, a ) +if not s1 then print( 's1 error', x1 ) end +if not s2 then print( 's2 error', x2 ) end +if not s3 then print( 's3 error', x3 ) end +if not s4 then print( 's4 error', x4 ) end +if not s5 then print( 's5 error', x5 ) end +if not s6 then print( 's6 error', x6 ) end +print( 'get='..tostring(s1)..','..tostring(x1==nil)..','..tostring(y1) ) +print( 'set='..tostring(s2)..','..tostring(x2==a)..','..tostring(y2) ) +print( 'get='..tostring(s3)..','..tostring(x3==mt)..','..tostring(y3) ) +print( 'get='..tostring(s4)..','..tostring(x4==nil)..','..tostring(y4) ) +print( 'set='..tostring(s5)..','..tostring(x5==a)..','..tostring(y5) ) +print( 'get='..tostring(s6)..','..tostring(x6==nil)..','..tostring(y6) ) +print( pcall( debug.getmetatable, 1 ) ) +-- print( pcall( debug.setmetatable, 1, {} ) ) +-- print( pcall( debug.setmetatable, 1, nil ) ) + +print( '----- debug.getinfo' ) +local printfield = function(tbl, field) + local x = tbl[field] + if x == nil then return end + local typ = type(x) + if typ=='table' then + x = '{'..table.concat(x,',')..'}' + elseif typ=='function' then + x = typ + elseif typ=='string' then + if field == 'source' then + x = x:sub(2) + end + end + print( ' '..field..': '..tostring(x) ) +end +local fields = { 'source', 'short_src', 'what', + 'currentline', 'linedefined', 'lastlinedefined', + 'nups', 'func', 'activelines' } +local printinfo = function(...) + for i,a in ipairs(arg) do + if type(a) == 'table' then + for j,field in ipairs(fields) do + printfield( a, field) + end + else + print( tostring(a) ) + end + end +end +function test() + local x = 5 + function f() + x = x + 1 + return x + end + function g() + x = x - 1 + print( '---' ) + printinfo( 'debug.getinfo(1)', debug.getinfo(1) ) + printinfo( 'debug.getinfo(1,"")', debug.getinfo(1, "") ) + printinfo( 'debug.getinfo(1,"l")', debug.getinfo(1, "l") ) + printinfo( 'debug.getinfo(1,"fL")', debug.getinfo(1, "fL") ) + printinfo( 'debug.getinfo(2)', debug.getinfo(2) ) + printinfo( 'debug.getinfo(2,"l")', debug.getinfo(2, "l") ) + printinfo( 'debug.getinfo(2,"fL")', debug.getinfo(2, "fL") ) + printinfo( 'debug.getinfo(10,"")', pcall( debug.getinfo, 10, "" ) ) + printinfo( 'debug.getinfo(-10,"")', pcall( debug.getinfo, -10, "" ) ) + --[[ + for i=1,3 do + printinfo( 'debug.traceback("msg")', debug.traceback('msg') ) + printinfo( 'debug.traceback("another",'..i..')', debug.traceback('another',i) ) + end + --]] + print( '---' ) + return x + end + print(f()) + print(g()) + return f, g +end + +local options = "nSlufL" +local e,f,g = pcall( test ) +print( 'e,f,g', e, type(f), type(g) ) +printinfo( 'debug.getinfo(f)', pcall(debug.getinfo, f) ) +printinfo( 'debug.getinfo(f,"'..options..'")', pcall(debug.getinfo, f, options) ) +for j=1,6 do + local opts = options:sub(j,j) + printinfo( 'debug.getinfo(f,"'..opts..'")', pcall(debug.getinfo, f, opts) ) +end +printinfo( 'debug.getinfo(g)', pcall(debug.getinfo, g) ) +printinfo( 'debug.getinfo(test)', pcall(debug.getinfo, test) ) + +print( '----- debug.sethook, debug.gethook' ) +f = function(x) + g = function(y) + return math.min(x,h) + end + local a = g(x) + return a + a +end +local hook = function(...) + print( ' ... in hook', ... ) + local info = debug.getinfo(2,"Sl") + if info then + print( ' info[2]='..tostring(info.short_src)..','..tostring(info.currentline) ) + end +end +local tryfunc = function(hook,mask,func,arg) + local x,f,h,m + pcall( function() + debug.sethook(hook,mask) + x = func(arg) + f,h,m = debug.gethook() + end ) + debug.sethook() + return x,f,h,m +end + +local tryhooks = function(mask) + local s1,a1,b1,c1,d1 = pcall( tryfunc, hook, mask, f, 333 ) + print( 'hook = '..mask..' -> '.. + 'result='..tostring(s1)..','..tostring(a1)..','.. + type(b1)..','..type(c1)..','.. + tostring(b1==f)..','..tostring(c1==hook)..','.. + tostring(d1)..' ' ) +end + +--[[ +tryhooks("c") +tryhooks("r") +tryhooks("l") +tryhooks("crl") +--]] + + \ No newline at end of file diff --git a/test/lua/errors.lua b/test/lua/errors.lua new file mode 100644 index 00000000..3ed06c2e --- /dev/null +++ b/test/lua/errors.lua @@ -0,0 +1,137 @@ +-- tostring replacement that assigns ids +local ts,id,nid,types = tostring,{},0,{table='tbl',thread='thr',userdata='uda',['function']='func'} +tostring = function(x) + if not x or not types[type(x)] then return ts(x) end + if not id[x] then nid=nid+1; id[x]=types[type(x)]..'.'..nid end + return id[x] +end + +-- test of common types of errors +-- local function c(f,...) return f(...) end +-- local function b(...) return c(...) end +--local function a(...) return (pcall(b,...)) end +local function a(...) local s,e=pcall(...) if s then return s,e else return false,type(e) end end +s = 'some string' +local t = {} +-- error message tests +print( 'a(error)', a(error) ) +print( 'a(error,"msg")', a(error,"msg") ) +print( 'a(error,"msg",0)', a(error,"msg",0) ) +print( 'a(error,"msg",1)', a(error,"msg",1) ) +print( 'a(error,"msg",2)', a(error,"msg",2) ) +print( 'a(error,"msg",3)', a(error,"msg",3) ) +print( 'a(error,"msg",4)', a(error,"msg",4) ) +print( 'a(error,"msg",5)', a(error,"msg",5) ) +print( 'a(error,"msg",6)', a(error,"msg",6) ) + +-- call errors +print( 'a(nil())', a(function() return n() end) ) +print( 'a(t()) ', a(function() return t() end) ) +print( 'a(s()) ', a(function() return s() end) ) +print( 'a(true())', a(function() local b = true; return b() end) ) + +-- arithmetic errors +print( 'a(nil+1)', a(function() return nil+1 end) ) +print( 'a(a+1) ', a(function() return a+1 end) ) +print( 'a(s+1) ', a(function() return s+1 end) ) +print( 'a(true+1)', a(function() local b = true; return b+1 end) ) + +-- table errors +print( 'a(nil.x)', a(function() return n.x end) ) +print( 'a(a.x) ', a(function() return a.x end) ) +print( 'a(s.x) ', a(function() return s.x end) ) +print( 'a(true.x)', a(function() local b = true; return b.x end) ) +print( 'a(nil.x=5)', a(function() n.x=5 end) ) +print( 'a(a.x=5) ', a(function() a.x=5 end) ) +print( 'a(s.x=5) ', a(function() s.x=5 end) ) +print( 'a(true.x=5)', a(function() local b = true; b.x=5 end) ) + +-- len operator +print( 'a(#nil) ', a(function() return #n end) ) +print( 'a(#t) ', a(function() return #t end) ) +print( 'a(#s) ', a(function() return #s end) ) +print( 'a(#a) ', a(function() return #a end) ) +print( 'a(#true)', a(function() local b = true; return #b end) ) + +-- comparison errors +print( 'a(nil>1)', a(function() return nil>1 end) ) +print( 'a(a>1) ', a(function() return a>1 end) ) +print( 'a(s>1) ', a(function() return s>1 end) ) +print( 'a(true>1)', a(function() local b = true; return b>1 end) ) + +-- unary minus errors +print( 'a(-nil)', a(function() return -n end) ) +print( 'a(-a) ', a(function() return -a end) ) +print( 'a(-s) ', a(function() return -s end) ) +print( 'a(-true)', a(function() local b = true; return -b end) ) + +-- string concatenation +local function concatsuite(comparefunc) + print( '"a".."b"', comparefunc("a","b") ) + print( '"a"..nil', comparefunc("a",nil) ) + print( 'nil.."b"', comparefunc(nil,"b") ) + print( '"a"..{}', comparefunc("a",{}) ) + print( '{}.."b"', comparefunc({},"b") ) + print( '"a"..2', comparefunc("a",2) ) + print( '2.."b"', comparefunc(2,"b") ) + print( '"a"..print', comparefunc("a",print) ) + print( 'print.."b"', comparefunc(print,"b") ) + print( '"a"..true', comparefunc("a",true) ) + print( 'true.."b"', comparefunc(true,"b") ) + print( 'nil..true', comparefunc(nil,true) ) + print( '"a"..3.5', comparefunc("a",3.5) ) + print( '3.5.."b"', comparefunc(3.5,"b") ) +end +local function strconcat(a,b) + return (pcall( function() return a..b end) ) +end +local function tblconcat(a,b) + local t={a,b} + return (pcall( function() return table.concat(t,'-',1,2) end )) +end + +print( '-------- string concatenation' ) +concatsuite(strconcat) +print( '-------- table concatenation' ) +concatsuite(tblconcat) + +-- pairs +print( '-------- pairs tests' ) +print( 'a(pairs(nil))', a(function() return pairs(nil,{}) end) ) +print( 'a(pairs(a)) ', a(function() return pairs(a,{}) end) ) +print( 'a(pairs(s)) ', a(function() return pairs(s,{}) end) ) +print( 'a(pairs(t)) ', a(function() return pairs(t,{}) end) ) +print( 'a(pairs(true))', a(function() local b = true; return pairs(b,{}) end) ) + +-- setmetatable +print( '-------- setmetatable tests' ) +function sm(...) + return tostring(setmetatable(...)) +end +print( 'a(setmetatable(nil))', a(function() return sm(nil,{}) end) ) +print( 'a(setmetatable(a)) ', a(function() return sm(a,{}) end) ) +print( 'a(setmetatable(s)) ', a(function() return sm(s,{}) end) ) +print( 'a(setmetatable(true))', a(function() local b = true; return sm(b,{}) end) ) +print( 'a(setmetatable(t)) ', a(function() return sm(t,{}) end) ) +print( 'a(getmetatable(t)) ', a(function() return getmetatable(t),type(getmetatable(t)) end) ) +print( 'a(setmetatable(t*)) ', a(function() return sm(t,{__metatable={}}) end) ) +print( 'a(getmetatable(t)) ', a(function() return getmetatable(t),type(getmetatable(t)) end) ) +print( 'a(setmetatable(t)) ', a(function() return sm(t,{}) end) ) +print( 'a(getmetatable(t)) ', a(function() return getmetatable(t),type(getmetatable(t)) end) ) +t = {} +print( 'a(setmetatable(t)) ', a(function() return sm(t,{}) end) ) +print( 'a(getmetatable(t)) ', a(function() return getmetatable(t),type(getmetatable(t)) end) ) +print( 'a(setmetatable(t*)) ', a(function() return sm(t,{__metatable='some string'}) end) ) +print( 'a(getmetatable(t)) ', a(function() return getmetatable(t),type(getmetatable(t)) end) ) +print( 'a(setmetatable(t)) ', a(function() return sm(t,{}) end) ) +print( 'a(getmetatable(t)) ', a(function() return getmetatable(t),type(getmetatable(t)) end) ) +print( 'a(setmetatable(t,nil)) ', a(function() return sm(t,nil) end) ) +print( 'a(setmetatable(t)) ', a(function() return sm(t) end) ) +print( 'a(setmetatable({},"abc")) ', a(function() return sm({},'abc') end) ) + +-- bad args to error! +print( 'error("msg","arg")', a(function() error('some message', 'some bad arg') end) ) + +-- loadfile, dofile on missing files +print( 'loadfile("bogus.txt")', a(function() return loadfile("bogus.txt") end) ) +print( 'dofile("bogus.txt")', a(function() return dofile("bogus.txt") end) ) diff --git a/test/lua/functions.lua b/test/lua/functions.lua new file mode 100644 index 00000000..e55f0357 --- /dev/null +++ b/test/lua/functions.lua @@ -0,0 +1,74 @@ + +function f0() print( "f0:" ) end +function f1(a) print( "f1:", a ) end +function f2(a,b) print( "f2:", a, b ) end +function f3(a,b,c) print( "f3:", a, b, c ) end +function f4(a,b,c,d) print( "f4:", a, b, c, d ) end + +f0() f0( "a1/1" ) f0( "a1/2", "a2/2" ) f0( "a1/3", "a2/3", "a3/3" ) f0( "a1/4", "a2/4", "a3/4", "a4/4" ) +f1() f1( "a1/1" ) f1( "a1/2", "a2/2" ) f1( "a1/3", "a2/3", "a3/3" ) f1( "a1/4", "a2/4", "a3/4", "a4/4" ) +f2() f2( "a1/1" ) f2( "a1/2", "a2/2" ) f2( "a1/3", "a2/3", "a3/3" ) f2( "a1/4", "a2/4", "a3/4", "a4/4" ) +f3() f3( "a1/1" ) f3( "a1/2", "a2/2" ) f3( "a1/3", "a2/3", "a3/3" ) f3( "a1/4", "a2/4", "a3/4", "a4/4" ) +f4() f4( "a1/1" ) f4( "a1/2", "a2/2" ) f4( "a1/3", "a2/3", "a3/3" ) f4( "a1/4", "a2/4", "a3/4", "a4/4" ) + +function g0(a,b,c,d) return end +function g1(a,b,c,d) return d end +function g2(a,b,c,d) return c, d end +function g3(a,b,c,d) return b, c, d end +function g4(a,b,c,d) return a, b, c, d end + +z = g0("c0.1/4", "c0.2/4", "c0.3/4", "c0.4/4") +print( "z0:", z ) +z = g2("c2.1/4", "c2.2/4", "c2.3/4", "c2.4/4") +print( "z2:", z ) +z = g4("c4.1/4", "c4.2/4", "c4.3/4", "c4.4/4") +print( "z4:", z ) + +a,b,c,d = g0( "c0.1/4", "c0.2/4", "c0.3/4", "c0.4/4" ) +print( "g0:", a, b, c, d, "(eol)" ) +a,b,c,d = g2( "b2.1/4", "b2.2/4", "b2.3/4", "b2.4/4" ) +print( "g2:", a, b, c, d, "(eol)" ) +a,b,c,d = g4( "b4.1/4", "b4.2/4", "b4.3/4", "b4.4/4" ) +print( "g4:", a, b, c, d, "(eol)" ) + +function func(a,b,c) + return a, b, c +end + +print( func(11, 12, 13) ) +print( func(23, 22, 21) ) +print( func(func(32,33,34), func(45,46,47), func(58,59,50)) ) + +function p(a,...) + print("a",a) + print("...",...) + print("...,a",...,a) + print("a,...",a,...) +end +p() +p("q") +p("q","r") +p("q","r","s") + +-- tail call tests +function first(...) + return 'abc', ..., '|', ... +end + +function second(a,...) + return 'def', ..., '|', a, ... +end + +function third( a, b, c ) + print( 'third', first( a, b, c ) ) + print( 'third', second( a, b, c ) ) + return second( a, b, c ) +end + +print( 'third', third() ) +print( 'third', third('p') ) +print( 'third', third('p','q') ) +print( 'third', third('p','q','r') ) +print( 'third', third('p','q','r','s') ) +print( 'third', third() ) + diff --git a/test/lua/iolib.lua b/test/lua/iolib.lua new file mode 100644 index 00000000..0edf27be --- /dev/null +++ b/test/lua/iolib.lua @@ -0,0 +1,137 @@ +local platform = ... +print( 'platform', platform ) + +-- simple io-library tests +-- +-- C version on Windows will add change \n into \r\n for text files at least +-- +print( io ~= nil ) +print( io.open ~= nil ) +print( io.stdin ~= nil ) +print( io.stdout ~= nil ) +print( io.stderr ~= nil ) +print( 'write', io.write() ) +print( 'write', io.write("This") ) +print( 'write', io.write(" is a pen.") ) +print( 'flush', io.flush() ) + +local f = io.open("abc.txt","w") +print( 'f', type(f) ) +print( io.type(f) ) +print( 'write', f:write("abcdef 12345 \t\t 678910 more\aaaaaaa\bbbbthe rest") ) +print( 'type(f)', io.type(f) ) +print( 'close', f:close() ) +print( 'type(f)', io.type(f) ) +print( 'type("f")', io.type("f") ) + +local g = io.open("abc.txt","r") +local t = { g:read(3, 3, "*n", "*n", "*l", "*l", "*a") } +for i,v in ipairs(t) do + print( string.format("%q",tostring(v)), type(v)) + print( '----- ', i ) +end + +local h,s = io.open("abc.txt", "a") +print( 'h', io.type(h), string.sub(tostring(h),1,6), s ) +print( 'write', h:write('and more and more and more text.') ) +print( 'close', h:close() ) + +if platform ~= 'JME' then + local j = io.open( "abc.txt", "r" ) + print( 'j', io.type(j) ) + print( 'seek', j:seek("set", 3) ) + print( 'read', j:read(4), j:read(3) ) + print( 'seek', j:seek("set", 2) ) + print( 'read', j:read(4), j:read(3) ) + print( 'seek', j:seek("cur", -8 ) ) + print( 'read', j:read(4), j:read(3) ) + print( 'seek(cur,0)', j:seek("cur",0) ) + print( 'seek(cur,20)', j:seek("cur",20) ) + print( 'seek(end,-5)', j:seek("end", -5) ) + print( 'read(4)', string.format("%q", tostring(j:read(4))) ) + print( 'read(4)', string.format("%q", tostring(j:read(4))) ) + print( 'read(4)', string.format("%q", tostring(j:read(4))) ) + print( 'close', j:close() ) +end + +-- write a few lines, including a non-terminating one +f = io.open("abc.txt","w") +print( 'f', io.type(f) ) +print( 'write', f:write("line one\nline two\n\nafter blank line\nunterminated line") ) +print( 'type(f)', io.type(f) ) +print( 'close', f:close() ) + +-- read using io.lines() +for l in io.lines("abc.txt") do + print( string.format('%q',l) ) +end +io.input("abc.txt") +for l in io.lines() do + print( string.format('%q',l) ) +end +io.input(io.open("abc.txt","r")) +for l in io.lines() do + print( string.format('%q',l) ) +end +io.input("abc.txt") +io.input(io.input()) +for l in io.lines() do + print( string.format('%q',l) ) +end + +local count = 0 +io.tmpfile = function() + count = count + 1 + return io.open("tmp"..count..".out","w") +end + +local a = io.tmpfile() +local b = io.tmpfile() +print( io.type(a) ) +print( io.type(b) ) +print( "a:write", a:write('aaaaaaa') ) +print( "b:write", b:write('bbbbbbb') ) +print( "a:setvbuf", a:setvbuf("no") ) +print( "a:setvbuf", a:setvbuf("full",1024) ) +print( "a:setvbuf", a:setvbuf("line") ) +print( "a:write", a:write('ccccc') ) +print( "b:write", b:write('ddddd') ) +print( "a:flush", a:flush() ) +print( "b:flush", b:flush() ) +--[[ +print( "a:read", a:read(7) ) +print( "b:read", b:read(7) ) +print( "a:seek", a:seek("cur",-4) ) +print( "b:seek", b:seek("cur",-4) ) +print( "a:read", ( a:read(7) ) ) +print( "b:read", ( b:read(7) ) ) +print( "a:seek", a:seek("cur",-8) ) +print( "b:seek", b:seek("cur",-8) ) +print( "a:read", ( a:read(7) ) ) +print( "b:read", ( b:read(7) ) ) +--]] + +local pcall = function(...) return ( pcall(...) )end + +print( 'a:close', pcall( a.close, a ) ) +print( 'a:write', pcall( a.write, a, 'eee') ) +print( 'a:flush', pcall( a.flush, a) ) +print( 'a:read', pcall( a.read, a, 5) ) +print( 'a:lines', pcall( a.lines, a) ) +print( 'a:seek', pcall( a.seek, a, "cur", -2) ) +print( 'a:setvbuf', pcall( a.setvbuf, a, "no") ) +print( 'a:close', pcall( a.close, a ) ) +print( 'io.type(a)', pcall( io.type, a ) ) + +print( 'io.close()', pcall( io.close ) ) +print( 'io.close(io.output())', pcall( io.close, io.output() ) ) + +io.output('abc.txt') +print( 'io.close()', pcall( io.close ) ) +print( 'io.write', pcall( io.write, 'eee') ) +print( 'io.flush', pcall( io.flush) ) +print( 'io.close', pcall( io.close ) ) +io.input('abc.txt'):close() +print( 'io.read', pcall( io.read, 5) ) +print( 'io.lines', pcall( io.lines) ) + diff --git a/test/lua/mathlib.lua b/test/lua/mathlib.lua new file mode 100644 index 00000000..bd574d0b --- /dev/null +++ b/test/lua/mathlib.lua @@ -0,0 +1,223 @@ +local platform = ... +print( 'platform', platform ) + +local aliases = { + ['0']='', + ['-0']='', + ['nan']='', + ['inf']='', + ['-inf']='', + ['1.#INF']='', + ['-1.#INF']='', + ['1.#IND']='', + ['-1.#IND']='', +} + +local UNOPVALUES = { -2.5, -2, 0, 2, 2.5, "'-2.5'", "'-2'", "'0'", "'2'", "'2.5'" } + +local NUMBERPAIRS = { + { 2, 0 }, { -2.5, 0 }, { 2, 1 }, + { 5, 2 }, {-5, 2 }, {16, 2}, {-16, -2}, + {256, .5}, {256, .25}, {256, 0.625}, + {256, -0.5}, {256, -.25}, {256, -.625}, {-256, .5}, + { .5, 0}, {.5, 1}, {.5, 2}, {.5, -1}, {.5, 2}, + {2.25, 0}, {2.25, 2}, {2.25, .5}, {2.25, 2.5}, {-2, 0}, + { 3, 3 }, +} + +local STRINGPAIRS = { + { "'2'", "'0'" }, { "'2.5'","'3'" }, { "'-2'", "'1.5'" }, { "'-2.5'", "'-1.5'" }, + { "'3.0'", "'3.0'" }, { 2.75, 2.75 }, { "'2.75'", "'2.75'" }, +} + +local MIXEDPAIRS = { + { 3, "'3'" }, { "'3'", 3 }, { 2.75, "'2.75'" }, { "'2.75'", 2.75 }, + { -3, "'-4'" }, { "'-3'", 4 }, { -3, "'4'" }, { "'-3'", -4 }, + { -4.75, "'2.75'" }, { "'-2.75'", 1.75 }, { 4.75, "'-2.75'" }, { "'2.75'", -1.75 }, +} + +local BINOPVALUES = {} + +local RELATIONALOPVALUES = {} + +local function addall( t, s ) + for i,v in ipairs(s) do + t[#t+1] = v + end +end +addall( BINOPVALUES, NUMBERPAIRS ) +addall( BINOPVALUES, STRINGPAIRS ) +addall( BINOPVALUES, MIXEDPAIRS ) +addall( RELATIONALOPVALUES, NUMBERPAIRS ) +addall( RELATIONALOPVALUES, STRINGPAIRS ) + +local VARARGSVALUES = { + { 4, }, { -4.5 }, { "'5.5'" }, { "'-5'" }, + { 4, "'8'" }, { -4.5, "'-8'" }, { "'5.5'", 2.2 }, { "'-5'", -2.2 }, + { 111,222,333 }, { -222,-333,-111 }, { 444,-111,-222 }, +} + +local CONSTANTS = { + "huge", "pi", +} +local UNARYOPS = { + "-", "not ", +} +local BINARYOPS = { + "+", "-", "*", "^", "/", "%", +} +local RELATIONALS = { + "==", "~=", ">", "<", ">=", "<=", +} +local ONEARG_JME = { + "abs", "ceil", "cos", "deg", + "exp", "floor", "frexp", "modf", + "rad", "sin", "sqrt", "tan", +} +local ONEARG_JSE = { + "acos", "asin", "atan", "cosh", + "log", "log10", "sinh", "tanh", +} +local TWOARGS_JME = { + "fmod", "ldexp", "pow", +} +local TWOARGS_JSE = { + "atan2", +} +local VARARGSFUNCS = { + "max", "min", +} + +local ts = tostring +tostring = function(x) + local s = ts(x) + if type(x)~='number' then return s end + if aliases[s] then return aliases[s] end + if #s < 7 then return s end + local a,b = string.match(s,'([%-0-9%.]*)([eE]?[%-0-9]*)') + return a and (string.sub(a,1,6)..(b or '')) or s +end + +local function eval( expr, script ) + script = script or ('return '..expr) + local s,a,b = loadstring( script, 'expr' ) + if s then print( expr, pcall( s ) ) + else print( expr, 'loadstring:', a ) end +end + +-- misc tests +print( '---------- miscellaneous tests ----------' ) +eval( 'math.sin( 0.0 )' ) +eval( 'math.cos( math.pi )' ) +eval( 'math.sqrt( 9.0 )' ) +eval( 'math.modf( 5.25 )') +eval( 'math.frexp(0.00625)' ) +eval( '-5 ^ 2' ) +eval( '-5 / 2' ) +eval( '-5 % 2' ) + +-- constants +print( '---------- constants ----------' ) +for i,v in ipairs(CONSTANTS) do + eval( 'math.'..v ) +end + +-- unary operators +for i,v in ipairs(UNARYOPS) do + print( '---------- unary operator '..v..' ----------' ) + for j,a in ipairs(UNOPVALUES) do + eval( v..a, 'return '..v..a ) + end +end + +-- binary operators +for i,v in ipairs(BINARYOPS) do + print( '---------- binary operator '..v..' ----------' ) + for j,xy in ipairs(BINOPVALUES) do + eval( xy[1]..v..xy[2], + 'local x,y='..xy[1]..','..xy[2]..'; return x'..v..'y' ) + end +end + +-- relational operators +for i,v in ipairs(RELATIONALS) do + print( '---------- binary operator '..v..' ----------' ) + for j,xy in ipairs(RELATIONALOPVALUES) do + eval( xy[1]..v..xy[2], + 'local x,y='..xy[1]..','..xy[2]..'; return x'..v..'y' ) + end +end + +-- one-argument math functions +for i,v in ipairs(ONEARG_JME) do + print( '---------- math.'..v..' ----------' ) + for j,x in ipairs(UNOPVALUES) do + eval( 'math.'..v..'('..x..')' ) + end +end +if platform ~= 'JME' then + for i,v in ipairs(ONEARG_JSE) do + print( '---------- math.'..v..' (jse only) ----------' ) + for j,x in ipairs(UNOPVALUES) do + eval( 'math.'..v..'('..x..')' ) + end + end +end + +-- two-argument math functions +for i,v in ipairs(TWOARGS_JME) do + print( '---------- math.'..v..' ----------' ) + for j,x in ipairs(BINOPVALUES) do + eval( 'math.'..v..'('..x[1]..','..x[2]..')' ) + end +end +if platform ~= 'JME' then + for i,v in ipairs(TWOARGS_JSE) do + print( '---------- math.'..v..' (jse only) ----------' ) + for j,x in ipairs(BINOPVALUES) do + eval( 'math.'..v..'('..x[1]..','..x[2]..')' ) + end + end +end + +-- var-arg math functions +for i,v in ipairs(VARARGSFUNCS) do + print( '---------- math.'..v..' ----------' ) + for j,x in ipairs(VARARGSVALUES) do + eval( 'math.'..v..'('..table.concat(x,',')..')' ) + end +end + +-- random tests +print("----------- Random number tests") +local function testrandom(string,lo,hi) + local c,e = loadstring('return '..string) + for i=1,5 do + local s,e = pcall(c) + if s then + print( string, s and type(e) or e, (e>=lo) and (e<=hi) ) + else + print( string, 'error', e ) + end + end +end +testrandom('math.random()',0,1) +testrandom('math.random(5,10)',5,10) +testrandom('math.random(30)',0,30) +testrandom('math.random(-4,-2)',-4,-2) +local t = {} +print( math.randomseed(20) ) +for i=1,20 do + t[i] = math.random() +end +print( '-- comparing new numbers') +for i=1,20 do + print( t[i] == math.random(), t[i] == t[0] ) +end +print( '-- resetting seed') + +print( math.randomseed(20) ) +for i=1,20 do + print( t[i] == math.random() ) +end +--]] diff --git a/test/lua/oslib.lua b/test/lua/oslib.lua new file mode 100644 index 00000000..ce6f6928 --- /dev/null +++ b/test/lua/oslib.lua @@ -0,0 +1,40 @@ +-- simple os-library tests +-- +-- because the nature of the "os" library is to provide os-specific behavior, +-- the compatibility tests must be extremely loose, and can really only +-- compare things like return value type to be meaningful. +-- +-- actual os behavior needs to go in an oslib function test +-- +local pcall = function(...) + local s,e,f = pcall(...) + return s,type(e) +end +print( 'os', type(os) ) +print( 'os.clock()', pcall( os.clock ) ) +print( 'os.date()', pcall( os.date ) ) +print( 'os.difftime(123000, 21500)', pcall( os.difftime, 123000, 21250 ) ) +print( 'os.execute("bogus")', pcall( os.execute, '' ) ) +print( 'os.getenv()', pcall( os.getenv ) ) +print( 'os.getenv("bogus.key")', pcall( os.getenv, 'bogus.key' ) ) +local s,p = pcall( os.tmpname ) +local s,q = pcall( os.tmpname ) +print( 'os.tmpname()', s, p ) +print( 'os.tmpname()', s, q ) +-- permission denied on windows +--print( 'os.remove(p)', pcall( os.remove, p ) ) +--print( 'os.rename(p,q)', pcall( os.rename, p, q ) ) +local s,f = pcall( io.open, p,"w" ) +print( 'io.open', s, f ) +print( 'write', pcall( f.write, f, "abcdef 12345" ) ) +print( 'close', pcall( f.close, f ) ) +print( 'os.rename(p,q)', pcall( os.rename, p, q ) ) +print( 'os.remove(q)', pcall( os.remove, q ) ) +print( 'os.remove(q)', pcall( os.remove, q ) ) +-- setlocale not supported on jse yet +-- print( 'os.setlocale()', pcall( os.setlocale ) ) +-- print( 'os.setlocale("jp")', pcall( os.setlocale, "jp" ) ) +-- print( 'os.setlocale("us","monetary")', pcall( os.setlocale, "us", "monetary" ) ) +-- print( 'os.setlocale(nil,"all")', pcall( os.setlocale, nil, "all" ) ) +print( 'os.setlocale("c")', pcall( os.setlocale, "c" ) ) +print( 'os.exit', type(os.exit) ) diff --git a/test/lua/stringlib.lua b/test/lua/stringlib.lua new file mode 100644 index 00000000..4bf95c67 --- /dev/null +++ b/test/lua/stringlib.lua @@ -0,0 +1,174 @@ +print( string.find("1234567890", ".", 0, true) ) +print( string.find( 'alo alx 123 b\0o b\0o', '(..*) %1' ) ) +print( string.find( 'aloALO', '%l*' ) ) +print( string.find( ' \n isto � assim', '%S%S*' ) ) + +print( string.find( "", "" ) ) +print( string.find( "ababaabbaba", "abb" ) ) +print( string.find( "ababaabbaba", "abb", 7 ) ) + +print( string.match( "aabaa", "a*" ) ) +print( string.match( "aabaa", "a*", 3 ) ) +print( string.match( "aabaa", "a*b" ) ) +print( string.match( "aabaa", "a*b", 3 ) ) + +print( string.match( "abbaaababaabaaabaa", "b(a*)b" ) ) + +print( string.match( "abbaaababaabaaabaa", "b(a*)()b" ) ) +print( string.match( "abbaaababaabaaabaa", "b(a*)()b", 3 ) ) +print( string.match( "abbaaababaabaaabaa", "b(a*)()b", 8 ) ) +print( string.match( "abbaaababaabaaabaa", "b(a*)()b", 12 ) ) + +print( string.byte("hi", -3) ) + +print( string.gsub("ABC", "@(%x+)", function(s) return "|abcd" end) ) +print( string.gsub("@123", "@(%x+)", function(s) return "|abcd" end) ) +print( string.gsub("ABC@123", "@(%x+)", function(s) return "|abcd" end) ) +print( string.gsub("ABC@123@def", "@(%x+)", function(s) return "|abcd" end) ) +print( string.gsub("ABC@123@qrs@def@tuv", "@(%x+)", function(s) return "|abcd" end) ) +print( string.gsub("ABC@123@qrs@def@tuv", "@(%x+)", function(s) return "@ab" end) ) + +print( tostring(1234567890123) ) +print( tostring(1234567890124) ) +print( tostring(1234567890125) ) + +function f1(s, p) + print(p) + p = string.gsub(p, "%%([0-9])", function (s) return "%" .. (s+1) end) + print(p) + p = string.gsub(p, "^(^?)", "%1()", 1) + print(p) + p = string.gsub(p, "($?)$", "()%1", 1) + print(p) + local t = {string.match(s, p)} + return string.sub(s, t[1], t[#t] - 1) +end + +print( pcall( f1, 'alo alx 123 b\0o b\0o', '(..*) %1' ) ) + +local function badpat() + print( string.gsub( "alo", "(.)", "%2" ) ) +end + +print( ( pcall( badpat ) ) ) + +for k, v in string.gmatch("w=200&h=150", "(%w+)=(%w+)") do + print(k, v) +end + +-- string.sub +function t(str) + local i = { 0, 1, 2, 8, -1 } + for ki,vi in ipairs(i) do + local s,v = pcall( string.sub, str, vi ) + print( 'string.sub("'..str..'",'..tostring(vi)..')='..tostring(s)..',"'..tostring(v)..'"' ) + local j = { 0, 1, 2, 4, 8, -1 } + for kj,vj in ipairs(j) do + local s,v = pcall( string.sub, str, vi, vj ) + print( 'string.sub("'..str..'",'..tostring(vi)..','..tostring(vj)..')='..tostring(s)..',"'..tostring(v)..'"' ) + end + end +end +t( 'abcdefghijklmn' ) +t( 'abcdefg' ) +t( 'abcd' ) +t( 'abc' ) +t( 'ab' ) +t( 'a' ) +t( '' ) + +print(string.len("Hello, world")) +print(#"Hello, world") +print(string.len("\0\0\0")) +print(#"\0\0\0") +print(string.len("\0\1\2\3")) +print(#"\0\1\2\3") +local s = "My JaCk-O-lAnTeRn CaSe TeXt" +print(s, string.len(s), #s) + + +-- string.format +print(string.format("(%.0d) (%.0d) (%.0d)", 0, -5, 9)) +print(string.format("(%.1d) (%.1d) (%.1d)", 0, -5, 9)) +print(string.format("(%.2d) (%.2d) (%.2d)", 0, -5, 9)) + +print(string.format("(%+.0d) (%+.0d) (%+.0d)", 0, -5, 9)) +print(string.format("(%+.1d) (%+.1d) (%+.1d)", 0, -5, 9)) +print(string.format("(%+.2d) (%+.2d) (%+.2d)", 0, -5, 9)) + +print(string.format("(%+3d) (% 3d) (%+ 3d)", 55, 55, 55)) + +print(string.format("(%-1d) (%-1d) (%-1d)", 1, 12, -12)) +print(string.format("(%-2d) (%-2d) (%-2d)", 1, 12, -12)) +print(string.format("(%-3d) (%-3d) (%-3d)", 1, 12, -12)) + + +print(string.format("(%8x) (%8d) (%8o)", 255, 255, 255)) +print(string.format("(%08x) (%08d) (%08o)", 255, 255, 255)) + +print(string.format("simple%ssimple", " simple ")) + +print(string.format("%%")) +specials = "\"specials\": %% \000 \r \n" +print(string.format("specials (%%s): ----->%s<----", specials) ) +print(string.format("specials (%%q): ----->%q<----", specials) ) +print(string.format("controls (%%q): ----->%q<----", ' \a \b \f \t \v \\ ') ) +print(string.format("extended (%%q): ----->%q<----", ' \222 \223 \224 ') ) +print(string.format("embedded newlines: %s\n%s\n%s", '======>', '<======>', '<=======')) +print(string.format("this is a %s long string", string.rep("really, ", 30))) + +local function pc(...) + local s,e = pcall(...) + return s and e or 'false-'..type(e) +end + +local function strtests(name,func,...) + print(name, 'good', pc( func, ... ) ) + print(name, 'empty', pc( func ) ) + print(name, 'table', pc( func, {} ) ) + print(name, 'nil', pc( func, nil ) ) +end + +strtests('lower', string.lower, s ) +strtests('upper', string.upper, s ) +strtests('reverse', string.reverse, s ) +strtests('char', string.char, 92, 60, 61, 93 ) +strtests('dump', string.dump, loadstring('print("hello, world")', 'sample') ) + + +-- floating point formats (not supported yet) +--[==[ +local prefixes = {'','+','-'} +local lengths = {'7','2','0','1',''} +local letters = {'f','e','g'} +local fmt, spec, desc +for i,letter in ipairs(letters) do + for k,before in ipairs(lengths) do + for j,prefix in ipairs(prefixes) do + spec = '(%'..prefix..before..letter..')' + fmt = spec..'\t'..spec..'\t'..spec..'\t'..spec..'\t'..spec..'\t'..spec + print(spec, string.format(fmt, 12.34, -12.34, 1/11, -1/11, 300/11, -300/11) ) + for l,after in ipairs(lengths) do + spec = '(%'..prefix..before..'.'..after..letter..')' + fmt = spec..' '..spec..' '..spec..' '..spec..' '..spec..' '..spec + print(spec, string.format(fmt, 12.34, -12.34, 1/11, -1/11, 300/11, -300/11) ) + end + end + end +end +--]==] + +local function fmterr(...) + local r, s = pcall(...) + if r then + return s + else + print( 'initial error -->'..tostring(s) ) + s = string.gsub(s, "stdin:%d+:%s*", "") + return s + end +end + +print(fmterr(string.find, "ab%c)0(", "%")) +print(fmterr(string.find, "ab%c)0(", "(")) +print(pcall(string.find, "ab%c)0(", ")")) diff --git a/test/lua/tablelib.lua b/test/lua/tablelib.lua new file mode 100644 index 00000000..da69efb9 --- /dev/null +++ b/test/lua/tablelib.lua @@ -0,0 +1,274 @@ +local func = function(t,...) + return (...) +end +local tbl = setmetatable({},{__index=func}) +print( tbl[2] ) + + +-- tostring replacement that assigns ids +local ts,id,nid,types = tostring,{},0,{table='tbl',thread='thr',userdata='uda',['function']='func'} +tostring = function(x) + if not x or not types[type(x)] then return ts(x) end + if not id[x] then nid=nid+1; id[x]=types[type(x)]..'.'..nid end + return id[x] +end + +local t = { "one", "two", "three", a='aaa', b='bbb', c='ccc' } + +table.insert(t,'six'); +table.insert(t,1,'seven'); +table.insert(t,4,'eight'); +table.insert(t,7,'nine'); +table.insert(t,10,'ten'); print( #t ) + +-- concat +print( '-- concat tests' ) +function tryconcat(t) + print( table.concat(t) ) + print( table.concat(t,'--') ) + print( table.concat(t,',',2) ) + print( table.concat(t,',',2,2) ) + print( table.concat(t,',',5,2) ) +end +tryconcat( { "one", "two", "three", a='aaa', b='bbb', c='ccc' } ) +tryconcat( { "one", "two", "three", "four", "five" } ) +function tryconcat(t) + print( table.concat(t) ) + print( table.concat(t,'--') ) + print( table.concat(t,',',2) ) +end +tryconcat( { a='aaa', b='bbb', c='ccc', d='ddd', e='eee' } ) +tryconcat( { [501]="one", [502]="two", [503]="three", [504]="four", [505]="five" } ) +tryconcat( {} ) + +-- print the elements of a table in a platform-independent way +function eles(t,f) + f = f or pairs + all = {} + for k,v in f(t) do + table.insert( all, "["..tostring(k).."]="..tostring(v) ) + end + table.sort( all ) + return "{"..table.concat(all,',').."}" +end + +-- insert, maxn +print( '-- insert, len tests' ) +local t = { "one", "two", "three", a='aaa', b='bbb', c='ccc' } + +print( eles(t), #t ) +table.insert(t,'six'); print( eles(t), #t ) +table.insert(t,1,'seven'); print( eles(t), #t ) +table.insert(t,4,'eight'); print( eles(t), #t ) +table.insert(t,7,'nine'); print( eles(t), #t ) +table.insert(t,10,'ten'); print( eles(t), #t ) +print( '#{}', #{} ) +print( '#{"a"}', #{"a"} ) +print( '#{"a","b"}', #{"a","b"} ) +print( '#{"a",nil}', #{"a",nil} ) +print( '#{nil,nil}', #{nil,nil} ) +print( '#{nil,"b"}', #{nil,"b"}==0 or #{nil,"b"}==2 ) +print( '#{"a","b","c"}', #{"a","b","c"} ) +print( '#{"a","b",nil}', #{"a","b",nil} ) +print( '#{"a",nil,nil}', #{"a",nil,nil} ) +print( '#{nil,nil,nil}', #{nil,nil,nil} ) +print( '#{nil,nil,"c"}', #{nil,nil,"c"}==0 or #{nil,nil,"c"}==3 ) +print( '#{nil,"b","c"}', #{nil,"b","c"}==0 or #{nil,"b","c"}==3 ) +print( '#{nil,"b",nil}', #{nil,"b",nil}==0 or #{nil,"b",nil}==2 ) +print( '#{"a",nil,"c"}', #{"a",nil,"c"}==1 or #{"a",nil,"c"}==3 ) + +-- remove +print( '-- remove tests' ) +t = { "one", "two", "three", "four", "five", "six", "seven", [10]="ten", a='aaa', b='bbb', c='ccc' } +print( eles(t), #t ) +print( 'table.remove(t)', table.remove(t) ); print( eles(t), #t ) +print( 'table.remove(t,1)', table.remove(t,1) ); print( eles(t), #t ) +print( 'table.remove(t,3)', table.remove(t,3) ); print( eles(t), #t ) +print( 'table.remove(t,5)', table.remove(t,5) ); print( eles(t), #t ) +print( 'table.remove(t,10)', table.remove(t,10) ); print( eles(t), #t ) +print( 'table.remove(t,-1)', table.remove(t,-1) ); print( eles(t), #t ) +print( 'table.remove(t,-1)', table.remove(t,-1) ) ; print( eles(t), #t ) + +-- sort +print( '-- sort tests' ) +function sorttest(t,f) + t = (t) + print( table.concat(t,'-') ) + if f then + table.sort(t,f) + else + table.sort(t) + end + print( table.concat(t,'-') ) +end +sorttest{ "one", "two", "three" } +sorttest{ "www", "vvv", "uuu", "ttt", "sss", "zzz", "yyy", "xxx" } +sorttest( { "www", "vvv", "uuu", "ttt", "sss", "zzz", "yyy", "xxx" }, function(a,b) return b0, returning f2(n-1,n,...)", n-1,n,... ) + return f2(n-1,n,...) +end + +local function f3(n,...) + if n <= 0 then + return sum(...) + end + print( " f3,n-1,n,...", f3,n-1,n,... ) + return pcall(f3,n-1,n,...) +end + +local function all(f) + for n=0,3 do + t = {} + for m=1,5 do + print( "--f, n, unpack(t)", f, n, unpack(t) ) + print( pcall( f, n, unpack(t)) ) + t[m] = m + end + end +end + +all(f1) +all(f2) +all(f3) + + +local function f(x) + -- tailcall to a builtin + return math.abs(x) +end + +local function factorial(i) + local function helper(product, n) + if n <= 0 then + return product + else + -- tail call to a nested Lua function + return helper(n * product, n - 1) + end + end + return helper(1, i) +end + +local result1 = factorial(5) +print(result1) +print(factorial(5)) + +local result2 = f(-1234) +print( result2 ) diff --git a/test/lua/upvalues.lua b/test/lua/upvalues.lua new file mode 100644 index 00000000..2c263dd8 --- /dev/null +++ b/test/lua/upvalues.lua @@ -0,0 +1,97 @@ + +print( '-------- simple upvalues tests --------' ) +local function simpleupvalues() + function test() + local x = 5 + function f() + x = x + 1 + return x + end + function g() + x = x - 1 + return x + end + print(f()) + print(g()) + return f, g + end + + f1, g1 = test() + print("f1()=", f1()) + print("g1()=", g1()) + + f2, g2 = test() + print("f2()=", f2()) + print("g2()=", g2()) + + print("g1()=", g1()) + print("f1()=", f1()) +end +print( 'simplevalues result:', pcall( simpleupvalues ) ) + + +-- The point of this test is that when an upvalue is created, it may +-- need to be inserted in the middle of the list, rather than always +-- appended at the end. Otherwise, it may not be found when it is +-- needed by another closure. +print( '----------- upvalued in middle ------------' ) +local function middleupvaluestest() + local function test() + local x = 3 + local y = 5 + local z = 7 + + local function f() + print("y=", y) + end + + local function g() + print("z=", z) + end + + local function h() + print("x=", x) + end + + local function setter(x1, y1, z1) + x = x1 + y = y1 + z = z1 + end + + return f, g, h, setter + end + + local f, g, h, setter = test() + + h() + f() + g() + + setter("x", "y", "z") + + h() + f() + g() +end +print( pcall( middleupvaluestest ) ) + + +print( '--------- nested upvalues ----------' ) +local function nestedupvaluestest() + local f + do + local x = 10 + function g() + print(x, f()) + end + end + + function f() + return 20 + end + + g() +end +print( 'nestedupvaluestest result:', pcall( nestedupvaluestest ) ) + diff --git a/test/lua/vm.lua b/test/lua/vm.lua new file mode 100644 index 00000000..735ccfb1 --- /dev/null +++ b/test/lua/vm.lua @@ -0,0 +1,250 @@ + +print( '-------- basic vm tests --------' ) + +print( '-- boolean tests' ) +local function booleantests() + t = true + f = false + n = nil + s = "Hello" + z = 0 + one = 1 + + print(t) + print(f) + + print(not t) + print(not f) + print(not n) + print(not z) + print(not s) + print(not(not(t))) + print(not(not(z))) + print(not(not(n))) + + print(t and f) + print(t or f) + print(f and t) + print(f or t) + + print(f or one) + print(f or z) + print(f or n) + + print(t and one) + print(t and z) + print(t and n) +end +print( 'booleantests result:', pcall( booleantests ) ) + + +print( '------------- varargs' ) +local function varargstest() + function p(a,...) + print("a",a) + print("...",...) + print("...,a",...,a) + print("a,...",a,...) + end + function q(a,...) + print("a,arg[1],arg[2],arg[3]",a,arg.n,arg[1],arg[2],arg[3]) + end + function r(a,...) + print("a,arg",a,arg) + print("a",a) + print("...",...) + print("...,a",...,a) + print("a,...",a,...) + end + function s(a) + local arg = { '1', '2', '3' } + print("a,arg[1],arg[2],arg[3]",a,arg[1],arg[2],arg[3]) + print("a",a) + end + function t(a,...) + local arg = { '1', '2', '3' } + print("a,arg[1],arg[2],arg[3]",a,arg[1],arg[2],arg[3]) + print("a",a) + print("...",...) + print("...,a",...,a) + print("a,...",a,...) + end + function u(arg) + print( 'arg', arg ) + end + function v(arg,...) + print( 'arg', arg ) + print("...",...) + print("arg,...",arg,...) + end + arg = { "global-1", "global-2", "global-3" } + function tryall(f,name) + print( '---- function '..name..'()' ) + print( '--'..name..'():' ) + print( ' ->', pcall( f ) ) + print( '--'..name..'("q"):' ) + print( ' ->', pcall( f, "q" ) ) + print( '--'..name..'("q","r"):' ) + print( ' ->', pcall( f, "q", "r" ) ) + print( '--'..name..'("q","r","s"):' ) + print( ' ->', pcall( f, "q", "r", "s" ) ) + end + tryall(p,'p') + tryall(q,'q') + tryall(r,'r') + tryall(s,'s') + tryall(t,'t') + tryall(u,'u') + tryall(v,'v') +end +print( 'varargstest result:', pcall( varargstest ) ) + +-- The purpose of this test case is to demonstrate that +-- basic metatable operations on non-table types work. +-- i.e. that s.sub(s,...) could be used in place of string.sub(s,...) +print( '---------- metatable tests' ) +local function metatabletests() + + -- tostring replacement that assigns ids + local ts,id,nid,types = tostring,{},0,{table='tbl',thread='thr',userdata='uda',['function']='func'} + tostring = function(x) + if not x or not types[type(x)] then return ts(x) end + if not id[x] then nid=nid+1; id[x]=types[type(x)]..'.'..nid end + return id[x] + end + local results = function(s,e,...) + if s then return e,... end + return false,type(e) + end + local pcall = function(...) + return results( pcall(...) ) + end + + + local s = "hello" + print(s:sub(2,4)) + + local t = {} + function op(name,...) + local a,b = pcall( setmetatable, t, ... ) + print( name, t, getmetatable(t), a, b ) + end + op('set{} ',{}) + op('set-nil',nil) + op('set{} ',{}) + op('set') + op('set{} ',{}) + op('set{} ',{}) + op('set{}{}',{},{}) + op('set-nil',nil) + op('set{__}',{__metatable={}}) + op('set{} ',{}) + op('set-nil',nil) + t = {} + op('set{} ',{}) + op('set-nil',nil) + op('set{__}',{__metatable='abc'}) + op('set{} ',{}) + op('set-nil',nil) + + + local i = 1234 + local t = setmetatable( {}, { + __mode="v", + __index=function(t,k) + local v = i + i = i + 1 + rawset(t,k,v) + return v + end, + } ) + + local l = { 'a', 'b', 'a', 'b', 'c', 'a', 'b', 'c', 'd' } + for i,key in ipairs(l) do + print( 't.'..key, t[key] ) + end +end +print( 'metatabletests result:', pcall( metatabletests ) ) + +-- test tables with more than 50 elements +print( '------------ huge tables' ) +local function hugetables() + local t = { 1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1, + } + print ("#t=",#t,'t[1,50,51,59]', t[1], t[50], t[51], t[59]) + print (table.concat(t,',')) + + local t2= { 0,3,4,7,9,8,12,15,23,5, + 10,13,14,17,19,18,112,115,123,15, + 20,33,24,27,29,28,212,215,223,25, + 40,43,44,47,49,48,412,415,423,45, + 50,53,54,57,59,58,512,515,523,55, + 60,63,64,67,69,68,612,615,623,65, + 70,73,74,77,79,78,72,715,723,75, + } + + print ("#t2=",#t2,'t[1,50,51,59]', t[1], t[50], t[51], t[59]) + print (table.concat(t2,',')) + + local t = { + [2000]='a', [2001]='b', [2002]='c', [2003]='d', [2004]='e', [2005]='f', [2006]='g', [2007]='h', [2008]='i', [2009]='j', + [3000]='a', [3001]='b', [3002]='c', [3003]='d', [3004]='e', [3005]='f', [3006]='g', [3007]='h', [3008]='i', [3009]='j', + [4000]='a', [4001]='b', [4002]='c', [4003]='d', [4004]='e', [4005]='f', [4006]='g', [4007]='h', [4008]='i', [4009]='j', + [5000]='a', [5001]='b', [5002]='c', [5003]='d', [5004]='e', [5005]='f', [5006]='g', [5007]='h', [5008]='i', [5009]='j', + [6000]='a', [6001]='b', [6002]='c', [6003]='d', [6004]='e', [6005]='f', [6006]='g', [6007]='h', [6008]='i', [6009]='j', + [7000]='a', [7001]='b', [7002]='c', [7003]='d', [7004]='e', [7005]='f', [7006]='g', [7007]='h', [7008]='i', [7009]='j', + [8000]='a', [8001]='b', [8002]='c', [8003]='d', [8004]='e', [8005]='f', [8006]='g', [8007]='h', [8008]='i', [8009]='j', + } + + for i=2000,8000,1000 do + for j=0,9,1 do + print( 't['..tostring(i+j)..']', t[i+j] ) + end + end +end +print( 'hugetables result:', pcall( hugetables ) ) + +print( '--------- many locals' ) +local function manylocals() + -- test program with more than 50 non-sequential integer elements + local t0000='a'; local t0001='b'; local t0002='c'; local t0003='d'; local t0004='e'; local t0005='f'; local t0006='g'; local t0007='h'; local t0008='i'; local t0009='j'; + local t1000='a'; local t1001='b'; local t1002='c'; local t1003='d'; local t1004='e'; local t1005='f'; local t1006='g'; local t1007='h'; local t1008='i'; local t1009='j'; + local t2000='a'; local t2001='b'; local t2002='c'; local t2003='d'; local t2004='e'; local t2005='f'; local t2006='g'; local t2007='h'; local t2008='i'; local t2009='j'; + local t3000='a'; local t3001='b'; local t3002='c'; local t3003='d'; local t3004='e'; local t3005='f'; local t3006='g'; local t3007='h'; local t3008='i'; local t3009='j'; + local t4000='a'; local t4001='b'; local t4002='c'; local t4003='d'; local t4004='e'; local t4005='f'; local t4006='g'; local t4007='h'; local t4008='i'; local t4009='j'; + local t5000='a'; local t5001='b'; local t5002='c'; local t5003='d'; local t5004='e'; local t5005='f'; local t5006='g'; local t5007='h'; local t5008='i'; local t5009='j'; + local t6000='a'; local t6001='b'; local t6002='c'; local t6003='d'; local t6004='e'; local t6005='f'; local t6006='g'; local t6007='h'; local t6008='i'; local t6009='j'; + local t7000='a'; local t7001='b'; local t7002='c'; local t7003='d'; local t7004='e'; local t7005='f'; local t7006='g'; local t7007='h'; local t7008='i'; local t7009='j'; + local t8000='a'; local t8001='b'; local t8002='c'; local t8003='d'; local t8004='e'; local t8005='f'; local t8006='g'; local t8007='h'; local t8008='i'; local t8009='j'; + local t9000='a'; local t9001='b'; local t9002='c'; local t9003='d'; local t9004='e'; local t9005='f'; local t9006='g'; local t9007='h'; local t9008='i'; local t9009='j'; + local t10000='a'; local t10001='b'; local t10002='c'; local t10003='d'; local t10004='e'; local t10005='f'; local t10006='g'; local t10007='h'; local t10008='i'; local t10009='j'; + local t11000='a'; local t11001='b'; local t11002='c'; local t11003='d'; local t11004='e'; local t11005='f'; local t11006='g'; local t11007='h'; local t11008='i'; local t11009='j'; + local t12000='a'; local t12001='b'; local t12002='c'; local t12003='d'; local t12004='e'; local t12005='f'; local t12006='g'; local t12007='h'; local t12008='i'; local t12009='j'; + local t13000='a'; local t13001='b'; local t13002='c'; local t13003='d'; local t13004='e'; local t13005='f'; local t13006='g'; local t13007='h'; local t13008='i'; local t13009='j'; + + -- print the variables + print(t0000,'a'); print(t0001,'b'); print(t0002,'c'); print(t0003,'d'); print(t0004,'e'); print(t0005,'f'); print(t0006,'g'); print(t0007,'h'); print(t0008,'i'); print(t0009,'j'); + print(t1000,'a'); print(t1001,'b'); print(t1002,'c'); print(t1003,'d'); print(t1004,'e'); print(t1005,'f'); print(t1006,'g'); print(t1007,'h'); print(t1008,'i'); print(t1009,'j'); + print(t2000,'a'); print(t2001,'b'); print(t2002,'c'); print(t2003,'d'); print(t2004,'e'); print(t2005,'f'); print(t2006,'g'); print(t2007,'h'); print(t2008,'i'); print(t2009,'j'); + print(t3000,'a'); print(t3001,'b'); print(t3002,'c'); print(t3003,'d'); print(t3004,'e'); print(t3005,'f'); print(t3006,'g'); print(t3007,'h'); print(t3008,'i'); print(t3009,'j'); + print(t4000,'a'); print(t4001,'b'); print(t4002,'c'); print(t4003,'d'); print(t4004,'e'); print(t4005,'f'); print(t4006,'g'); print(t4007,'h'); print(t4008,'i'); print(t4009,'j'); + print(t5000,'a'); print(t5001,'b'); print(t5002,'c'); print(t5003,'d'); print(t5004,'e'); print(t5005,'f'); print(t5006,'g'); print(t5007,'h'); print(t5008,'i'); print(t5009,'j'); + print(t6000,'a'); print(t6001,'b'); print(t6002,'c'); print(t6003,'d'); print(t6004,'e'); print(t6005,'f'); print(t6006,'g'); print(t6007,'h'); print(t6008,'i'); print(t6009,'j'); + print(t7000,'a'); print(t7001,'b'); print(t7002,'c'); print(t7003,'d'); print(t7004,'e'); print(t7005,'f'); print(t7006,'g'); print(t7007,'h'); print(t7008,'i'); print(t7009,'j'); + print(t8000,'a'); print(t8001,'b'); print(t8002,'c'); print(t8003,'d'); print(t8004,'e'); print(t8005,'f'); print(t8006,'g'); print(t8007,'h'); print(t8008,'i'); print(t8009,'j'); + print(t9000,'a'); print(t9001,'b'); print(t9002,'c'); print(t9003,'d'); print(t9004,'e'); print(t9005,'f'); print(t9006,'g'); print(t9007,'h'); print(t9008,'i'); print(t9009,'j'); + print(t10000,'a'); print(t10001,'b'); print(t10002,'c'); print(t10003,'d'); print(t10004,'e'); print(t10005,'f'); print(t10006,'g'); print(t10007,'h'); print(t10008,'i'); print(t10009,'j'); + print(t11000,'a'); print(t11001,'b'); print(t11002,'c'); print(t11003,'d'); print(t11004,'e'); print(t11005,'f'); print(t11006,'g'); print(t11007,'h'); print(t11008,'i'); print(t11009,'j'); + print(t12000,'a'); print(t12001,'b'); print(t12002,'c'); print(t12003,'d'); print(t12004,'e'); print(t12005,'f'); print(t12006,'g'); print(t12007,'h'); print(t12008,'i'); print(t12009,'j'); + print(t13000,'a'); print(t13001,'b'); print(t13002,'c'); print(t13003,'d'); print(t13004,'e'); print(t13005,'f'); print(t13006,'g'); print(t13007,'h'); print(t13008,'i'); print(t13009,'j'); +end +print( 'manylocals result:', pcall( manylocals ) ) diff --git a/version.properties b/version.properties index f7fefdbd..ec7f350e 100644 --- a/version.properties +++ b/version.properties @@ -1 +1,2 @@ -version: 1.0.2 +# on the way to version 2.0 +version: 1.9.50