Compare commits
50 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3a18136076 | ||
|
|
1b057988bc | ||
|
|
92001a4a78 | ||
|
|
906c70248b | ||
|
|
4554c67c3e | ||
|
|
cb7ee575c7 | ||
|
|
c758d4c62a | ||
|
|
8c1c06bb16 | ||
|
|
99ca5fe952 | ||
|
|
1f064655cc | ||
|
|
9a05cc3678 | ||
|
|
6da3cb2c32 | ||
|
|
d2cb7cb1c2 | ||
|
|
0ac769ffba | ||
|
|
ef60786e48 | ||
|
|
f9c15faab8 | ||
|
|
59b28ef35c | ||
|
|
9db7d4ec8a | ||
|
|
40eaace714 | ||
|
|
fe000bb59e | ||
|
|
9aa220df41 | ||
|
|
97c52f15f3 | ||
|
|
54f578790b | ||
|
|
a51a08c1b8 | ||
|
|
7620e758a0 | ||
|
|
80da9253dd | ||
|
|
a133fa900e | ||
|
|
0fa62e544a | ||
|
|
601dbef6e5 | ||
|
|
01b3a4fd09 | ||
|
|
ed0df13acb | ||
|
|
642fd63724 | ||
|
|
a1586cd530 | ||
|
|
291cba2d79 | ||
|
|
96bffc1322 | ||
|
|
e88710fbbc | ||
|
|
99da8be0f8 | ||
|
|
c8c654cb2b | ||
|
|
cf68a42547 | ||
|
|
5c0d4cccdd | ||
|
|
b979b41688 | ||
|
|
b807fa9a99 | ||
|
|
ef5b1b0e09 | ||
|
|
04a038da45 | ||
|
|
bc0e3190d4 | ||
|
|
16dcd084e1 | ||
|
|
15f8c15039 | ||
|
|
bff0da36dc | ||
|
|
1e933a6b75 | ||
|
|
c9bb3e01ca |
6
LICENSE
6
LICENSE
@@ -2,7 +2,7 @@
|
||||
|
||||
ZeroBrane Studio sources are released under the MIT License
|
||||
|
||||
Copyright (c) 2011 Paul Kulchenko (paul@kulchenko.com)
|
||||
Copyright (c) 2011-2012 Paul Kulchenko (paul@kulchenko.com)
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -26,9 +26,9 @@ THE SOFTWARE.
|
||||
|
||||
Estrela Editor sources are released under the MIT License
|
||||
|
||||
Copyright (c) 2008-2011
|
||||
Copyright (c) 2008-2012
|
||||
Luxinia DevTeam:
|
||||
Eike Decker & Christoph Kubisch
|
||||
Christoph Kubisch & Eike Decker
|
||||
info at luxinia.de
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
|
||||
11
README
11
README
@@ -23,6 +23,15 @@ tools. Its main focus is extensibility for target applications using Lua.
|
||||
* Console to directly test code snippets with local and remote execution
|
||||
* Integrated debugger (with support for local and remote debugging)
|
||||
|
||||
--[[ FRONT-ENDS ]]--------------------------------------------------------
|
||||
|
||||
There is currently two front-ends using the same editor engine. The original
|
||||
is "estrela", which has a focus on 3d graphics related usage of Lua, especially
|
||||
in combination with the luxinia engine or luxinia2 framework.
|
||||
The second is "zbstudio" which has a focus on remote use of Lua in robotics.
|
||||
|
||||
Both are part of the standard distribution.
|
||||
|
||||
--[[ INSTALLATION ]]-------------------------------------------------------
|
||||
|
||||
git clone git://github.com/pkulchenko/ZeroBraneStudio.git zbstudio
|
||||
@@ -43,7 +52,7 @@ Overriding Config:
|
||||
|
||||
Estrela Editor
|
||||
|
||||
Luxinia DevTeam: Eike Decker & Christoph Kubisch (info at luxinia.de)
|
||||
Luxinia Dev: Christoph Kubisch (crazybutcher@luxinia.de)
|
||||
|
||||
ZeroBrane Studio and MobDebug
|
||||
|
||||
|
||||
930
api/lua/glfw.lua
Normal file
930
api/lua/glfw.lua
Normal file
@@ -0,0 +1,930 @@
|
||||
--[[// glfw | GLFW window manager
|
||||
enum {
|
||||
/*************************************************************************
|
||||
* GLFW version
|
||||
*************************************************************************/
|
||||
|
||||
GLFW_VERSION_MAJOR =2,
|
||||
GLFW_VERSION_MINOR =7,
|
||||
GLFW_VERSION_REVISION =2,
|
||||
|
||||
|
||||
/*************************************************************************
|
||||
* Input handling definitions
|
||||
*************************************************************************/
|
||||
|
||||
/* Key and button state/action definitions */
|
||||
GLFW_RELEASE =0,
|
||||
GLFW_PRESS =1,
|
||||
|
||||
GLFW_TRUE = 1,
|
||||
GLFW_FALSE = 0,
|
||||
|
||||
/* Keyboard key definitions: 8-bit ISO-8859-1 (Latin 1) encoding is used
|
||||
* for printable keys (such as A-Z, 0-9 etc), and values above 256
|
||||
* represent special (non-printable) keys (e.g. F1, Page Up etc).
|
||||
*/
|
||||
GLFW_KEY_UNKNOWN =-1,
|
||||
GLFW_KEY_SPACE =32,
|
||||
GLFW_KEY_APOSTROPHE = 39,
|
||||
GLFW_KEY_COMMA = 44,
|
||||
GLFW_KEY_MINUS = 45,
|
||||
GLFW_KEY_PERIOD = 46,
|
||||
GLFW_KEY_SLASH = 47,
|
||||
GLFW_KEY_0 = 48,
|
||||
GLFW_KEY_1 = 49,
|
||||
GLFW_KEY_2 = 50,
|
||||
GLFW_KEY_3 = 51,
|
||||
GLFW_KEY_4 = 52,
|
||||
GLFW_KEY_5 = 53,
|
||||
GLFW_KEY_6 = 54,
|
||||
GLFW_KEY_7 = 55,
|
||||
GLFW_KEY_8 = 56,
|
||||
GLFW_KEY_9 = 57,
|
||||
GLFW_KEY_SEMICOLON = 59,
|
||||
GLFW_KEY_EQUAL = 61,
|
||||
GLFW_KEY_A = 65,
|
||||
GLFW_KEY_B = 66,
|
||||
GLFW_KEY_C = 67,
|
||||
GLFW_KEY_D = 68,
|
||||
GLFW_KEY_E = 69,
|
||||
GLFW_KEY_F = 70,
|
||||
GLFW_KEY_G = 71,
|
||||
GLFW_KEY_H = 72,
|
||||
GLFW_KEY_I = 73,
|
||||
GLFW_KEY_J = 74,
|
||||
GLFW_KEY_K = 75,
|
||||
GLFW_KEY_L = 76,
|
||||
GLFW_KEY_M = 77,
|
||||
GLFW_KEY_N = 78,
|
||||
GLFW_KEY_O = 79,
|
||||
GLFW_KEY_P = 80,
|
||||
GLFW_KEY_Q = 81,
|
||||
GLFW_KEY_R = 82,
|
||||
GLFW_KEY_S = 83,
|
||||
GLFW_KEY_T = 84,
|
||||
GLFW_KEY_U = 85,
|
||||
GLFW_KEY_V = 86,
|
||||
GLFW_KEY_W = 87,
|
||||
GLFW_KEY_X = 88,
|
||||
GLFW_KEY_Y = 89,
|
||||
GLFW_KEY_Z = 90,
|
||||
GLFW_KEY_LEFT_BRACKET = 91,
|
||||
GLFW_KEY_BACKSLASH = 92,
|
||||
GLFW_KEY_RIGHT_BRACKET = 93,
|
||||
GLFW_KEY_GRAVE_ACCENT = 96,
|
||||
GLFW_KEY_WORLD_1 = 161,
|
||||
GLFW_KEY_WORLD_2 = 162,
|
||||
|
||||
GLFW_KEY_SPECIAL =256,
|
||||
GLFW_KEY_ESC =(GLFW_KEY_SPECIAL+1),
|
||||
GLFW_KEY_F1 =(GLFW_KEY_SPECIAL+2),
|
||||
GLFW_KEY_F2 =(GLFW_KEY_SPECIAL+3),
|
||||
GLFW_KEY_F3 =(GLFW_KEY_SPECIAL+4),
|
||||
GLFW_KEY_F4 =(GLFW_KEY_SPECIAL+5),
|
||||
GLFW_KEY_F5 =(GLFW_KEY_SPECIAL+6),
|
||||
GLFW_KEY_F6 =(GLFW_KEY_SPECIAL+7),
|
||||
GLFW_KEY_F7 =(GLFW_KEY_SPECIAL+8),
|
||||
GLFW_KEY_F8 =(GLFW_KEY_SPECIAL+9),
|
||||
GLFW_KEY_F9 =(GLFW_KEY_SPECIAL+10),
|
||||
GLFW_KEY_F10 =(GLFW_KEY_SPECIAL+11),
|
||||
GLFW_KEY_F11 =(GLFW_KEY_SPECIAL+12),
|
||||
GLFW_KEY_F12 =(GLFW_KEY_SPECIAL+13),
|
||||
GLFW_KEY_F13 =(GLFW_KEY_SPECIAL+14),
|
||||
GLFW_KEY_F14 =(GLFW_KEY_SPECIAL+15),
|
||||
GLFW_KEY_F15 =(GLFW_KEY_SPECIAL+16),
|
||||
GLFW_KEY_F16 =(GLFW_KEY_SPECIAL+17),
|
||||
GLFW_KEY_F17 =(GLFW_KEY_SPECIAL+18),
|
||||
GLFW_KEY_F18 =(GLFW_KEY_SPECIAL+19),
|
||||
GLFW_KEY_F19 =(GLFW_KEY_SPECIAL+20),
|
||||
GLFW_KEY_F20 =(GLFW_KEY_SPECIAL+21),
|
||||
GLFW_KEY_F21 =(GLFW_KEY_SPECIAL+22),
|
||||
GLFW_KEY_F22 =(GLFW_KEY_SPECIAL+23),
|
||||
GLFW_KEY_F23 =(GLFW_KEY_SPECIAL+24),
|
||||
GLFW_KEY_F24 =(GLFW_KEY_SPECIAL+25),
|
||||
GLFW_KEY_F25 =(GLFW_KEY_SPECIAL+26),
|
||||
GLFW_KEY_UP =(GLFW_KEY_SPECIAL+27),
|
||||
GLFW_KEY_DOWN =(GLFW_KEY_SPECIAL+28),
|
||||
GLFW_KEY_LEFT =(GLFW_KEY_SPECIAL+29),
|
||||
GLFW_KEY_RIGHT =(GLFW_KEY_SPECIAL+30),
|
||||
GLFW_KEY_LSHIFT =(GLFW_KEY_SPECIAL+31),
|
||||
GLFW_KEY_RSHIFT =(GLFW_KEY_SPECIAL+32),
|
||||
GLFW_KEY_LCTRL =(GLFW_KEY_SPECIAL+33),
|
||||
GLFW_KEY_RCTRL =(GLFW_KEY_SPECIAL+34),
|
||||
GLFW_KEY_LALT =(GLFW_KEY_SPECIAL+35),
|
||||
GLFW_KEY_RALT =(GLFW_KEY_SPECIAL+36),
|
||||
GLFW_KEY_TAB =(GLFW_KEY_SPECIAL+37),
|
||||
GLFW_KEY_ENTER =(GLFW_KEY_SPECIAL+38),
|
||||
GLFW_KEY_BACKSPACE =(GLFW_KEY_SPECIAL+39),
|
||||
GLFW_KEY_INSERT =(GLFW_KEY_SPECIAL+40),
|
||||
GLFW_KEY_DEL =(GLFW_KEY_SPECIAL+41),
|
||||
GLFW_KEY_PAGEUP =(GLFW_KEY_SPECIAL+42),
|
||||
GLFW_KEY_PAGEDOWN =(GLFW_KEY_SPECIAL+43),
|
||||
GLFW_KEY_HOME =(GLFW_KEY_SPECIAL+44),
|
||||
GLFW_KEY_END =(GLFW_KEY_SPECIAL+45),
|
||||
GLFW_KEY_KP_0 =(GLFW_KEY_SPECIAL+46),
|
||||
GLFW_KEY_KP_1 =(GLFW_KEY_SPECIAL+47),
|
||||
GLFW_KEY_KP_2 =(GLFW_KEY_SPECIAL+48),
|
||||
GLFW_KEY_KP_3 =(GLFW_KEY_SPECIAL+49),
|
||||
GLFW_KEY_KP_4 =(GLFW_KEY_SPECIAL+50),
|
||||
GLFW_KEY_KP_5 =(GLFW_KEY_SPECIAL+51),
|
||||
GLFW_KEY_KP_6 =(GLFW_KEY_SPECIAL+52),
|
||||
GLFW_KEY_KP_7 =(GLFW_KEY_SPECIAL+53),
|
||||
GLFW_KEY_KP_8 =(GLFW_KEY_SPECIAL+54),
|
||||
GLFW_KEY_KP_9 =(GLFW_KEY_SPECIAL+55),
|
||||
GLFW_KEY_KP_DIVIDE =(GLFW_KEY_SPECIAL+56),
|
||||
GLFW_KEY_KP_MULTIPLY =(GLFW_KEY_SPECIAL+57),
|
||||
GLFW_KEY_KP_SUBTRACT =(GLFW_KEY_SPECIAL+58),
|
||||
GLFW_KEY_KP_ADD =(GLFW_KEY_SPECIAL+59),
|
||||
GLFW_KEY_KP_DECIMAL =(GLFW_KEY_SPECIAL+60),
|
||||
GLFW_KEY_KP_EQUAL =(GLFW_KEY_SPECIAL+61),
|
||||
GLFW_KEY_KP_ENTER =(GLFW_KEY_SPECIAL+62),
|
||||
GLFW_KEY_KP_NUM_LOCK =(GLFW_KEY_SPECIAL+63),
|
||||
GLFW_KEY_CAPS_LOCK =(GLFW_KEY_SPECIAL+64),
|
||||
GLFW_KEY_SCROLL_LOCK =(GLFW_KEY_SPECIAL+65),
|
||||
GLFW_KEY_PAUSE =(GLFW_KEY_SPECIAL+66),
|
||||
GLFW_KEY_LSUPER =(GLFW_KEY_SPECIAL+67),
|
||||
GLFW_KEY_RSUPER =(GLFW_KEY_SPECIAL+68),
|
||||
GLFW_KEY_MENU =(GLFW_KEY_SPECIAL+69),
|
||||
GLFW_KEY_LAST =GLFW_KEY_MENU,
|
||||
|
||||
/* Mouse button definitions */
|
||||
GLFW_MOUSE_BUTTON_1 =0,
|
||||
GLFW_MOUSE_BUTTON_2 =1,
|
||||
GLFW_MOUSE_BUTTON_3 =2,
|
||||
GLFW_MOUSE_BUTTON_4 =3,
|
||||
GLFW_MOUSE_BUTTON_5 =4,
|
||||
GLFW_MOUSE_BUTTON_6 =5,
|
||||
GLFW_MOUSE_BUTTON_7 =6,
|
||||
GLFW_MOUSE_BUTTON_8 =7,
|
||||
GLFW_MOUSE_BUTTON_LAST =GLFW_MOUSE_BUTTON_8,
|
||||
|
||||
/* Mouse button aliases */
|
||||
GLFW_MOUSE_BUTTON_LEFT =GLFW_MOUSE_BUTTON_1,
|
||||
GLFW_MOUSE_BUTTON_RIGHT =GLFW_MOUSE_BUTTON_2,
|
||||
GLFW_MOUSE_BUTTON_MIDDLE =GLFW_MOUSE_BUTTON_3,
|
||||
|
||||
|
||||
/* Joystick identifiers */
|
||||
GLFW_JOYSTICK_1 =0,
|
||||
GLFW_JOYSTICK_2 =1,
|
||||
GLFW_JOYSTICK_3 =2,
|
||||
GLFW_JOYSTICK_4 =3,
|
||||
GLFW_JOYSTICK_5 =4,
|
||||
GLFW_JOYSTICK_6 =5,
|
||||
GLFW_JOYSTICK_7 =6,
|
||||
GLFW_JOYSTICK_8 =7,
|
||||
GLFW_JOYSTICK_9 =8,
|
||||
GLFW_JOYSTICK_10 =9,
|
||||
GLFW_JOYSTICK_11 =10,
|
||||
GLFW_JOYSTICK_12 =11,
|
||||
GLFW_JOYSTICK_13 =12,
|
||||
GLFW_JOYSTICK_14 =13,
|
||||
GLFW_JOYSTICK_15 =14,
|
||||
GLFW_JOYSTICK_16 =15,
|
||||
GLFW_JOYSTICK_LAST =GLFW_JOYSTICK_16,
|
||||
|
||||
|
||||
/*************************************************************************
|
||||
* Other definitions
|
||||
*************************************************************************/
|
||||
|
||||
/* glfwOpenWindow modes */
|
||||
GLFW_WINDOW =0x00010001,
|
||||
GLFW_FULLSCREEN =0x00010002,
|
||||
|
||||
/* glfwGetWindowParam tokens */
|
||||
GLFW_OPENED =0x00020001,
|
||||
GLFW_ACTIVE =0x00020002,
|
||||
GLFW_ICONIFIED =0x00020003,
|
||||
GLFW_ACCELERATED =0x00020004,
|
||||
GLFW_RED_BITS =0x00020005,
|
||||
GLFW_GREEN_BITS =0x00020006,
|
||||
GLFW_BLUE_BITS =0x00020007,
|
||||
GLFW_ALPHA_BITS =0x00020008,
|
||||
GLFW_DEPTH_BITS =0x00020009,
|
||||
GLFW_STENCIL_BITS =0x0002000A,
|
||||
|
||||
/* The following constants are used for both glfwGetWindowParam
|
||||
* and glfwOpenWindowHint
|
||||
*/
|
||||
GLFW_REFRESH_RATE =0x0002000B,
|
||||
GLFW_ACCUM_RED_BITS =0x0002000C,
|
||||
GLFW_ACCUM_GREEN_BITS =0x0002000D,
|
||||
GLFW_ACCUM_BLUE_BITS =0x0002000E,
|
||||
GLFW_ACCUM_ALPHA_BITS =0x0002000F,
|
||||
GLFW_AUX_BUFFERS =0x00020010,
|
||||
GLFW_STEREO =0x00020011,
|
||||
GLFW_WINDOW_NO_RESIZE =0x00020012,
|
||||
GLFW_FSAA_SAMPLES =0x00020013,
|
||||
GLFW_OPENGL_VERSION_MAJOR =0x00020014,
|
||||
GLFW_OPENGL_VERSION_MINOR =0x00020015,
|
||||
GLFW_OPENGL_FORWARD_COMPAT =0x00020016,
|
||||
GLFW_OPENGL_DEBUG_CONTEXT =0x00020017,
|
||||
GLFW_OPENGL_PROFILE =0x00020018,
|
||||
|
||||
/* GLFW_OPENGL_PROFILE tokens */
|
||||
GLFW_OPENGL_CORE_PROFILE =0x00050001,
|
||||
GLFW_OPENGL_COMPAT_PROFILE =0x00050002,
|
||||
|
||||
/* glfwEnable/glfwDisable tokens */
|
||||
GLFW_MOUSE_CURSOR =0x00030001,
|
||||
GLFW_STICKY_KEYS =0x00030002,
|
||||
GLFW_STICKY_MOUSE_BUTTONS =0x00030003,
|
||||
GLFW_SYSTEM_KEYS =0x00030004,
|
||||
GLFW_KEY_REPEAT =0x00030005,
|
||||
GLFW_AUTO_POLL_EVENTS =0x00030006,
|
||||
|
||||
/* glfwWaitThread wait modes */
|
||||
GLFW_WAIT =0x00040001,
|
||||
GLFW_NOWAIT =0x00040002,
|
||||
|
||||
/* glfwGetJoystickParam tokens */
|
||||
GLFW_PRESENT =0x00050001,
|
||||
GLFW_AXES =0x00050002,
|
||||
GLFW_BUTTONS =0x00050003,
|
||||
|
||||
/* glfwReadImage/glfwLoadTexture2D flags */
|
||||
GLFW_NO_RESCALE_BIT =0x00000001 /* Only for glfwReadImage */,
|
||||
GLFW_ORIGIN_UL_BIT =0x00000002,
|
||||
GLFW_BUILD_MIPMAPS_BIT =0x00000004 /* Only for glfwLoadTexture2D */,
|
||||
GLFW_ALPHA_MAP_BIT =0x00000008,
|
||||
|
||||
/* Time spans longer than this (seconds) are considered to be infinity */
|
||||
};
|
||||
|
||||
const float GLFW_INFINITY =100000.0;
|
||||
|
||||
/* The video mode structure used by glfwGetVideoModes() */
|
||||
typedef struct {
|
||||
int Width, Height;
|
||||
int RedBits, BlueBits, GreenBits;
|
||||
} GLFWvidmode;
|
||||
|
||||
/* Image/texture information */
|
||||
typedef struct {
|
||||
int Width, Height;
|
||||
int Format;
|
||||
int BytesPerPixel;
|
||||
unsigned char *Data;
|
||||
} GLFWimage;
|
||||
|
||||
/* Thread ID */
|
||||
typedef int GLFWthread;
|
||||
|
||||
/* Mutex object */
|
||||
typedef void * GLFWmutex;
|
||||
|
||||
/* Condition variable object */
|
||||
typedef void * GLFWcond;
|
||||
|
||||
/* Function pointer types */
|
||||
typedef void (GLFWCALL * GLFWwindowsizefun)(int,int);
|
||||
typedef int (GLFWCALL * GLFWwindowclosefun)(void);
|
||||
typedef void (GLFWCALL * GLFWwindowrefreshfun)(void);
|
||||
typedef void (GLFWCALL * GLFWmousebuttonfun)(int,int);
|
||||
typedef void (GLFWCALL * GLFWmouseposfun)(int,int);
|
||||
typedef void (GLFWCALL * GLFWmousewheelfun)(int);
|
||||
typedef void (GLFWCALL * GLFWkeyfun)(int,int);
|
||||
typedef void (GLFWCALL * GLFWcharfun)(int,int);
|
||||
typedef void (GLFWCALL * GLFWthreadfun)(void *);
|
||||
|
||||
|
||||
/*************************************************************************
|
||||
* Prototypes
|
||||
*************************************************************************/
|
||||
|
||||
/* GLFW initialization, termination and version querying */
|
||||
int glfwInit( void );
|
||||
void glfwTerminate( void );
|
||||
void glfwGetVersion( int *major, int *minor, int *rev );
|
||||
|
||||
/* Window handling */
|
||||
int glfwOpenWindow( int width, int height, int redbits, int greenbits, int bluebits, int alphabits, int depthbits, int stencilbits, int mode );
|
||||
void glfwOpenWindowHint( int target, int hint );
|
||||
void glfwCloseWindow( void );
|
||||
void glfwSetWindowTitle( const char *title );
|
||||
void glfwGetWindowSize( int *width, int *height );
|
||||
void glfwSetWindowSize( int width, int height );
|
||||
void glfwSetWindowPos( int x, int y );
|
||||
void glfwIconifyWindow( void );
|
||||
void glfwRestoreWindow( void );
|
||||
void glfwSwapBuffers( void );
|
||||
void glfwSwapInterval( int interval );
|
||||
int glfwGetWindowParam( int param );
|
||||
void glfwSetWindowSizeCallback( GLFWwindowsizefun cbfun );
|
||||
void glfwSetWindowCloseCallback( GLFWwindowclosefun cbfun );
|
||||
void glfwSetWindowRefreshCallback( GLFWwindowrefreshfun cbfun );
|
||||
|
||||
/* Video mode functions */
|
||||
int glfwGetVideoModes( GLFWvidmode *list, int maxcount );
|
||||
void glfwGetDesktopMode( GLFWvidmode *mode );
|
||||
|
||||
/* Input handling */
|
||||
void glfwPollEvents( void );
|
||||
void glfwWaitEvents( void );
|
||||
int glfwGetKey( int key );
|
||||
int glfwGetMouseButton( int button );
|
||||
void glfwGetMousePos( int *xpos, int *ypos );
|
||||
void glfwSetMousePos( int xpos, int ypos );
|
||||
int glfwGetMouseWheel( void );
|
||||
void glfwSetMouseWheel( int pos );
|
||||
void glfwSetKeyCallback( GLFWkeyfun cbfun );
|
||||
void glfwSetCharCallback( GLFWcharfun cbfun );
|
||||
void glfwSetMouseButtonCallback( GLFWmousebuttonfun cbfun );
|
||||
void glfwSetMousePosCallback( GLFWmouseposfun cbfun );
|
||||
void glfwSetMouseWheelCallback( GLFWmousewheelfun cbfun );
|
||||
|
||||
/* Joystick input */
|
||||
int glfwGetJoystickParam( int joy, int param );
|
||||
int glfwGetJoystickPos( int joy, float *pos, int numaxes );
|
||||
int glfwGetJoystickButtons( int joy, unsigned char *buttons, int numbuttons );
|
||||
|
||||
/* Time */
|
||||
double glfwGetTime( void );
|
||||
void glfwSetTime( double time );
|
||||
void glfwSleep( double time );
|
||||
|
||||
/* Extension support */
|
||||
int glfwExtensionSupported( const char *extension );
|
||||
void* glfwGetProcAddress( const char *procname );
|
||||
void glfwGetGLVersion( int *major, int *minor, int *rev );
|
||||
|
||||
/* Threading support */
|
||||
GLFWthread glfwCreateThread( GLFWthreadfun fun, void *arg );
|
||||
void glfwDestroyThread( GLFWthread ID );
|
||||
int glfwWaitThread( GLFWthread ID, int waitmode );
|
||||
GLFWthread glfwGetThreadID( void );
|
||||
GLFWmutex glfwCreateMutex( void );
|
||||
void glfwDestroyMutex( GLFWmutex mutex );
|
||||
void glfwLockMutex( GLFWmutex mutex );
|
||||
void glfwUnlockMutex( GLFWmutex mutex );
|
||||
GLFWcond glfwCreateCond( void );
|
||||
void glfwDestroyCond( GLFWcond cond );
|
||||
void glfwWaitCond( GLFWcond cond, GLFWmutex mutex, double timeout );
|
||||
void glfwSignalCond( GLFWcond cond );
|
||||
void glfwBroadcastCond( GLFWcond cond );
|
||||
int glfwGetNumberOfProcessors( void );
|
||||
|
||||
/* Enable/disable functions */
|
||||
void glfwEnable( int token );
|
||||
void glfwDisable( int token );
|
||||
|
||||
/* Image/texture I/O support */
|
||||
int glfwReadImage( const char *name, GLFWimage *img, int flags );
|
||||
int glfwReadMemoryImage( const void *data, long size, GLFWimage *img, int flags );
|
||||
void glfwFreeImage( GLFWimage *img );
|
||||
int glfwLoadTexture2D( const char *name, int flags );
|
||||
int glfwLoadMemoryTexture2D( const void *data, long size, int flags );
|
||||
int glfwLoadTextureImage2D( GLFWimage *img, int flags );
|
||||
]]
|
||||
--auto-generated api from ffi headers
|
||||
local api =
|
||||
{
|
||||
["GLFW_VERSION_MAJOR"] = { type ='value', },
|
||||
["GLFW_VERSION_MINOR"] = { type ='value', },
|
||||
["GLFW_VERSION_REVISION"] = { type ='value', },
|
||||
["GLFW_RELEASE"] = { type ='value', },
|
||||
["GLFW_PRESS"] = { type ='value', },
|
||||
["GLFW_KEY_UNKNOWN"] = { type ='value', },
|
||||
["GLFW_KEY_SPACE"] = { type ='value', },
|
||||
["GLFW_KEY_APOSTROPHE"] = { type ='value', },
|
||||
["GLFW_KEY_COMMA"] = { type ='value', },
|
||||
["GLFW_KEY_MINUS"] = { type ='value', },
|
||||
["GLFW_KEY_PERIOD"] = { type ='value', },
|
||||
["GLFW_KEY_SLASH"] = { type ='value', },
|
||||
["GLFW_KEY_0"] = { type ='value', },
|
||||
["GLFW_KEY_1"] = { type ='value', },
|
||||
["GLFW_KEY_2"] = { type ='value', },
|
||||
["GLFW_KEY_3"] = { type ='value', },
|
||||
["GLFW_KEY_4"] = { type ='value', },
|
||||
["GLFW_KEY_5"] = { type ='value', },
|
||||
["GLFW_KEY_6"] = { type ='value', },
|
||||
["GLFW_KEY_7"] = { type ='value', },
|
||||
["GLFW_KEY_8"] = { type ='value', },
|
||||
["GLFW_KEY_9"] = { type ='value', },
|
||||
["GLFW_KEY_SEMICOLON"] = { type ='value', },
|
||||
["GLFW_KEY_EQUAL"] = { type ='value', },
|
||||
["GLFW_KEY_A"] = { type ='value', },
|
||||
["GLFW_KEY_B"] = { type ='value', },
|
||||
["GLFW_KEY_C"] = { type ='value', },
|
||||
["GLFW_KEY_D"] = { type ='value', },
|
||||
["GLFW_KEY_E"] = { type ='value', },
|
||||
["GLFW_KEY_F"] = { type ='value', },
|
||||
["GLFW_KEY_G"] = { type ='value', },
|
||||
["GLFW_KEY_H"] = { type ='value', },
|
||||
["GLFW_KEY_I"] = { type ='value', },
|
||||
["GLFW_KEY_J"] = { type ='value', },
|
||||
["GLFW_KEY_K"] = { type ='value', },
|
||||
["GLFW_KEY_L"] = { type ='value', },
|
||||
["GLFW_KEY_M"] = { type ='value', },
|
||||
["GLFW_KEY_N"] = { type ='value', },
|
||||
["GLFW_KEY_O"] = { type ='value', },
|
||||
["GLFW_KEY_P"] = { type ='value', },
|
||||
["GLFW_KEY_Q"] = { type ='value', },
|
||||
["GLFW_KEY_R"] = { type ='value', },
|
||||
["GLFW_KEY_S"] = { type ='value', },
|
||||
["GLFW_KEY_T"] = { type ='value', },
|
||||
["GLFW_KEY_U"] = { type ='value', },
|
||||
["GLFW_KEY_V"] = { type ='value', },
|
||||
["GLFW_KEY_W"] = { type ='value', },
|
||||
["GLFW_KEY_X"] = { type ='value', },
|
||||
["GLFW_KEY_Y"] = { type ='value', },
|
||||
["GLFW_KEY_Z"] = { type ='value', },
|
||||
["GLFW_KEY_LEFT_BRACKET"] = { type ='value', },
|
||||
["GLFW_KEY_BACKSLASH"] = { type ='value', },
|
||||
["GLFW_KEY_RIGHT_BRACKET"] = { type ='value', },
|
||||
["GLFW_KEY_GRAVE_ACCENT"] = { type ='value', },
|
||||
["GLFW_KEY_WORLD_1"] = { type ='value', },
|
||||
["GLFW_KEY_WORLD_2"] = { type ='value', },
|
||||
["GLFW_KEY_SPECIAL"] = { type ='value', },
|
||||
["GLFW_KEY_ESC"] = { type ='value', },
|
||||
["GLFW_KEY_F1"] = { type ='value', },
|
||||
["GLFW_KEY_F2"] = { type ='value', },
|
||||
["GLFW_KEY_F3"] = { type ='value', },
|
||||
["GLFW_KEY_F4"] = { type ='value', },
|
||||
["GLFW_KEY_F5"] = { type ='value', },
|
||||
["GLFW_KEY_F6"] = { type ='value', },
|
||||
["GLFW_KEY_F7"] = { type ='value', },
|
||||
["GLFW_KEY_F8"] = { type ='value', },
|
||||
["GLFW_KEY_F9"] = { type ='value', },
|
||||
["GLFW_KEY_F10"] = { type ='value', },
|
||||
["GLFW_KEY_F11"] = { type ='value', },
|
||||
["GLFW_KEY_F12"] = { type ='value', },
|
||||
["GLFW_KEY_F13"] = { type ='value', },
|
||||
["GLFW_KEY_F14"] = { type ='value', },
|
||||
["GLFW_KEY_F15"] = { type ='value', },
|
||||
["GLFW_KEY_F16"] = { type ='value', },
|
||||
["GLFW_KEY_F17"] = { type ='value', },
|
||||
["GLFW_KEY_F18"] = { type ='value', },
|
||||
["GLFW_KEY_F19"] = { type ='value', },
|
||||
["GLFW_KEY_F20"] = { type ='value', },
|
||||
["GLFW_KEY_F21"] = { type ='value', },
|
||||
["GLFW_KEY_F22"] = { type ='value', },
|
||||
["GLFW_KEY_F23"] = { type ='value', },
|
||||
["GLFW_KEY_F24"] = { type ='value', },
|
||||
["GLFW_KEY_F25"] = { type ='value', },
|
||||
["GLFW_KEY_UP"] = { type ='value', },
|
||||
["GLFW_KEY_DOWN"] = { type ='value', },
|
||||
["GLFW_KEY_LEFT"] = { type ='value', },
|
||||
["GLFW_KEY_RIGHT"] = { type ='value', },
|
||||
["GLFW_KEY_LSHIFT"] = { type ='value', },
|
||||
["GLFW_KEY_RSHIFT"] = { type ='value', },
|
||||
["GLFW_KEY_LCTRL"] = { type ='value', },
|
||||
["GLFW_KEY_RCTRL"] = { type ='value', },
|
||||
["GLFW_KEY_LALT"] = { type ='value', },
|
||||
["GLFW_KEY_RALT"] = { type ='value', },
|
||||
["GLFW_KEY_TAB"] = { type ='value', },
|
||||
["GLFW_KEY_ENTER"] = { type ='value', },
|
||||
["GLFW_KEY_BACKSPACE"] = { type ='value', },
|
||||
["GLFW_KEY_INSERT"] = { type ='value', },
|
||||
["GLFW_KEY_DEL"] = { type ='value', },
|
||||
["GLFW_KEY_PAGEUP"] = { type ='value', },
|
||||
["GLFW_KEY_PAGEDOWN"] = { type ='value', },
|
||||
["GLFW_KEY_HOME"] = { type ='value', },
|
||||
["GLFW_KEY_END"] = { type ='value', },
|
||||
["GLFW_KEY_KP_0"] = { type ='value', },
|
||||
["GLFW_KEY_KP_1"] = { type ='value', },
|
||||
["GLFW_KEY_KP_2"] = { type ='value', },
|
||||
["GLFW_KEY_KP_3"] = { type ='value', },
|
||||
["GLFW_KEY_KP_4"] = { type ='value', },
|
||||
["GLFW_KEY_KP_5"] = { type ='value', },
|
||||
["GLFW_KEY_KP_6"] = { type ='value', },
|
||||
["GLFW_KEY_KP_7"] = { type ='value', },
|
||||
["GLFW_KEY_KP_8"] = { type ='value', },
|
||||
["GLFW_KEY_KP_9"] = { type ='value', },
|
||||
["GLFW_KEY_KP_DIVIDE"] = { type ='value', },
|
||||
["GLFW_KEY_KP_MULTIPLY"] = { type ='value', },
|
||||
["GLFW_KEY_KP_SUBTRACT"] = { type ='value', },
|
||||
["GLFW_KEY_KP_ADD"] = { type ='value', },
|
||||
["GLFW_KEY_KP_DECIMAL"] = { type ='value', },
|
||||
["GLFW_KEY_KP_EQUAL"] = { type ='value', },
|
||||
["GLFW_KEY_KP_ENTER"] = { type ='value', },
|
||||
["GLFW_KEY_KP_NUM_LOCK"] = { type ='value', },
|
||||
["GLFW_KEY_CAPS_LOCK"] = { type ='value', },
|
||||
["GLFW_KEY_SCROLL_LOCK"] = { type ='value', },
|
||||
["GLFW_KEY_PAUSE"] = { type ='value', },
|
||||
["GLFW_KEY_LSUPER"] = { type ='value', },
|
||||
["GLFW_KEY_RSUPER"] = { type ='value', },
|
||||
["GLFW_KEY_MENU"] = { type ='value', },
|
||||
["GLFW_KEY_LAST"] = { type ='value', },
|
||||
["GLFW_MOUSE_BUTTON_1"] = { type ='value', },
|
||||
["GLFW_MOUSE_BUTTON_2"] = { type ='value', },
|
||||
["GLFW_MOUSE_BUTTON_3"] = { type ='value', },
|
||||
["GLFW_MOUSE_BUTTON_4"] = { type ='value', },
|
||||
["GLFW_MOUSE_BUTTON_5"] = { type ='value', },
|
||||
["GLFW_MOUSE_BUTTON_6"] = { type ='value', },
|
||||
["GLFW_MOUSE_BUTTON_7"] = { type ='value', },
|
||||
["GLFW_MOUSE_BUTTON_8"] = { type ='value', },
|
||||
["GLFW_MOUSE_BUTTON_LAST"] = { type ='value', },
|
||||
["GLFW_MOUSE_BUTTON_LEFT"] = { type ='value', },
|
||||
["GLFW_MOUSE_BUTTON_RIGHT"] = { type ='value', },
|
||||
["GLFW_MOUSE_BUTTON_MIDDLE"] = { type ='value', },
|
||||
["GLFW_JOYSTICK_1"] = { type ='value', },
|
||||
["GLFW_JOYSTICK_2"] = { type ='value', },
|
||||
["GLFW_JOYSTICK_3"] = { type ='value', },
|
||||
["GLFW_JOYSTICK_4"] = { type ='value', },
|
||||
["GLFW_JOYSTICK_5"] = { type ='value', },
|
||||
["GLFW_JOYSTICK_6"] = { type ='value', },
|
||||
["GLFW_JOYSTICK_7"] = { type ='value', },
|
||||
["GLFW_JOYSTICK_8"] = { type ='value', },
|
||||
["GLFW_JOYSTICK_9"] = { type ='value', },
|
||||
["GLFW_JOYSTICK_10"] = { type ='value', },
|
||||
["GLFW_JOYSTICK_11"] = { type ='value', },
|
||||
["GLFW_JOYSTICK_12"] = { type ='value', },
|
||||
["GLFW_JOYSTICK_13"] = { type ='value', },
|
||||
["GLFW_JOYSTICK_14"] = { type ='value', },
|
||||
["GLFW_JOYSTICK_15"] = { type ='value', },
|
||||
["GLFW_JOYSTICK_16"] = { type ='value', },
|
||||
["GLFW_JOYSTICK_LAST"] = { type ='value', },
|
||||
["GLFW_WINDOW"] = { type ='value', },
|
||||
["GLFW_FULLSCREEN"] = { type ='value', },
|
||||
["GLFW_OPENED"] = { type ='value', },
|
||||
["GLFW_ACTIVE"] = { type ='value', },
|
||||
["GLFW_ICONIFIED"] = { type ='value', },
|
||||
["GLFW_ACCELERATED"] = { type ='value', },
|
||||
["GLFW_RED_BITS"] = { type ='value', },
|
||||
["GLFW_GREEN_BITS"] = { type ='value', },
|
||||
["GLFW_BLUE_BITS"] = { type ='value', },
|
||||
["GLFW_ALPHA_BITS"] = { type ='value', },
|
||||
["GLFW_DEPTH_BITS"] = { type ='value', },
|
||||
["GLFW_STENCIL_BITS"] = { type ='value', },
|
||||
["GLFW_REFRESH_RATE"] = { type ='value', },
|
||||
["GLFW_ACCUM_RED_BITS"] = { type ='value', },
|
||||
["GLFW_ACCUM_GREEN_BITS"] = { type ='value', },
|
||||
["GLFW_ACCUM_BLUE_BITS"] = { type ='value', },
|
||||
["GLFW_ACCUM_ALPHA_BITS"] = { type ='value', },
|
||||
["GLFW_AUX_BUFFERS"] = { type ='value', },
|
||||
["GLFW_STEREO"] = { type ='value', },
|
||||
["GLFW_WINDOW_NO_RESIZE"] = { type ='value', },
|
||||
["GLFW_FSAA_SAMPLES"] = { type ='value', },
|
||||
["GLFW_OPENGL_VERSION_MAJOR"] = { type ='value', },
|
||||
["GLFW_OPENGL_VERSION_MINOR"] = { type ='value', },
|
||||
["GLFW_OPENGL_FORWARD_COMPAT"] = { type ='value', },
|
||||
["GLFW_OPENGL_DEBUG_CONTEXT"] = { type ='value', },
|
||||
["GLFW_OPENGL_PROFILE"] = { type ='value', },
|
||||
["GLFW_OPENGL_CORE_PROFILE"] = { type ='value', },
|
||||
["GLFW_OPENGL_COMPAT_PROFILE"] = { type ='value', },
|
||||
["GLFW_MOUSE_CURSOR"] = { type ='value', },
|
||||
["GLFW_STICKY_KEYS"] = { type ='value', },
|
||||
["GLFW_STICKY_MOUSE_BUTTONS"] = { type ='value', },
|
||||
["GLFW_SYSTEM_KEYS"] = { type ='value', },
|
||||
["GLFW_KEY_REPEAT"] = { type ='value', },
|
||||
["GLFW_AUTO_POLL_EVENTS"] = { type ='value', },
|
||||
["GLFW_WAIT"] = { type ='value', },
|
||||
["GLFW_NOWAIT"] = { type ='value', },
|
||||
["GLFW_PRESENT"] = { type ='value', },
|
||||
["GLFW_AXES"] = { type ='value', },
|
||||
["GLFW_BUTTONS"] = { type ='value', },
|
||||
["GLFW_NO_RESCALE_BIT"] = { type ='value', },
|
||||
["GLFW_ORIGIN_UL_BIT"] = { type ='value', },
|
||||
["GLFW_BUILD_MIPMAPS_BIT"] = { type ='value', },
|
||||
["GLFW_ALPHA_MAP_BIT"] = { type ='value', },
|
||||
["glfwInit"] = { type ='function',
|
||||
description = "",
|
||||
returns = "(int)",
|
||||
valuetype = nil,
|
||||
args = "(void)", },
|
||||
["glfwTerminate"] = { type ='function',
|
||||
description = "",
|
||||
returns = "()",
|
||||
valuetype = nil,
|
||||
args = "(void)", },
|
||||
["glfwGetVersion"] = { type ='function',
|
||||
description = "",
|
||||
returns = "()",
|
||||
valuetype = nil,
|
||||
args = "(int *major, int *minor, int *rev)", },
|
||||
["glfwOpenWindow"] = { type ='function',
|
||||
description = "",
|
||||
returns = "(int)",
|
||||
valuetype = nil,
|
||||
args = "(int width, int height, int redbits, int greenbits, int bluebits, int alphabits, int depthbits, int stencilbits, int mode)", },
|
||||
["glfwOpenWindowHint"] = { type ='function',
|
||||
description = "",
|
||||
returns = "()",
|
||||
valuetype = nil,
|
||||
args = "(int target, int hint)", },
|
||||
["glfwCloseWindow"] = { type ='function',
|
||||
description = "",
|
||||
returns = "()",
|
||||
valuetype = nil,
|
||||
args = "(void)", },
|
||||
["glfwSetWindowTitle"] = { type ='function',
|
||||
description = "",
|
||||
returns = "()",
|
||||
valuetype = nil,
|
||||
args = "(const char *title)", },
|
||||
["glfwGetWindowSize"] = { type ='function',
|
||||
description = "",
|
||||
returns = "()",
|
||||
valuetype = nil,
|
||||
args = "(int *width, int *height)", },
|
||||
["glfwSetWindowSize"] = { type ='function',
|
||||
description = "",
|
||||
returns = "()",
|
||||
valuetype = nil,
|
||||
args = "(int width, int height)", },
|
||||
["glfwSetWindowPos"] = { type ='function',
|
||||
description = "",
|
||||
returns = "()",
|
||||
valuetype = nil,
|
||||
args = "(int x, int y)", },
|
||||
["glfwIconifyWindow"] = { type ='function',
|
||||
description = "",
|
||||
returns = "()",
|
||||
valuetype = nil,
|
||||
args = "(void)", },
|
||||
["glfwRestoreWindow"] = { type ='function',
|
||||
description = "",
|
||||
returns = "()",
|
||||
valuetype = nil,
|
||||
args = "(void)", },
|
||||
["glfwSwapBuffers"] = { type ='function',
|
||||
description = "",
|
||||
returns = "()",
|
||||
valuetype = nil,
|
||||
args = "(void)", },
|
||||
["glfwSwapInterval"] = { type ='function',
|
||||
description = "",
|
||||
returns = "()",
|
||||
valuetype = nil,
|
||||
args = "(int interval)", },
|
||||
["glfwGetWindowParam"] = { type ='function',
|
||||
description = "",
|
||||
returns = "(int)",
|
||||
valuetype = nil,
|
||||
args = "(int param)", },
|
||||
["glfwSetWindowSizeCallback"] = { type ='function',
|
||||
description = "",
|
||||
returns = "()",
|
||||
valuetype = nil,
|
||||
args = "(GLFWwindowsizefun cbfun)", },
|
||||
["glfwSetWindowCloseCallback"] = { type ='function',
|
||||
description = "",
|
||||
returns = "()",
|
||||
valuetype = nil,
|
||||
args = "(GLFWwindowclosefun cbfun)", },
|
||||
["glfwSetWindowRefreshCallback"] = { type ='function',
|
||||
description = "",
|
||||
returns = "()",
|
||||
valuetype = nil,
|
||||
args = "(GLFWwindowrefreshfun cbfun)", },
|
||||
["glfwGetVideoModes"] = { type ='function',
|
||||
description = "",
|
||||
returns = "(int)",
|
||||
valuetype = nil,
|
||||
args = "(GLFWvidmode *list, int maxcount)", },
|
||||
["glfwGetDesktopMode"] = { type ='function',
|
||||
description = "",
|
||||
returns = "()",
|
||||
valuetype = nil,
|
||||
args = "(GLFWvidmode *mode)", },
|
||||
["glfwPollEvents"] = { type ='function',
|
||||
description = "",
|
||||
returns = "()",
|
||||
valuetype = nil,
|
||||
args = "(void)", },
|
||||
["glfwWaitEvents"] = { type ='function',
|
||||
description = "",
|
||||
returns = "()",
|
||||
valuetype = nil,
|
||||
args = "(void)", },
|
||||
["glfwGetKey"] = { type ='function',
|
||||
description = "",
|
||||
returns = "(int)",
|
||||
valuetype = nil,
|
||||
args = "(int key)", },
|
||||
["glfwGetMouseButton"] = { type ='function',
|
||||
description = "",
|
||||
returns = "(int)",
|
||||
valuetype = nil,
|
||||
args = "(int button)", },
|
||||
["glfwGetMousePos"] = { type ='function',
|
||||
description = "",
|
||||
returns = "()",
|
||||
valuetype = nil,
|
||||
args = "(int *xpos, int *ypos)", },
|
||||
["glfwSetMousePos"] = { type ='function',
|
||||
description = "",
|
||||
returns = "()",
|
||||
valuetype = nil,
|
||||
args = "(int xpos, int ypos)", },
|
||||
["glfwGetMouseWheel"] = { type ='function',
|
||||
description = "",
|
||||
returns = "(int)",
|
||||
valuetype = nil,
|
||||
args = "(void)", },
|
||||
["glfwSetMouseWheel"] = { type ='function',
|
||||
description = "",
|
||||
returns = "()",
|
||||
valuetype = nil,
|
||||
args = "(int pos)", },
|
||||
["glfwSetKeyCallback"] = { type ='function',
|
||||
description = "",
|
||||
returns = "()",
|
||||
valuetype = nil,
|
||||
args = "(GLFWkeyfun cbfun)", },
|
||||
["glfwSetCharCallback"] = { type ='function',
|
||||
description = "",
|
||||
returns = "()",
|
||||
valuetype = nil,
|
||||
args = "(GLFWcharfun cbfun)", },
|
||||
["glfwSetMouseButtonCallback"] = { type ='function',
|
||||
description = "",
|
||||
returns = "()",
|
||||
valuetype = nil,
|
||||
args = "(GLFWmousebuttonfun cbfun)", },
|
||||
["glfwSetMousePosCallback"] = { type ='function',
|
||||
description = "",
|
||||
returns = "()",
|
||||
valuetype = nil,
|
||||
args = "(GLFWmouseposfun cbfun)", },
|
||||
["glfwSetMouseWheelCallback"] = { type ='function',
|
||||
description = "",
|
||||
returns = "()",
|
||||
valuetype = nil,
|
||||
args = "(GLFWmousewheelfun cbfun)", },
|
||||
["glfwGetJoystickParam"] = { type ='function',
|
||||
description = "",
|
||||
returns = "(int)",
|
||||
valuetype = nil,
|
||||
args = "(int joy, int param)", },
|
||||
["glfwGetJoystickPos"] = { type ='function',
|
||||
description = "",
|
||||
returns = "(int)",
|
||||
valuetype = nil,
|
||||
args = "(int joy, float *pos, int numaxes)", },
|
||||
["glfwGetJoystickButtons"] = { type ='function',
|
||||
description = "",
|
||||
returns = "(int)",
|
||||
valuetype = nil,
|
||||
args = "(int joy, unsigned char *buttons, int numbuttons)", },
|
||||
["glfwGetTime"] = { type ='function',
|
||||
description = "",
|
||||
returns = "(double)",
|
||||
valuetype = nil,
|
||||
args = "(void)", },
|
||||
["glfwSetTime"] = { type ='function',
|
||||
description = "",
|
||||
returns = "(void)",
|
||||
valuetype = nil,
|
||||
args = "(double time)", },
|
||||
["glfwSleep"] = { type ='function',
|
||||
description = "",
|
||||
returns = "(void)",
|
||||
valuetype = nil,
|
||||
args = "(double time)", },
|
||||
["glfwExtensionSupported"] = { type ='function',
|
||||
description = "",
|
||||
returns = "(int)",
|
||||
valuetype = nil,
|
||||
args = "(const char *extension)", },
|
||||
["glfwGetProcAddress"] = { type ='function',
|
||||
description = "",
|
||||
returns = "(void*)",
|
||||
valuetype = nil,
|
||||
args = "(const char *procname)", },
|
||||
["glfwGetGLVersion"] = { type ='function',
|
||||
description = "",
|
||||
returns = "(void)",
|
||||
valuetype = nil,
|
||||
args = "(int *major, int *minor, int *rev)", },
|
||||
["glfwCreateThread"] = { type ='function',
|
||||
description = "",
|
||||
returns = "(GLFWthread)",
|
||||
valuetype = nil,
|
||||
args = "(GLFWthreadfun fun, void *arg)", },
|
||||
["glfwDestroyThread"] = { type ='function',
|
||||
description = "",
|
||||
returns = "()",
|
||||
valuetype = nil,
|
||||
args = "(GLFWthread ID)", },
|
||||
["glfwWaitThread"] = { type ='function',
|
||||
description = "",
|
||||
returns = "(int)",
|
||||
valuetype = nil,
|
||||
args = "(GLFWthread ID, int waitmode)", },
|
||||
["glfwGetThreadID"] = { type ='function',
|
||||
description = "",
|
||||
returns = "(GLFWthread)",
|
||||
valuetype = nil,
|
||||
args = "(void)", },
|
||||
["glfwCreateMutex"] = { type ='function',
|
||||
description = "",
|
||||
returns = "(GLFWmutex)",
|
||||
valuetype = nil,
|
||||
args = "(void)", },
|
||||
["glfwDestroyMutex"] = { type ='function',
|
||||
description = "",
|
||||
returns = "()",
|
||||
valuetype = nil,
|
||||
args = "(GLFWmutex mutex)", },
|
||||
["glfwLockMutex"] = { type ='function',
|
||||
description = "",
|
||||
returns = "()",
|
||||
valuetype = nil,
|
||||
args = "(GLFWmutex mutex)", },
|
||||
["glfwUnlockMutex"] = { type ='function',
|
||||
description = "",
|
||||
returns = "()",
|
||||
valuetype = nil,
|
||||
args = "(GLFWmutex mutex)", },
|
||||
["glfwCreateCond"] = { type ='function',
|
||||
description = "",
|
||||
returns = "(GLFWcond)",
|
||||
valuetype = nil,
|
||||
args = "(void)", },
|
||||
["glfwDestroyCond"] = { type ='function',
|
||||
description = "",
|
||||
returns = "()",
|
||||
valuetype = nil,
|
||||
args = "(GLFWcond cond)", },
|
||||
["glfwWaitCond"] = { type ='function',
|
||||
description = "",
|
||||
returns = "()",
|
||||
valuetype = nil,
|
||||
args = "(GLFWcond cond, GLFWmutex mutex, double timeout)", },
|
||||
["glfwSignalCond"] = { type ='function',
|
||||
description = "",
|
||||
returns = "()",
|
||||
valuetype = nil,
|
||||
args = "(GLFWcond cond)", },
|
||||
["glfwBroadcastCond"] = { type ='function',
|
||||
description = "",
|
||||
returns = "()",
|
||||
valuetype = nil,
|
||||
args = "(GLFWcond cond)", },
|
||||
["glfwGetNumberOfProcessors"] = { type ='function',
|
||||
description = "",
|
||||
returns = "(int)",
|
||||
valuetype = nil,
|
||||
args = "(void)", },
|
||||
["glfwEnable"] = { type ='function',
|
||||
description = "",
|
||||
returns = "()",
|
||||
valuetype = nil,
|
||||
args = "(int token)", },
|
||||
["glfwDisable"] = { type ='function',
|
||||
description = "",
|
||||
returns = "()",
|
||||
valuetype = nil,
|
||||
args = "(int token)", },
|
||||
["glfwReadImage"] = { type ='function',
|
||||
description = "",
|
||||
returns = "(int)",
|
||||
valuetype = nil,
|
||||
args = "(const char *name, GLFWimage *img, int flags)", },
|
||||
["glfwReadMemoryImage"] = { type ='function',
|
||||
description = "",
|
||||
returns = "(int)",
|
||||
valuetype = nil,
|
||||
args = "(const void *data, long size, GLFWimage *img, int flags)", },
|
||||
["glfwFreeImage"] = { type ='function',
|
||||
description = "",
|
||||
returns = "()",
|
||||
valuetype = nil,
|
||||
args = "(GLFWimage *img)", },
|
||||
["glfwLoadTexture2D"] = { type ='function',
|
||||
description = "",
|
||||
returns = "(int)",
|
||||
valuetype = nil,
|
||||
args = "(const char *name, int flags)", },
|
||||
["glfwLoadMemoryTexture2D"] = { type ='function',
|
||||
description = "",
|
||||
returns = "(int)",
|
||||
valuetype = nil,
|
||||
args = "(const void *data, long size, int flags)", },
|
||||
["glfwLoadTextureImage2D"] = { type ='function',
|
||||
description = "",
|
||||
returns = "(int)",
|
||||
valuetype = nil,
|
||||
args = "(GLFWimage *img, int flags)", },
|
||||
["GLFWvidmode"] = { type ='class',
|
||||
description = "",
|
||||
childs = {
|
||||
["Width"] = { type ='value', description = "int", valuetype = nil, },
|
||||
["Height"] = { type ='value', description = "int", valuetype = nil, },
|
||||
["RedBits"] = { type ='value', description = "int", valuetype = nil, },
|
||||
["BlueBits"] = { type ='value', description = "int", valuetype = nil, },
|
||||
["GreenBits"] = { type ='value', description = "int", valuetype = nil, },
|
||||
}
|
||||
},
|
||||
["GLFWimage"] = { type ='class',
|
||||
description = "",
|
||||
childs = {
|
||||
["Width"] = { type ='value', description = "int", valuetype = nil, },
|
||||
["Height"] = { type ='value', description = "int", valuetype = nil, },
|
||||
["Format"] = { type ='value', description = "int", valuetype = nil, },
|
||||
["BytesPerPixel"] = { type ='value', description = "int", valuetype = nil, },
|
||||
}
|
||||
},
|
||||
}
|
||||
return {
|
||||
glfw = {
|
||||
type = 'lib',
|
||||
description = "GLFW window manager",
|
||||
childs = api,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ return {
|
||||
local bottomnotebook = ide.frame.bottomnotebook
|
||||
bottomnotebook:SetSelection(1)
|
||||
|
||||
ShellExecuteCode(wfilename)
|
||||
ShellExecuteFile(wfilename)
|
||||
end,
|
||||
fprojdir = function(self,wfilename)
|
||||
return wfilename:GetPath(wx.wxPATH_GET_VOLUME)
|
||||
|
||||
@@ -9,15 +9,17 @@ return {
|
||||
if rundebug then
|
||||
DebuggerAttachDefault()
|
||||
script = (""..
|
||||
"package.path=package.path..';"..mainpath.."lualibs/?/?.lua';"..
|
||||
"package.path=package.path..';"..mainpath.."lualibs/?/?.lua;"..mainpath.."lualibs/?.lua';"..
|
||||
"package.cpath=package.cpath..';"..mainpath.."bin/clibs/?.dll';"..
|
||||
"require 'mobdebug'; io.stdout:setvbuf('no'); mobdebug.loop('" .. wx.wxGetHostName().."',"..ide.debugger.portnumber..")")
|
||||
"require('mobdebug').loop('" .. wx.wxGetHostName().."',"..ide.debugger.portnumber..")")
|
||||
else
|
||||
script = ([[dofile '%s']]):format(filepath)
|
||||
end
|
||||
local code = ([[xpcall(function() %s end,function(err) print(debug.traceback(err)) end)]]):format(script)
|
||||
local code = ([[xpcall(function() io.stdout:setvbuf('no'); %s end,function(err) print(debug.traceback(err)) end)]]):format(script)
|
||||
local cmd = '"'..mainpath..'bin/lua.exe" -e "'..code..'"'
|
||||
return CommandLineRun(cmd,self:fworkdir(wfilename),true,false)
|
||||
-- CommandLineRun(cmd,wdir,tooutput,nohide,stringcallback,uid,endcallback)
|
||||
return CommandLineRun(cmd,self:fworkdir(wfilename),true,false,nil,nil,
|
||||
function() ide.debugger.pid = nil end)
|
||||
end,
|
||||
fprojdir = function(self,wfilename)
|
||||
return wfilename:GetPath(wx.wxPATH_GET_VOLUME)
|
||||
|
||||
@@ -7,7 +7,7 @@ return {
|
||||
name = "Luxinia",
|
||||
description = "Luxinia project",
|
||||
api = {"luxiniaapi","baselib"},
|
||||
fcmdline = function(self,wfilename)
|
||||
frun = function(self,wfilename,withdebug)
|
||||
local projdir = ide.config.path.projectdir
|
||||
local endstr = (projdir and projdir:len()>0
|
||||
and " -p "..projdir or "")
|
||||
@@ -18,6 +18,7 @@ return {
|
||||
local cmd = 'luxinia.exe --nologo'..endstr
|
||||
CommandLineRun(cmd,ide.config.path.luxinia,true,true)
|
||||
end,
|
||||
fuid = function(self,wfilename) return "luxinia "..(ide.config.path.projectdir or "") end,
|
||||
fprojdir = function(self,wfilename)
|
||||
local path = GetPathWithSep(wfilename)
|
||||
fname = wx.wxFileName(path)
|
||||
|
||||
@@ -6,7 +6,7 @@ end
|
||||
return {
|
||||
name = "Luxinia2",
|
||||
description = "Luxinia2",
|
||||
api = {"baselib","cg30","cggl30","glfw3","glewgl","assimp20","luxmath","luxgfx","luxscene","luajit2",},
|
||||
api = {"baselib","cg30","cggl30","glfw","glewgl","assimp20","luxmath","luxgfx","luxscene","luajit2",},
|
||||
|
||||
finitclient = function(self)
|
||||
if (not CommandLineRunning(self:fuid(wfilename))) then return end
|
||||
@@ -52,7 +52,7 @@ return {
|
||||
local editorDir = string.gsub(ide.editorFilename:gsub("[^/\\]+$",""),"\\","/")
|
||||
script = ""..
|
||||
"package.path=package.path..';"..editorDir.."lualibs/?/?.lua';"..
|
||||
"require 'mobdebug'; io.stdout:setvbuf('no'); mobdebug.start('" .. wx.wxGetHostName().."',"..ide.debugger.portnumber..")"
|
||||
"io.stdout:setvbuf('no'); require('mobdebug').start('" .. wx.wxGetHostName().."',"..ide.debugger.portnumber..")"
|
||||
|
||||
args = args..' -es "'..script..'"'..startargs
|
||||
else
|
||||
|
||||
1271
lualibs/metalua/compile.lua
Normal file
1271
lualibs/metalua/compile.lua
Normal file
File diff suppressed because it is too large
Load Diff
756
lualibs/metalua/gg.lua
Normal file
756
lualibs/metalua/gg.lua
Normal file
@@ -0,0 +1,756 @@
|
||||
----------------------------------------------------------------------
|
||||
-- Metalua.
|
||||
--
|
||||
-- Summary: parser generator. Collection of higher order functors,
|
||||
-- which allow to build and combine parsers. Relies on a lexer
|
||||
-- that supports the same API as the one exposed in mll.lua.
|
||||
--
|
||||
----------------------------------------------------------------------
|
||||
--
|
||||
-- Copyright (c) 2006-2008, Fabien Fleutot <metalua@gmail.com>.
|
||||
--
|
||||
-- This software is released under the MIT Licence, see licence.txt
|
||||
-- for details.
|
||||
--
|
||||
----------------------------------------------------------------------
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
--
|
||||
-- Exported API:
|
||||
--
|
||||
-- Parser generators:
|
||||
-- * [gg.sequence()]
|
||||
-- * [gg.multisequence()]
|
||||
-- * [gg.expr()]
|
||||
-- * [gg.list()]
|
||||
-- * [gg.onkeyword()]
|
||||
-- * [gg.optkeyword()]
|
||||
--
|
||||
-- Other functions:
|
||||
-- * [gg.parse_error()]
|
||||
-- * [gg.make_parser()]
|
||||
-- * [gg.is_parser()]
|
||||
--
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
module("gg", package.seeall)
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
-- parser metatable, which maps __call to method parse, and adds some
|
||||
-- error tracing boilerplate.
|
||||
-------------------------------------------------------------------------------
|
||||
local parser_metatable = { }
|
||||
function parser_metatable.__call (parser, lx, ...)
|
||||
--printf ("Call parser %q of type %q", parser.name or "?", parser.kind)
|
||||
if mlc.metabugs then
|
||||
return parser:parse (lx, ...)
|
||||
--local x = parser:parse (lx, ...)
|
||||
--printf ("Result of parser %q: %s",
|
||||
-- parser.name or "?",
|
||||
-- _G.table.tostring(x, "nohash", 80))
|
||||
--return x
|
||||
else
|
||||
local li = lx:lineinfo_right() or { "?", "?", "?", "?" }
|
||||
local status, ast = pcall (parser.parse, parser, lx, ...)
|
||||
if status then return ast else
|
||||
-- Try to replace the gg.lua location, in the error msg, with
|
||||
-- the place where the current parser started handling the
|
||||
-- lexstream.
|
||||
-- Since the error is rethrown, these places are stacked.
|
||||
error (string.format ("%s\n - (l.%s, c.%s, k.%s) in parser %s",
|
||||
ast :strmatch "gg.lua:%d+: (.*)" or ast,
|
||||
li[1], li[2], li[3], parser.name or parser.kind))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
-- Turn a table into a parser, mainly by setting the metatable.
|
||||
-------------------------------------------------------------------------------
|
||||
function make_parser(kind, p)
|
||||
p.kind = kind
|
||||
if not p.transformers then p.transformers = { } end
|
||||
function p.transformers:add (x)
|
||||
table.insert (self, x)
|
||||
end
|
||||
setmetatable (p, parser_metatable)
|
||||
return p
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
-- Return true iff [x] is a parser.
|
||||
-- If it's a gg-generated parser, return the name of its kind.
|
||||
-------------------------------------------------------------------------------
|
||||
function is_parser (x)
|
||||
return type(x)=="function" or getmetatable(x)==parser_metatable and x.kind
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
-- Parse a sequence, without applying builder nor transformers
|
||||
-------------------------------------------------------------------------------
|
||||
local function raw_parse_sequence (lx, p)
|
||||
local r = { }
|
||||
for i=1, #p do
|
||||
e=p[i]
|
||||
if type(e) == "string" then
|
||||
if not lx:is_keyword (lx:next(), e) then
|
||||
parse_error (lx, "A keyword was expected, probably `%s'.", e) end
|
||||
elseif is_parser (e) then
|
||||
table.insert (r, e (lx))
|
||||
else
|
||||
gg.parse_error (lx,"Sequence `%s': element #%i is neither a string "..
|
||||
"nor a parser: %s",
|
||||
p.name, i, table.tostring(e))
|
||||
end
|
||||
end
|
||||
return r
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
-- Parse a multisequence, without applying multisequence transformers.
|
||||
-- The sequences are completely parsed.
|
||||
-------------------------------------------------------------------------------
|
||||
local function raw_parse_multisequence (lx, sequence_table, default)
|
||||
local seq_parser = sequence_table[lx:is_keyword(lx:peek())]
|
||||
if seq_parser then return seq_parser (lx)
|
||||
elseif default then return default (lx)
|
||||
else return false end
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
-- Applies all transformers listed in parser on ast.
|
||||
-------------------------------------------------------------------------------
|
||||
local function transform (ast, parser, fli, lli)
|
||||
if parser.transformers then
|
||||
for _, t in ipairs (parser.transformers) do ast = t(ast) or ast end
|
||||
end
|
||||
if type(ast) == 'table'then
|
||||
local ali = ast.lineinfo
|
||||
if not ali or ali.first~=fli or ali.last~=lli then
|
||||
ast.lineinfo = { first = fli, last = lli }
|
||||
end
|
||||
end
|
||||
return ast
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
-- Generate a tracable parsing error (not implemented yet)
|
||||
-------------------------------------------------------------------------------
|
||||
function parse_error(lx, fmt, ...)
|
||||
local li = lx:lineinfo_left() or {-1,-1,-1, "<unknown file>"}
|
||||
local msg = string.format("line %i, char %i: "..fmt, li[1], li[2], ...)
|
||||
local src = lx.src
|
||||
if li[3]>0 and src then
|
||||
local i, j = li[3], li[3]
|
||||
while src:sub(i,i) ~= '\n' and i>=0 do i=i-1 end
|
||||
while src:sub(j,j) ~= '\n' and j<=#src do j=j+1 end
|
||||
local srcline = src:sub (i+1, j-1)
|
||||
local idx = string.rep (" ", li[2]).."^"
|
||||
msg = string.format("%s\n>>> %s\n>>> %s", msg, srcline, idx)
|
||||
end
|
||||
error(msg)
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
--
|
||||
-- Sequence parser generator
|
||||
--
|
||||
-------------------------------------------------------------------------------
|
||||
-- Input fields:
|
||||
--
|
||||
-- * [builder]: how to build an AST out of sequence parts. let [x] be the list
|
||||
-- of subparser results (keywords are simply omitted). [builder] can be:
|
||||
-- - [nil], in which case the result of parsing is simply [x]
|
||||
-- - a string, which is then put as a tag on [x]
|
||||
-- - a function, which takes [x] as a parameter and returns an AST.
|
||||
--
|
||||
-- * [name]: the name of the parser. Used for debug messages
|
||||
--
|
||||
-- * [transformers]: a list of AST->AST functions, applied in order on ASTs
|
||||
-- returned by the parser.
|
||||
--
|
||||
-- * Table-part entries corresponds to keywords (strings) and subparsers
|
||||
-- (function and callable objects).
|
||||
--
|
||||
-- After creation, the following fields are added:
|
||||
-- * [parse] the parsing function lexer->AST
|
||||
-- * [kind] == "sequence"
|
||||
-- * [name] is set, if it wasn't in the input.
|
||||
--
|
||||
-------------------------------------------------------------------------------
|
||||
function sequence (p)
|
||||
make_parser ("sequence", p)
|
||||
|
||||
-------------------------------------------------------------------
|
||||
-- Parsing method
|
||||
-------------------------------------------------------------------
|
||||
function p:parse (lx)
|
||||
-- Raw parsing:
|
||||
local fli = lx:lineinfo_right()
|
||||
local seq = raw_parse_sequence (lx, self)
|
||||
local lli = lx:lineinfo_left()
|
||||
|
||||
-- Builder application:
|
||||
local builder, tb = self.builder, type (self.builder)
|
||||
if tb == "string" then seq.tag = builder
|
||||
elseif tb == "function" or builder and builder.__call then seq = builder(seq)
|
||||
elseif builder == nil then -- nothing
|
||||
else error ("Invalid builder of type "..tb.." in sequence") end
|
||||
seq = transform (seq, self, fli, lli)
|
||||
assert (not seq or seq.lineinfo)
|
||||
return seq
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------
|
||||
-- Construction
|
||||
-------------------------------------------------------------------
|
||||
-- Try to build a proper name
|
||||
if p.name then
|
||||
-- don't touch existing name
|
||||
elseif type(p[1])=="string" then -- find name based on 1st keyword
|
||||
if #p==1 then p.name=p[1]
|
||||
elseif type(p[#p])=="string" then
|
||||
p.name = p[1] .. " ... " .. p[#p]
|
||||
else p.name = p[1] .. " ..." end
|
||||
else -- can't find a decent name
|
||||
p.name = "<anonymous>"
|
||||
end
|
||||
|
||||
return p
|
||||
end --</sequence>
|
||||
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
--
|
||||
-- Multiple, keyword-driven, sequence parser generator
|
||||
--
|
||||
-------------------------------------------------------------------------------
|
||||
-- in [p], useful fields are:
|
||||
--
|
||||
-- * [transformers]: as usual
|
||||
--
|
||||
-- * [name]: as usual
|
||||
--
|
||||
-- * Table-part entries must be sequence parsers, or tables which can
|
||||
-- be turned into a sequence parser by [gg.sequence]. These
|
||||
-- sequences must start with a keyword, and this initial keyword
|
||||
-- must be different for each sequence. The table-part entries will
|
||||
-- be removed after [gg.multisequence] returns.
|
||||
--
|
||||
-- * [default]: the parser to run if the next keyword in the lexer is
|
||||
-- none of the registered initial keywords. If there's no default
|
||||
-- parser and no suitable initial keyword, the multisequence parser
|
||||
-- simply returns [false].
|
||||
--
|
||||
-- After creation, the following fields are added:
|
||||
--
|
||||
-- * [parse] the parsing function lexer->AST
|
||||
--
|
||||
-- * [sequences] the table of sequences, indexed by initial keywords.
|
||||
--
|
||||
-- * [add] method takes a sequence parser or a config table for
|
||||
-- [gg.sequence], and adds/replaces the corresponding sequence
|
||||
-- parser. If the keyword was already used, the former sequence is
|
||||
-- removed and a warning is issued.
|
||||
--
|
||||
-- * [get] method returns a sequence by its initial keyword
|
||||
--
|
||||
-- * [kind] == "multisequence"
|
||||
--
|
||||
-------------------------------------------------------------------------------
|
||||
function multisequence (p)
|
||||
make_parser ("multisequence", p)
|
||||
|
||||
-------------------------------------------------------------------
|
||||
-- Add a sequence (might be just a config table for [gg.sequence])
|
||||
-------------------------------------------------------------------
|
||||
function p:add (s)
|
||||
-- compile if necessary:
|
||||
local keyword = type(s)=='table' and s[1]
|
||||
if type(s)=='table' and not is_parser(s) then sequence(s) end
|
||||
if is_parser(s)~='sequence' or type(keyword)~='string' then
|
||||
if self.default then -- two defaults
|
||||
error ("In a multisequence parser, all but one sequences "..
|
||||
"must start with a keyword")
|
||||
else self.default = s end -- first default
|
||||
elseif self.sequences[keyword] then -- duplicate keyword
|
||||
eprintf (" *** Warning: keyword %q overloaded in multisequence ***",
|
||||
keyword)
|
||||
self.sequences[keyword] = s
|
||||
else -- newly caught keyword
|
||||
self.sequences[keyword] = s
|
||||
end
|
||||
end -- </multisequence.add>
|
||||
|
||||
-------------------------------------------------------------------
|
||||
-- Get the sequence starting with this keyword. [kw :: string]
|
||||
-------------------------------------------------------------------
|
||||
function p:get (kw) return self.sequences [kw] end
|
||||
|
||||
-------------------------------------------------------------------
|
||||
-- Remove the sequence starting with keyword [kw :: string]
|
||||
-------------------------------------------------------------------
|
||||
function p:del (kw)
|
||||
if not self.sequences[kw] then
|
||||
eprintf("*** Warning: trying to delete sequence starting "..
|
||||
"with %q from a multisequence having no such "..
|
||||
"entry ***", kw) end
|
||||
local removed = self.sequences[kw]
|
||||
self.sequences[kw] = nil
|
||||
return removed
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------
|
||||
-- Parsing method
|
||||
-------------------------------------------------------------------
|
||||
function p:parse (lx)
|
||||
local fli = lx:lineinfo_right()
|
||||
local x = raw_parse_multisequence (lx, self.sequences, self.default)
|
||||
local lli = lx:lineinfo_left()
|
||||
return transform (x, self, fli, lli)
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------
|
||||
-- Construction
|
||||
-------------------------------------------------------------------
|
||||
-- Register the sequences passed to the constructor. They're going
|
||||
-- from the array part of the parser to the hash part of field
|
||||
-- [sequences]
|
||||
p.sequences = { }
|
||||
for i=1, #p do p:add (p[i]); p[i] = nil end
|
||||
|
||||
-- FIXME: why is this commented out?
|
||||
--if p.default and not is_parser(p.default) then sequence(p.default) end
|
||||
return p
|
||||
end --</multisequence>
|
||||
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
--
|
||||
-- Expression parser generator
|
||||
--
|
||||
-------------------------------------------------------------------------------
|
||||
--
|
||||
-- Expression configuration relies on three tables: [prefix], [infix]
|
||||
-- and [suffix]. Moreover, the primary parser can be replaced by a
|
||||
-- table: in this case the [primary] table will be passed to
|
||||
-- [gg.multisequence] to create a parser.
|
||||
--
|
||||
-- Each of these tables is a modified multisequence parser: the
|
||||
-- differences with respect to regular multisequence config tables are:
|
||||
--
|
||||
-- * the builder takes specific parameters:
|
||||
-- - for [prefix], it takes the result of the prefix sequence parser,
|
||||
-- and the prefixed expression
|
||||
-- - for [infix], it takes the left-hand-side expression, the results
|
||||
-- of the infix sequence parser, and the right-hand-side expression.
|
||||
-- - for [suffix], it takes the suffixed expression, and theresult
|
||||
-- of the suffix sequence parser.
|
||||
--
|
||||
-- * the default field is a list, with parameters:
|
||||
-- - [parser] the raw parsing function
|
||||
-- - [transformers], as usual
|
||||
-- - [prec], the operator's precedence
|
||||
-- - [assoc] for [infix] table, the operator's associativity, which
|
||||
-- can be "left", "right" or "flat" (default to left)
|
||||
--
|
||||
-- In [p], useful fields are:
|
||||
-- * [transformers]: as usual
|
||||
-- * [name]: as usual
|
||||
-- * [primary]: the atomic expression parser, or a multisequence config
|
||||
-- table (mandatory)
|
||||
-- * [prefix]: prefix operators config table, see above.
|
||||
-- * [infix]: infix operators config table, see above.
|
||||
-- * [suffix]: suffix operators config table, see above.
|
||||
--
|
||||
-- After creation, these fields are added:
|
||||
-- * [kind] == "expr"
|
||||
-- * [parse] as usual
|
||||
-- * each table is turned into a multisequence, and therefore has an
|
||||
-- [add] method
|
||||
--
|
||||
-------------------------------------------------------------------------------
|
||||
function expr (p)
|
||||
make_parser ("expr", p)
|
||||
|
||||
-------------------------------------------------------------------
|
||||
-- parser method.
|
||||
-- In addition to the lexer, it takes an optional precedence:
|
||||
-- it won't read expressions whose precedence is lower or equal
|
||||
-- to [prec].
|
||||
-------------------------------------------------------------------
|
||||
function p:parse (lx, prec)
|
||||
prec = prec or 0
|
||||
|
||||
------------------------------------------------------
|
||||
-- Extract the right parser and the corresponding
|
||||
-- options table, for (pre|in|suff)fix operators.
|
||||
-- Options include prec, assoc, transformers.
|
||||
------------------------------------------------------
|
||||
local function get_parser_info (tab)
|
||||
local p2 = tab:get (lx:is_keyword (lx:peek()))
|
||||
if p2 then -- keyword-based sequence found
|
||||
local function parser(lx) return raw_parse_sequence(lx, p2) end
|
||||
return parser, p2
|
||||
else -- Got to use the default parser
|
||||
local d = tab.default
|
||||
if d then return d.parse or d.parser, d
|
||||
else return false, false end
|
||||
end
|
||||
end
|
||||
|
||||
------------------------------------------------------
|
||||
-- Look for a prefix sequence. Multiple prefixes are
|
||||
-- handled through the recursive [p.parse] call.
|
||||
-- Notice the double-transform: one for the primary
|
||||
-- expr, and one for the one with the prefix op.
|
||||
------------------------------------------------------
|
||||
local function handle_prefix ()
|
||||
local fli = lx:lineinfo_right()
|
||||
local p2_func, p2 = get_parser_info (self.prefix)
|
||||
local op = p2_func and p2_func (lx)
|
||||
if op then -- Keyword-based sequence found
|
||||
local ili = lx:lineinfo_right() -- Intermediate LineInfo
|
||||
local e = p2.builder (op, self:parse (lx, p2.prec))
|
||||
local lli = lx:lineinfo_left()
|
||||
return transform (transform (e, p2, ili, lli), self, fli, lli)
|
||||
else -- No prefix found, get a primary expression
|
||||
local e = self.primary(lx)
|
||||
local lli = lx:lineinfo_left()
|
||||
return transform (e, self, fli, lli)
|
||||
end
|
||||
end --</expr.parse.handle_prefix>
|
||||
|
||||
------------------------------------------------------
|
||||
-- Look for an infix sequence+right-hand-side operand.
|
||||
-- Return the whole binary expression result,
|
||||
-- or false if no operator was found.
|
||||
------------------------------------------------------
|
||||
local function handle_infix (e)
|
||||
local p2_func, p2 = get_parser_info (self.infix)
|
||||
if not p2 then return false end
|
||||
|
||||
-----------------------------------------
|
||||
-- Handle flattening operators: gather all operands
|
||||
-- of the series in [list]; when a different operator
|
||||
-- is found, stop, build from [list], [transform] and
|
||||
-- return.
|
||||
-----------------------------------------
|
||||
if (not p2.prec or p2.prec>prec) and p2.assoc=="flat" then
|
||||
local fli = lx:lineinfo_right()
|
||||
local pflat, list = p2, { e }
|
||||
repeat
|
||||
local op = p2_func(lx)
|
||||
if not op then break end
|
||||
table.insert (list, self:parse (lx, p2.prec))
|
||||
local _ -- We only care about checking that p2==pflat
|
||||
_, p2 = get_parser_info (self.infix)
|
||||
until p2 ~= pflat
|
||||
local e2 = pflat.builder (list)
|
||||
local lli = lx:lineinfo_left()
|
||||
return transform (transform (e2, pflat, fli, lli), self, fli, lli)
|
||||
|
||||
-----------------------------------------
|
||||
-- Handle regular infix operators: [e] the LHS is known,
|
||||
-- just gather the operator and [e2] the RHS.
|
||||
-- Result goes in [e3].
|
||||
-----------------------------------------
|
||||
elseif p2.prec and p2.prec>prec or
|
||||
p2.prec==prec and p2.assoc=="right" then
|
||||
local fli = e.lineinfo.first -- lx:lineinfo_right()
|
||||
local op = p2_func(lx)
|
||||
if not op then return false end
|
||||
local e2 = self:parse (lx, p2.prec)
|
||||
local e3 = p2.builder (e, op, e2)
|
||||
local lli = lx:lineinfo_left()
|
||||
return transform (transform (e3, p2, fli, lli), self, fli, lli)
|
||||
|
||||
-----------------------------------------
|
||||
-- Check for non-associative operators, and complain if applicable.
|
||||
-----------------------------------------
|
||||
elseif p2.assoc=="none" and p2.prec==prec then
|
||||
parse_error (lx, "non-associative operator!")
|
||||
|
||||
-----------------------------------------
|
||||
-- No infix operator suitable at that precedence
|
||||
-----------------------------------------
|
||||
else return false end
|
||||
|
||||
end --</expr.parse.handle_infix>
|
||||
|
||||
------------------------------------------------------
|
||||
-- Look for a suffix sequence.
|
||||
-- Return the result of suffix operator on [e],
|
||||
-- or false if no operator was found.
|
||||
------------------------------------------------------
|
||||
local function handle_suffix (e)
|
||||
-- FIXME bad fli, must take e.lineinfo.first
|
||||
local p2_func, p2 = get_parser_info (self.suffix)
|
||||
if not p2 then return false end
|
||||
if not p2.prec or p2.prec>=prec then
|
||||
--local fli = lx:lineinfo_right()
|
||||
local fli = e.lineinfo.first
|
||||
local op = p2_func(lx)
|
||||
if not op then return false end
|
||||
local lli = lx:lineinfo_left()
|
||||
e = p2.builder (e, op)
|
||||
e = transform (transform (e, p2, fli, lli), self, fli, lli)
|
||||
return e
|
||||
end
|
||||
return false
|
||||
end --</expr.parse.handle_suffix>
|
||||
|
||||
------------------------------------------------------
|
||||
-- Parser body: read suffix and (infix+operand)
|
||||
-- extensions as long as we're able to fetch more at
|
||||
-- this precedence level.
|
||||
------------------------------------------------------
|
||||
local e = handle_prefix()
|
||||
repeat
|
||||
local x = handle_suffix (e); e = x or e
|
||||
local y = handle_infix (e); e = y or e
|
||||
until not (x or y)
|
||||
|
||||
-- No transform: it already happened in operators handling
|
||||
return e
|
||||
end --</expr.parse>
|
||||
|
||||
-------------------------------------------------------------------
|
||||
-- Construction
|
||||
-------------------------------------------------------------------
|
||||
if not p.primary then p.primary=p[1]; p[1]=nil end
|
||||
for _, t in ipairs{ "primary", "prefix", "infix", "suffix" } do
|
||||
if not p[t] then p[t] = { } end
|
||||
if not is_parser(p[t]) then multisequence(p[t]) end
|
||||
end
|
||||
function p:add(...) return self.primary:add(...) end
|
||||
return p
|
||||
end --</expr>
|
||||
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
--
|
||||
-- List parser generator
|
||||
--
|
||||
-------------------------------------------------------------------------------
|
||||
-- In [p], the following fields can be provided in input:
|
||||
--
|
||||
-- * [builder]: takes list of subparser results, returns AST
|
||||
-- * [transformers]: as usual
|
||||
-- * [name]: as usual
|
||||
--
|
||||
-- * [terminators]: list of strings representing the keywords which
|
||||
-- might mark the end of the list. When non-empty, the list is
|
||||
-- allowed to be empty. A string is treated as a single-element
|
||||
-- table, whose element is that string, e.g. ["do"] is the same as
|
||||
-- [{"do"}].
|
||||
--
|
||||
-- * [separators]: list of strings representing the keywords which can
|
||||
-- separate elements of the list. When non-empty, one of these
|
||||
-- keyword has to be found between each element. Lack of a separator
|
||||
-- indicates the end of the list. A string is treated as a
|
||||
-- single-element table, whose element is that string, e.g. ["do"]
|
||||
-- is the same as [{"do"}]. If [terminators] is empty/nil, then
|
||||
-- [separators] has to be non-empty.
|
||||
--
|
||||
-- After creation, the following fields are added:
|
||||
-- * [parse] the parsing function lexer->AST
|
||||
-- * [kind] == "list"
|
||||
--
|
||||
-------------------------------------------------------------------------------
|
||||
function list (p)
|
||||
make_parser ("list", p)
|
||||
|
||||
-------------------------------------------------------------------
|
||||
-- Parsing method
|
||||
-------------------------------------------------------------------
|
||||
function p:parse (lx)
|
||||
|
||||
------------------------------------------------------
|
||||
-- Used to quickly check whether there's a terminator
|
||||
-- or a separator immediately ahead
|
||||
------------------------------------------------------
|
||||
local function peek_is_in (keywords)
|
||||
return keywords and lx:is_keyword(lx:peek(), unpack(keywords)) end
|
||||
|
||||
local x = { }
|
||||
local fli = lx:lineinfo_right()
|
||||
|
||||
-- if there's a terminator to start with, don't bother trying
|
||||
if not peek_is_in (self.terminators) then
|
||||
repeat table.insert (x, self.primary (lx)) -- read one element
|
||||
until
|
||||
-- First reason to stop: There's a separator list specified,
|
||||
-- and next token isn't one. Otherwise, consume it with [lx:next()]
|
||||
self.separators and not(peek_is_in (self.separators) and lx:next()) or
|
||||
-- Other reason to stop: terminator token ahead
|
||||
peek_is_in (self.terminators) or
|
||||
-- Last reason: end of file reached
|
||||
lx:peek().tag=="Eof"
|
||||
end
|
||||
|
||||
local lli = lx:lineinfo_left()
|
||||
|
||||
-- Apply the builder. It can be a string, or a callable value,
|
||||
-- or simply nothing.
|
||||
local b = self.builder
|
||||
if b then
|
||||
if type(b)=="string" then x.tag = b -- b is a string, use it as a tag
|
||||
elseif type(b)=="function" then x=b(x)
|
||||
else
|
||||
local bmt = getmetatable(b)
|
||||
if bmt and bmt.__call then x=b(x) end
|
||||
end
|
||||
end
|
||||
return transform (x, self, fli, lli)
|
||||
end --</list.parse>
|
||||
|
||||
-------------------------------------------------------------------
|
||||
-- Construction
|
||||
-------------------------------------------------------------------
|
||||
if not p.primary then p.primary = p[1]; p[1] = nil end
|
||||
if type(p.terminators) == "string" then p.terminators = { p.terminators }
|
||||
elseif p.terminators and #p.terminators == 0 then p.terminators = nil end
|
||||
if type(p.separators) == "string" then p.separators = { p.separators }
|
||||
elseif p.separators and #p.separators == 0 then p.separators = nil end
|
||||
|
||||
return p
|
||||
end --</list>
|
||||
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
--
|
||||
-- Keyword-conditionned parser generator
|
||||
--
|
||||
-------------------------------------------------------------------------------
|
||||
--
|
||||
-- Only apply a parser if a given keyword is found. The result of
|
||||
-- [gg.onkeyword] parser is the result of the subparser (modulo
|
||||
-- [transformers] applications).
|
||||
--
|
||||
-- lineinfo: the keyword is *not* included in the boundaries of the
|
||||
-- resulting lineinfo. A review of all usages of gg.onkeyword() in the
|
||||
-- implementation of metalua has shown that it was the appropriate choice
|
||||
-- in every case.
|
||||
--
|
||||
-- Input fields:
|
||||
--
|
||||
-- * [name]: as usual
|
||||
--
|
||||
-- * [transformers]: as usual
|
||||
--
|
||||
-- * [peek]: if non-nil, the conditionning keyword is left in the lexeme
|
||||
-- stream instead of being consumed.
|
||||
--
|
||||
-- * [primary]: the subparser.
|
||||
--
|
||||
-- * [keywords]: list of strings representing triggering keywords.
|
||||
--
|
||||
-- * Table-part entries can contain strings, and/or exactly one parser.
|
||||
-- Strings are put in [keywords], and the parser is put in [primary].
|
||||
--
|
||||
-- After the call, the following fields will be set:
|
||||
--
|
||||
-- * [parse] the parsing method
|
||||
-- * [kind] == "onkeyword"
|
||||
-- * [primary]
|
||||
-- * [keywords]
|
||||
--
|
||||
-------------------------------------------------------------------------------
|
||||
function onkeyword (p)
|
||||
make_parser ("onkeyword", p)
|
||||
|
||||
-------------------------------------------------------------------
|
||||
-- Parsing method
|
||||
-------------------------------------------------------------------
|
||||
function p:parse(lx)
|
||||
if lx:is_keyword (lx:peek(), unpack(self.keywords)) then
|
||||
--local fli = lx:lineinfo_right()
|
||||
if not self.peek then lx:next() end
|
||||
local content = self.primary (lx)
|
||||
--local lli = lx:lineinfo_left()
|
||||
local fli, lli = content.lineinfo.first, content.lineinfo.last
|
||||
return transform (content, p, fli, lli)
|
||||
else return false end
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------
|
||||
-- Construction
|
||||
-------------------------------------------------------------------
|
||||
if not p.keywords then p.keywords = { } end
|
||||
for _, x in ipairs(p) do
|
||||
if type(x)=="string" then table.insert (p.keywords, x)
|
||||
else assert (not p.primary and is_parser (x)); p.primary = x end
|
||||
end
|
||||
if not next (p.keywords) then
|
||||
eprintf("Warning, no keyword to trigger gg.onkeyword") end
|
||||
assert (p.primary, 'no primary parser in gg.onkeyword')
|
||||
return p
|
||||
end --</onkeyword>
|
||||
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
--
|
||||
-- Optional keyword consummer pseudo-parser generator
|
||||
--
|
||||
-------------------------------------------------------------------------------
|
||||
--
|
||||
-- This doesn't return a real parser, just a function. That function parses
|
||||
-- one of the keywords passed as parameters, and returns it. It returns
|
||||
-- [false] if no matching keyword is found.
|
||||
--
|
||||
-- Notice that tokens returned by lexer already carry lineinfo, therefore
|
||||
-- there's no need to add them, as done usually through transform() calls.
|
||||
-------------------------------------------------------------------------------
|
||||
function optkeyword (...)
|
||||
local args = {...}
|
||||
if type (args[1]) == "table" then
|
||||
assert (#args == 1)
|
||||
args = args[1]
|
||||
end
|
||||
for _, v in ipairs(args) do assert (type(v)=="string") end
|
||||
return function (lx)
|
||||
local x = lx:is_keyword (lx:peek(), unpack (args))
|
||||
if x then lx:next(); return x
|
||||
else return false end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
--
|
||||
-- Run a parser with a special lexer
|
||||
--
|
||||
-------------------------------------------------------------------------------
|
||||
--
|
||||
-- This doesn't return a real parser, just a function.
|
||||
-- First argument is the lexer class to be used with the parser,
|
||||
-- 2nd is the parser itself.
|
||||
-- The resulting parser returns whatever the argument parser does.
|
||||
--
|
||||
-------------------------------------------------------------------------------
|
||||
function with_lexer(new_lexer, parser)
|
||||
|
||||
-------------------------------------------------------------------
|
||||
-- Most gg functions take their parameters in a table, so it's
|
||||
-- better to silently accept when with_lexer{ } is called with
|
||||
-- its arguments in a list:
|
||||
-------------------------------------------------------------------
|
||||
if not parser and #new_lexer==2 and type(new_lexer[1])=='table' then
|
||||
return with_lexer(unpack(new_lexer))
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------
|
||||
-- Save the current lexer, switch it for the new one, run the parser,
|
||||
-- restore the previous lexer, even if the parser caused an error.
|
||||
-------------------------------------------------------------------
|
||||
return function (lx)
|
||||
local old_lexer = getmetatable(lx)
|
||||
lx:sync()
|
||||
setmetatable(lx, new_lexer)
|
||||
local status, result = pcall(parser, lx)
|
||||
lx:sync()
|
||||
setmetatable(lx, old_lexer)
|
||||
if status then return result else error(result) end
|
||||
end
|
||||
end
|
||||
1034
lualibs/metalua/lcode.lua
Normal file
1034
lualibs/metalua/lcode.lua
Normal file
File diff suppressed because it is too large
Load Diff
441
lualibs/metalua/ldump.lua
Normal file
441
lualibs/metalua/ldump.lua
Normal file
@@ -0,0 +1,441 @@
|
||||
----------------------------------------------------------------------
|
||||
--
|
||||
-- WARNING! You're entering a hackish area, proceed at your own risks!
|
||||
--
|
||||
-- This code results from the borrowing, then ruthless abuse, of
|
||||
-- Yueliang's implementation of Lua 5.0 compiler. I claim
|
||||
-- responsibility for all of the ugly, dirty stuff that you might spot
|
||||
-- in it.
|
||||
--
|
||||
-- Eventually, this code will be rewritten, either in Lua or more
|
||||
-- probably in C. Meanwhile, if you're interested into digging
|
||||
-- metalua's sources, this is not the best part to invest your time
|
||||
-- on.
|
||||
--
|
||||
-- End of warning.
|
||||
--
|
||||
----------------------------------------------------------------------
|
||||
|
||||
--[[--------------------------------------------------------------------
|
||||
|
||||
$Id$
|
||||
|
||||
ldump.lua
|
||||
Save bytecodes in Lua
|
||||
This file is part of Yueliang.
|
||||
|
||||
Copyright (c) 2005 Kein-Hong Man <khman@users.sf.net>
|
||||
The COPYRIGHT file describes the conditions
|
||||
under which this software may be distributed.
|
||||
|
||||
------------------------------------------------------------------------
|
||||
|
||||
[FF] Slightly modified, mainly to produce Lua 5.1 bytecode.
|
||||
|
||||
----------------------------------------------------------------------]]
|
||||
|
||||
--[[--------------------------------------------------------------------
|
||||
-- Notes:
|
||||
-- * LUA_NUMBER (double), byte order (little endian) and some other
|
||||
-- header values hard-coded; see other notes below...
|
||||
-- * One significant difference is that instructions are still in table
|
||||
-- form (with OP/A/B/C/Bx fields) and luaP:Instruction() is needed to
|
||||
-- convert them into 4-char strings
|
||||
-- * Deleted:
|
||||
-- luaU:DumpVector: folded into DumpLines, DumpCode
|
||||
-- * Added:
|
||||
-- luaU:endianness() (from lundump.c)
|
||||
-- luaU:make_setS: create a chunk writer that writes to a string
|
||||
-- luaU:make_setF: create a chunk writer that writes to a file
|
||||
-- (lua.h contains a typedef for a Chunkwriter pointer, and
|
||||
-- a Lua-based implementation exists, writer() in lstrlib.c)
|
||||
-- luaU:from_double(x): encode double value for writing
|
||||
-- luaU:from_int(x): encode integer value for writing
|
||||
-- (error checking is limited for these conversion functions)
|
||||
-- (double conversion does not support denormals or NaNs)
|
||||
-- luaU:ttype(o) (from lobject.h)
|
||||
----------------------------------------------------------------------]]
|
||||
|
||||
module("bytecode", package.seeall)
|
||||
|
||||
format = { }
|
||||
format.header = string.dump(function()end):sub(1, 12)
|
||||
format.little_endian, format.int_size,
|
||||
format.size_t_size, format.instr_size,
|
||||
format.number_size, format.integral = format.header:byte(7, 12)
|
||||
format.little_endian = format.little_endian~=0
|
||||
format.integral = format.integral ~=0
|
||||
|
||||
assert(format.integral or format.number_size==8, "Number format not supported by dumper")
|
||||
assert(format.little_endian, "Big endian architectures not supported by dumper")
|
||||
|
||||
--requires luaP
|
||||
luaU = {}
|
||||
|
||||
-- constants used by dumper
|
||||
luaU.LUA_TNIL = 0
|
||||
luaU.LUA_TBOOLEAN = 1
|
||||
luaU.LUA_TNUMBER = 3 -- (all in lua.h)
|
||||
luaU.LUA_TSTRING = 4
|
||||
luaU.LUA_TNONE = -1
|
||||
|
||||
-- definitions for headers of binary files
|
||||
--luaU.LUA_SIGNATURE = "\27Lua" -- binary files start with "<esc>Lua"
|
||||
--luaU.VERSION = 81 -- 0x50; last format change was in 5.0
|
||||
--luaU.FORMAT_VERSION = 0 -- 0 is official version. yeah I know I'm a liar.
|
||||
|
||||
-- a multiple of PI for testing native format
|
||||
-- multiplying by 1E7 gives non-trivial integer values
|
||||
--luaU.TEST_NUMBER = 3.14159265358979323846E7
|
||||
|
||||
--[[--------------------------------------------------------------------
|
||||
-- Additional functions to handle chunk writing
|
||||
-- * to use make_setS and make_setF, see test_ldump.lua elsewhere
|
||||
----------------------------------------------------------------------]]
|
||||
|
||||
------------------------------------------------------------------------
|
||||
-- works like the lobject.h version except that TObject used in these
|
||||
-- scripts only has a 'value' field, no 'tt' field (native types used)
|
||||
------------------------------------------------------------------------
|
||||
function luaU:ttype(o)
|
||||
local tt = type(o.value)
|
||||
if tt == "number" then return self.LUA_TNUMBER
|
||||
elseif tt == "string" then return self.LUA_TSTRING
|
||||
elseif tt == "nil" then return self.LUA_TNIL
|
||||
elseif tt == "boolean" then return self.LUA_TBOOLEAN
|
||||
else
|
||||
return self.LUA_TNONE -- the rest should not appear
|
||||
end
|
||||
end
|
||||
|
||||
------------------------------------------------------------------------
|
||||
-- create a chunk writer that writes to a string
|
||||
-- * returns the writer function and a table containing the string
|
||||
-- * to get the final result, look in buff.data
|
||||
------------------------------------------------------------------------
|
||||
function luaU:make_setS()
|
||||
local buff = {}
|
||||
buff.data = ""
|
||||
local writer =
|
||||
function(s, buff) -- chunk writer
|
||||
if not s then return end
|
||||
buff.data = buff.data..s
|
||||
end
|
||||
return writer, buff
|
||||
end
|
||||
|
||||
------------------------------------------------------------------------
|
||||
-- create a chunk writer that writes to a file
|
||||
-- * returns the writer function and a table containing the file handle
|
||||
-- * if a nil is passed, then writer should close the open file
|
||||
------------------------------------------------------------------------
|
||||
function luaU:make_setF(filename)
|
||||
local buff = {}
|
||||
buff.h = io.open(filename, "wb")
|
||||
if not buff.h then return nil end
|
||||
local writer =
|
||||
function(s, buff) -- chunk writer
|
||||
if not buff.h then return end
|
||||
if not s then buff.h:close(); return end
|
||||
buff.h:write(s)
|
||||
end
|
||||
return writer, buff
|
||||
end
|
||||
|
||||
-----------------------------------------------------------------------
|
||||
-- converts a IEEE754 double number to an 8-byte little-endian string
|
||||
-- * luaU:from_double() and luaU:from_int() are from ChunkBake project
|
||||
-- * supports +/- Infinity, but not denormals or NaNs
|
||||
-----------------------------------------------------------------------
|
||||
function luaU:from_double(x)
|
||||
local function grab_byte(v)
|
||||
return math.floor(v / 256),
|
||||
string.char(math.mod(math.floor(v), 256))
|
||||
end
|
||||
local sign = 0
|
||||
if x < 0 then sign = 1; x = -x end
|
||||
local mantissa, exponent = math.frexp(x)
|
||||
if x == 0 then -- zero
|
||||
mantissa, exponent = 0, 0
|
||||
elseif x == 1/0 then
|
||||
mantissa, exponent = 0, 2047
|
||||
else
|
||||
mantissa = (mantissa * 2 - 1) * math.ldexp(0.5, 53)
|
||||
exponent = exponent + 1022
|
||||
end
|
||||
local v, byte = "" -- convert to bytes
|
||||
x = mantissa
|
||||
for i = 1,6 do
|
||||
x, byte = grab_byte(x); v = v..byte -- 47:0
|
||||
end
|
||||
x, byte = grab_byte(exponent * 16 + x); v = v..byte -- 55:48
|
||||
x, byte = grab_byte(sign * 128 + x); v = v..byte -- 63:56
|
||||
return v
|
||||
end
|
||||
|
||||
-----------------------------------------------------------------------
|
||||
-- converts a number to a little-endian 32-bit integer string
|
||||
-- * input value assumed to not overflow, can be signed/unsigned
|
||||
-----------------------------------------------------------------------
|
||||
function luaU:from_int(x, size)
|
||||
local v = ""
|
||||
x = math.floor(x)
|
||||
if x >= 0 then
|
||||
for i = 1, size do
|
||||
v = v..string.char(math.mod(x, 256)); x = math.floor(x / 256)
|
||||
end
|
||||
else -- x < 0
|
||||
x = -x
|
||||
local carry = 1
|
||||
for i = 1, size do
|
||||
local c = 255 - math.mod(x, 256) + carry
|
||||
if c == 256 then c = 0; carry = 1 else carry = 0 end
|
||||
v = v..string.char(c); x = math.floor(x / 256)
|
||||
end
|
||||
end
|
||||
return v
|
||||
end
|
||||
|
||||
--[[--------------------------------------------------------------------
|
||||
-- Functions to make a binary chunk
|
||||
-- * many functions have the size parameter removed, since output is
|
||||
-- in the form of a string and some sizes are implicit or hard-coded
|
||||
-- * luaU:DumpVector has been deleted (used in DumpCode & DumpLines)
|
||||
----------------------------------------------------------------------]]
|
||||
|
||||
------------------------------------------------------------------------
|
||||
-- dump a block of literal bytes
|
||||
------------------------------------------------------------------------
|
||||
function luaU:DumpLiteral(s, D) self:DumpBlock(s, D) end
|
||||
|
||||
--[[--------------------------------------------------------------------
|
||||
-- struct DumpState:
|
||||
-- L -- lua_State (not used in this script)
|
||||
-- write -- lua_Chunkwriter (chunk writer function)
|
||||
-- data -- void* (chunk writer context or data already written)
|
||||
----------------------------------------------------------------------]]
|
||||
|
||||
------------------------------------------------------------------------
|
||||
-- dumps a block of bytes
|
||||
-- * lua_unlock(D.L), lua_lock(D.L) deleted
|
||||
------------------------------------------------------------------------
|
||||
function luaU:DumpBlock(b, D) D.write(b, D.data) end
|
||||
|
||||
------------------------------------------------------------------------
|
||||
-- dumps a single byte
|
||||
------------------------------------------------------------------------
|
||||
function luaU:DumpByte(y, D)
|
||||
self:DumpBlock(string.char(y), D)
|
||||
end
|
||||
|
||||
------------------------------------------------------------------------
|
||||
-- dumps a 32-bit signed integer (for int)
|
||||
------------------------------------------------------------------------
|
||||
function luaU:DumpInt(x, D)
|
||||
self:DumpBlock(self:from_int(x, format.int_size), D)
|
||||
end
|
||||
|
||||
------------------------------------------------------------------------
|
||||
-- dumps a 32-bit unsigned integer (for size_t)
|
||||
------------------------------------------------------------------------
|
||||
function luaU:DumpSize(x, D)
|
||||
self:DumpBlock(self:from_int(x, format.size_t_size), D)
|
||||
end
|
||||
|
||||
------------------------------------------------------------------------
|
||||
-- dumps a LUA_NUMBER (hard-coded as a double)
|
||||
------------------------------------------------------------------------
|
||||
function luaU:DumpNumber(x, D)
|
||||
if format.integral then
|
||||
self:DumpBlock(self:from_int(x, format.number_size), D)
|
||||
else
|
||||
self:DumpBlock(self:from_double(x), D)
|
||||
end
|
||||
end
|
||||
|
||||
------------------------------------------------------------------------
|
||||
-- dumps a Lua string
|
||||
------------------------------------------------------------------------
|
||||
function luaU:DumpString(s, D)
|
||||
if s == nil then
|
||||
self:DumpSize(0, D)
|
||||
else
|
||||
s = s.."\0" -- include trailing '\0'
|
||||
self:DumpSize(string.len(s), D)
|
||||
self:DumpBlock(s, D)
|
||||
end
|
||||
end
|
||||
|
||||
------------------------------------------------------------------------
|
||||
-- dumps instruction block from function prototype
|
||||
------------------------------------------------------------------------
|
||||
function luaU:DumpCode(f, D)
|
||||
local n = f.sizecode
|
||||
self:DumpInt(n, D)
|
||||
--was DumpVector
|
||||
for i = 0, n - 1 do
|
||||
self:DumpBlock(luaP:Instruction(f.code[i]), D)
|
||||
end
|
||||
end
|
||||
|
||||
------------------------------------------------------------------------
|
||||
-- dumps local variable names from function prototype
|
||||
------------------------------------------------------------------------
|
||||
function luaU:DumpLocals(f, D)
|
||||
local n = f.sizelocvars
|
||||
self:DumpInt(n, D)
|
||||
for i = 0, n - 1 do
|
||||
-- Dirty temporary fix:
|
||||
-- `Stat{ } keeps properly count of the number of local vars,
|
||||
-- but fails to keep score of their debug info (names).
|
||||
-- It therefore might happen that #f.localvars < f.sizelocvars, or
|
||||
-- that a variable's startpc and endpc fields are left unset.
|
||||
-- FIXME: This might not be needed anymore, check the bug report
|
||||
-- by J. Belmonte.
|
||||
local var = f.locvars[i]
|
||||
if not var then break end
|
||||
-- printf("[DUMPLOCALS] dumping local var #%i = %s", i, table.tostring(var))
|
||||
self:DumpString(var.varname, D)
|
||||
self:DumpInt(var.startpc or 0, D)
|
||||
self:DumpInt(var.endpc or 0, D)
|
||||
end
|
||||
end
|
||||
|
||||
------------------------------------------------------------------------
|
||||
-- dumps line information from function prototype
|
||||
------------------------------------------------------------------------
|
||||
function luaU:DumpLines(f, D)
|
||||
local n = f.sizelineinfo
|
||||
self:DumpInt(n, D)
|
||||
--was DumpVector
|
||||
for i = 0, n - 1 do
|
||||
self:DumpInt(f.lineinfo[i], D) -- was DumpBlock
|
||||
--print(i, f.lineinfo[i])
|
||||
end
|
||||
end
|
||||
|
||||
------------------------------------------------------------------------
|
||||
-- dump upvalue names from function prototype
|
||||
------------------------------------------------------------------------
|
||||
function luaU:DumpUpvalues(f, D)
|
||||
local n = f.sizeupvalues
|
||||
self:DumpInt(n, D)
|
||||
for i = 0, n - 1 do
|
||||
self:DumpString(f.upvalues[i], D)
|
||||
end
|
||||
end
|
||||
|
||||
------------------------------------------------------------------------
|
||||
-- dump constant pool from function prototype
|
||||
-- * nvalue(o) and tsvalue(o) macros removed
|
||||
------------------------------------------------------------------------
|
||||
function luaU:DumpConstants(f, D)
|
||||
local n = f.sizek
|
||||
self:DumpInt(n, D)
|
||||
for i = 0, n - 1 do
|
||||
local o = f.k[i] -- TObject
|
||||
local tt = self:ttype(o)
|
||||
assert (tt >= 0)
|
||||
self:DumpByte(tt, D)
|
||||
if tt == self.LUA_TNUMBER then
|
||||
self:DumpNumber(o.value, D)
|
||||
elseif tt == self.LUA_TSTRING then
|
||||
self:DumpString(o.value, D)
|
||||
elseif tt == self.LUA_TBOOLEAN then
|
||||
self:DumpByte (o.value and 1 or 0, D)
|
||||
elseif tt == self.LUA_TNIL then
|
||||
else
|
||||
assert(false) -- cannot happen
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function luaU:DumpProtos (f, D)
|
||||
local n = f.sizep
|
||||
assert (n)
|
||||
self:DumpInt(n, D)
|
||||
for i = 0, n - 1 do
|
||||
self:DumpFunction(f.p[i], f.source, D)
|
||||
end
|
||||
end
|
||||
|
||||
function luaU:DumpDebug(f, D)
|
||||
self:DumpLines(f, D)
|
||||
self:DumpLocals(f, D)
|
||||
self:DumpUpvalues(f, D)
|
||||
end
|
||||
|
||||
|
||||
------------------------------------------------------------------------
|
||||
-- dump child function prototypes from function prototype
|
||||
--FF completely reworked for 5.1 format
|
||||
------------------------------------------------------------------------
|
||||
function luaU:DumpFunction(f, p, D)
|
||||
-- print "Dumping function:"
|
||||
-- table.print(f, 60)
|
||||
|
||||
local source = f.source
|
||||
if source == p then source = nil end
|
||||
self:DumpString(source, D)
|
||||
self:DumpInt(f.lineDefined, D)
|
||||
self:DumpInt(f.lastLineDefined or 42, D)
|
||||
self:DumpByte(f.nups, D)
|
||||
self:DumpByte(f.numparams, D)
|
||||
self:DumpByte(f.is_vararg, D)
|
||||
self:DumpByte(f.maxstacksize, D)
|
||||
self:DumpCode(f, D)
|
||||
self:DumpConstants(f, D)
|
||||
self:DumpProtos( f, D)
|
||||
self:DumpDebug(f, D)
|
||||
end
|
||||
|
||||
------------------------------------------------------------------------
|
||||
-- dump Lua header section (some sizes hard-coded)
|
||||
--FF: updated for version 5.1
|
||||
------------------------------------------------------------------------
|
||||
function luaU:DumpHeader(D)
|
||||
self:DumpLiteral(format.header, D)
|
||||
end
|
||||
|
||||
------------------------------------------------------------------------
|
||||
-- dump function as precompiled chunk
|
||||
-- * w, data are created from make_setS, make_setF
|
||||
--FF: suppressed extraneous [L] param
|
||||
------------------------------------------------------------------------
|
||||
function luaU:dump (Main, w, data)
|
||||
local D = {} -- DumpState
|
||||
D.write = w
|
||||
D.data = data
|
||||
self:DumpHeader(D)
|
||||
self:DumpFunction(Main, nil, D)
|
||||
-- added: for a chunk writer writing to a file, this final call with
|
||||
-- nil data is to indicate to the writer to close the file
|
||||
D.write(nil, D.data)
|
||||
end
|
||||
|
||||
------------------------------------------------------------------------
|
||||
-- find byte order (from lundump.c)
|
||||
-- * hard-coded to little-endian
|
||||
------------------------------------------------------------------------
|
||||
function luaU:endianness()
|
||||
return 1
|
||||
end
|
||||
|
||||
-- FIXME: ugly concat-base generation in [make_setS], bufferize properly!
|
||||
function dump_string (proto)
|
||||
local writer, buff = luaU:make_setS()
|
||||
luaU:dump (proto, writer, buff)
|
||||
return buff.data
|
||||
end
|
||||
|
||||
-- FIXME: [make_setS] sucks, perform synchronous file writing
|
||||
-- Now unused
|
||||
function dump_file (proto, filename)
|
||||
local writer, buff = luaU:make_setS()
|
||||
luaU:dump (proto, writer, buff)
|
||||
local file = io.open (filename, "wb")
|
||||
file:write (buff.data)
|
||||
io.close(file)
|
||||
if UNIX_SHARPBANG then os.execute ("chmod a+x "..filename) end
|
||||
end
|
||||
510
lualibs/metalua/lexer.lua
Normal file
510
lualibs/metalua/lexer.lua
Normal file
@@ -0,0 +1,510 @@
|
||||
----------------------------------------------------------------------
|
||||
-- Metalua: $Id: mll.lua,v 1.3 2006/11/15 09:07:50 fab13n Exp $
|
||||
--
|
||||
-- Summary: generic Lua-style lexer definition. You need this plus
|
||||
-- some keyword additions to create the complete Lua lexer,
|
||||
-- as is done in mlp_lexer.lua.
|
||||
--
|
||||
-- TODO:
|
||||
--
|
||||
-- * Make it easy to define new flavors of strings. Replacing the
|
||||
-- lexer.patterns.long_string regexp by an extensible list, with
|
||||
-- customizable token tag, would probably be enough. Maybe add:
|
||||
-- + an index of capture for the regexp, that would specify
|
||||
-- which capture holds the content of the string-like token
|
||||
-- + a token tag
|
||||
-- + or a string->string transformer function.
|
||||
--
|
||||
-- * There are some _G.table to prevent a namespace clash which has
|
||||
-- now disappered. remove them.
|
||||
----------------------------------------------------------------------
|
||||
--
|
||||
-- Copyright (c) 2006, Fabien Fleutot <metalua@gmail.com>.
|
||||
--
|
||||
-- This software is released under the MIT Licence, see licence.txt
|
||||
-- for details.
|
||||
--
|
||||
----------------------------------------------------------------------
|
||||
|
||||
module ("lexer", package.seeall)
|
||||
|
||||
require 'metalua.runtime'
|
||||
|
||||
|
||||
lexer = { alpha={ }, sym={ } }
|
||||
lexer.__index=lexer
|
||||
|
||||
local debugf = function() end
|
||||
--local debugf=printf
|
||||
|
||||
----------------------------------------------------------------------
|
||||
-- Patterns used by [lexer:extract] to decompose the raw string into
|
||||
-- correctly tagged tokens.
|
||||
----------------------------------------------------------------------
|
||||
lexer.patterns = {
|
||||
spaces = "^[ \r\n\t]*()",
|
||||
short_comment = "^%-%-([^\n]*)()\n",
|
||||
final_short_comment = "^%-%-([^\n]*)()$",
|
||||
long_comment = "^%-%-%[(=*)%[\n?(.-)%]%1%]()",
|
||||
long_string = "^%[(=*)%[\n?(.-)%]%1%]()",
|
||||
number_mantissa = { "^%d+%.?%d*()", "^%d*%.%d+()" },
|
||||
number_exponant = "^[eE][%+%-]?%d+()",
|
||||
number_hex = "^0[xX]%x+()",
|
||||
word = "^([%a_][%w_]*)()"
|
||||
}
|
||||
|
||||
----------------------------------------------------------------------
|
||||
-- unescape a whole string, applying [unesc_digits] and
|
||||
-- [unesc_letter] as many times as required.
|
||||
----------------------------------------------------------------------
|
||||
local function unescape_string (s)
|
||||
|
||||
-- Turn the digits of an escape sequence into the corresponding
|
||||
-- character, e.g. [unesc_digits("123") == string.char(123)].
|
||||
local function unesc_digits (backslashes, digits)
|
||||
if #backslashes%2==0 then
|
||||
-- Even number of backslashes, they escape each other, not the digits.
|
||||
-- Return them so that unesc_letter() can treaat them
|
||||
return backslashes..digits
|
||||
else
|
||||
-- Remove the odd backslash, which escapes the number sequence.
|
||||
-- The rest will be returned and parsed by unesc_letter()
|
||||
backslashes = backslashes :sub (1,-2)
|
||||
end
|
||||
local k, j, i = digits:reverse():byte(1, 3)
|
||||
local z = _G.string.byte "0"
|
||||
local code = (k or z) + 10*(j or z) + 100*(i or z) - 111*z
|
||||
if code > 255 then
|
||||
error ("Illegal escape sequence '\\"..digits..
|
||||
"' in string: ASCII codes must be in [0..255]")
|
||||
end
|
||||
return backslashes .. string.char (code)
|
||||
end
|
||||
|
||||
-- Take a letter [x], and returns the character represented by the
|
||||
-- sequence ['\\'..x], e.g. [unesc_letter "n" == "\n"].
|
||||
local function unesc_letter(x)
|
||||
local t = {
|
||||
a = "\a", b = "\b", f = "\f",
|
||||
n = "\n", r = "\r", t = "\t", v = "\v",
|
||||
["\\"] = "\\", ["'"] = "'", ['"'] = '"', ["\n"] = "\n" }
|
||||
return t[x] or error([[Unknown escape sequence '\]]..x..[[']])
|
||||
end
|
||||
|
||||
return s
|
||||
:gsub ("(\\+)([0-9][0-9]?[0-9]?)", unesc_digits)
|
||||
:gsub ("\\(%D)",unesc_letter)
|
||||
end
|
||||
|
||||
lexer.extractors = {
|
||||
"skip_whitespaces_and_comments",
|
||||
"extract_short_string", "extract_word", "extract_number",
|
||||
"extract_long_string", "extract_symbol" }
|
||||
|
||||
lexer.token_metatable = {
|
||||
-- __tostring = function(a)
|
||||
-- return string.format ("`%s{'%s'}",a.tag, a[1])
|
||||
-- end
|
||||
}
|
||||
|
||||
lexer.lineinfo_metatable = { }
|
||||
|
||||
----------------------------------------------------------------------
|
||||
-- Really extract next token fron the raw string
|
||||
-- (and update the index).
|
||||
-- loc: offset of the position just after spaces and comments
|
||||
-- previous_i: offset in src before extraction began
|
||||
----------------------------------------------------------------------
|
||||
function lexer:extract ()
|
||||
local previous_i = self.i
|
||||
local loc = self.i
|
||||
local eof, token
|
||||
|
||||
-- Put line info, comments and metatable around the tag and content
|
||||
-- provided by extractors, thus returning a complete lexer token.
|
||||
-- first_line: line # at the beginning of token
|
||||
-- first_column_offset: char # of the last '\n' before beginning of token
|
||||
-- i: scans from beginning of prefix spaces/comments to end of token.
|
||||
local function build_token (tag, content)
|
||||
assert (tag and content)
|
||||
local i, first_line, first_column_offset, previous_line_length =
|
||||
previous_i, self.line, self.column_offset, nil
|
||||
|
||||
-- update self.line and first_line. i := indexes of '\n' chars
|
||||
while true do
|
||||
i = self.src :find ("\n", i+1, true)
|
||||
if not i or i>self.i then break end -- no more '\n' until end of token
|
||||
previous_line_length = i - self.column_offset
|
||||
if loc and i <= loc then -- '\n' before beginning of token
|
||||
first_column_offset = i
|
||||
first_line = first_line+1
|
||||
end
|
||||
self.line = self.line+1
|
||||
self.column_offset = i
|
||||
end
|
||||
|
||||
-- lineinfo entries: [1]=line, [2]=column, [3]=char, [4]=filename
|
||||
local fli = { first_line, loc-first_column_offset, loc, self.src_name }
|
||||
local lli = { self.line, self.i-self.column_offset-1, self.i-1, self.src_name }
|
||||
--Pluto barfes when the metatable is set:(
|
||||
setmetatable(fli, lexer.lineinfo_metatable)
|
||||
setmetatable(lli, lexer.lineinfo_metatable)
|
||||
local a = { tag = tag, lineinfo = { first=fli, last=lli }, content }
|
||||
if lli[2]==-1 then lli[1], lli[2] = lli[1]-1, previous_line_length-1 end
|
||||
if #self.attached_comments > 0 then
|
||||
a.lineinfo.comments = self.attached_comments
|
||||
fli.comments = self.attached_comments
|
||||
if self.lineinfo_last then
|
||||
self.lineinfo_last.comments = self.attached_comments
|
||||
end
|
||||
end
|
||||
self.attached_comments = { }
|
||||
return setmetatable (a, self.token_metatable)
|
||||
end --</function build_token>
|
||||
|
||||
for ext_idx, extractor in ipairs(self.extractors) do
|
||||
-- printf("method = %s", method)
|
||||
local tag, content = self [extractor] (self)
|
||||
-- [loc] is placed just after the leading whitespaces and comments;
|
||||
-- for this to work, the whitespace extractor *must be* at index 1.
|
||||
if ext_idx==1 then loc = self.i end
|
||||
|
||||
if tag then
|
||||
--printf("`%s{ %q }\t%i", tag, content, loc);
|
||||
return build_token (tag, content)
|
||||
end
|
||||
end
|
||||
|
||||
error "None of the lexer extractors returned anything!"
|
||||
end
|
||||
|
||||
----------------------------------------------------------------------
|
||||
-- skip whites and comments
|
||||
-- FIXME: doesn't take into account:
|
||||
-- - unterminated long comments
|
||||
-- - short comments at last line without a final \n
|
||||
----------------------------------------------------------------------
|
||||
function lexer:skip_whitespaces_and_comments()
|
||||
local table_insert = _G.table.insert
|
||||
repeat -- loop as long as a space or comment chunk is found
|
||||
local _, j
|
||||
local again = false
|
||||
local last_comment_content = nil
|
||||
-- skip spaces
|
||||
self.i = self.src:match (self.patterns.spaces, self.i)
|
||||
-- skip a long comment if any
|
||||
_, last_comment_content, j =
|
||||
self.src :match (self.patterns.long_comment, self.i)
|
||||
if j then
|
||||
table_insert(self.attached_comments,
|
||||
{last_comment_content, self.i, j, "long"})
|
||||
self.i=j; again=true
|
||||
end
|
||||
-- skip a short comment if any
|
||||
last_comment_content, j = self.src:match (self.patterns.short_comment, self.i)
|
||||
if j then
|
||||
table_insert(self.attached_comments,
|
||||
{last_comment_content, self.i, j, "short"})
|
||||
self.i=j; again=true
|
||||
end
|
||||
if self.i>#self.src then return "Eof", "eof" end
|
||||
until not again
|
||||
|
||||
if self.src:match (self.patterns.final_short_comment, self.i) then
|
||||
return "Eof", "eof" end
|
||||
--assert (not self.src:match(self.patterns.short_comment, self.i))
|
||||
--assert (not self.src:match(self.patterns.long_comment, self.i))
|
||||
-- --assert (not self.src:match(self.patterns.spaces, self.i))
|
||||
return
|
||||
end
|
||||
|
||||
----------------------------------------------------------------------
|
||||
-- extract a '...' or "..." short string
|
||||
----------------------------------------------------------------------
|
||||
function lexer:extract_short_string()
|
||||
-- [k] is the first unread char, [self.i] points to [k] in [self.src]
|
||||
local j, k = self.i, self.src :sub (self.i,self.i)
|
||||
if k~="'" and k~='"' then return end
|
||||
local i = self.i + 1
|
||||
local j = i
|
||||
while true do
|
||||
-- k = opening char: either simple-quote or double-quote
|
||||
-- i = index of beginning-of-string
|
||||
-- x = next "interesting" character
|
||||
-- j = position after interesting char
|
||||
-- y = char just after x
|
||||
local x, y
|
||||
x, j, y = self.src :match ("([\\\r\n"..k.."])()(.?)", j)
|
||||
if x == '\\' then j=j+1 -- don't parse escaped char
|
||||
elseif x == k then break -- unescaped end of string
|
||||
else -- eof or '\r' or '\n' reached before end of string
|
||||
assert (not x or x=="\r" or x=="\n")
|
||||
error "Unterminated string"
|
||||
end
|
||||
end
|
||||
self.i = j
|
||||
|
||||
return "String", unescape_string (self.src:sub (i,j-2))
|
||||
end
|
||||
|
||||
----------------------------------------------------------------------
|
||||
--
|
||||
----------------------------------------------------------------------
|
||||
function lexer:extract_word()
|
||||
-- Id / keyword
|
||||
local word, j = self.src:match (self.patterns.word, self.i)
|
||||
if word then
|
||||
self.i = j
|
||||
if self.alpha [word] then return "Keyword", word
|
||||
else return "Id", word end
|
||||
end
|
||||
end
|
||||
|
||||
----------------------------------------------------------------------
|
||||
--
|
||||
----------------------------------------------------------------------
|
||||
function lexer:extract_number()
|
||||
-- Number
|
||||
local j = self.src:match(self.patterns.number_hex, self.i)
|
||||
if not j then
|
||||
j = self.src:match (self.patterns.number_mantissa[1], self.i) or
|
||||
self.src:match (self.patterns.number_mantissa[2], self.i)
|
||||
if j then
|
||||
j = self.src:match (self.patterns.number_exponant, j) or j;
|
||||
end
|
||||
end
|
||||
if not j then return end
|
||||
-- Number found, interpret with tonumber() and return it
|
||||
local n = tonumber (self.src:sub (self.i, j-1))
|
||||
self.i = j
|
||||
return "Number", n
|
||||
end
|
||||
|
||||
----------------------------------------------------------------------
|
||||
--
|
||||
----------------------------------------------------------------------
|
||||
function lexer:extract_long_string()
|
||||
-- Long string
|
||||
local _, content, j = self.src:match (self.patterns.long_string, self.i)
|
||||
if j then self.i = j; return "String", content end
|
||||
end
|
||||
|
||||
----------------------------------------------------------------------
|
||||
--
|
||||
----------------------------------------------------------------------
|
||||
function lexer:extract_symbol()
|
||||
-- compound symbol
|
||||
local k = self.src:sub (self.i,self.i)
|
||||
local symk = self.sym [k]
|
||||
if not symk then
|
||||
self.i = self.i + 1
|
||||
return "Keyword", k
|
||||
end
|
||||
for _, sym in pairs (symk) do
|
||||
if sym == self.src:sub (self.i, self.i + #sym - 1) then
|
||||
self.i = self.i + #sym;
|
||||
return "Keyword", sym
|
||||
end
|
||||
end
|
||||
-- single char symbol
|
||||
self.i = self.i+1
|
||||
return "Keyword", k
|
||||
end
|
||||
|
||||
----------------------------------------------------------------------
|
||||
-- Add a keyword to the list of keywords recognized by the lexer.
|
||||
----------------------------------------------------------------------
|
||||
function lexer:add (w, ...)
|
||||
assert(not ..., "lexer:add() takes only one arg, although possibly a table")
|
||||
if type (w) == "table" then
|
||||
for _, x in ipairs (w) do self:add (x) end
|
||||
else
|
||||
if w:match (self.patterns.word .. "$") then self.alpha [w] = true
|
||||
elseif w:match "^%p%p+$" then
|
||||
local k = w:sub(1,1)
|
||||
local list = self.sym [k]
|
||||
if not list then list = { }; self.sym [k] = list end
|
||||
_G.table.insert (list, w)
|
||||
elseif w:match "^%p$" then return
|
||||
else error "Invalid keyword" end
|
||||
end
|
||||
end
|
||||
|
||||
----------------------------------------------------------------------
|
||||
-- Return the [n]th next token, without consumming it.
|
||||
-- [n] defaults to 1. If it goes pass the end of the stream, an EOF
|
||||
-- token is returned.
|
||||
----------------------------------------------------------------------
|
||||
function lexer:peek (n)
|
||||
if not n then n=1 end
|
||||
if n > #self.peeked then
|
||||
for i = #self.peeked+1, n do
|
||||
self.peeked [i] = self:extract()
|
||||
end
|
||||
end
|
||||
return self.peeked [n]
|
||||
end
|
||||
|
||||
----------------------------------------------------------------------
|
||||
-- Return the [n]th next token, removing it as well as the 0..n-1
|
||||
-- previous tokens. [n] defaults to 1. If it goes pass the end of the
|
||||
-- stream, an EOF token is returned.
|
||||
----------------------------------------------------------------------
|
||||
function lexer:next (n)
|
||||
n = n or 1
|
||||
self:peek (n)
|
||||
local a
|
||||
for i=1,n do
|
||||
a = _G.table.remove (self.peeked, 1)
|
||||
if a then
|
||||
--debugf ("lexer:next() ==> %s %s",
|
||||
-- table.tostring(a), tostring(a))
|
||||
end
|
||||
self.lastline = a.lineinfo.last[1]
|
||||
end
|
||||
self.lineinfo_last = a.lineinfo.last
|
||||
return a or eof_token
|
||||
end
|
||||
|
||||
----------------------------------------------------------------------
|
||||
-- Returns an object which saves the stream's current state.
|
||||
----------------------------------------------------------------------
|
||||
-- FIXME there are more fields than that to save
|
||||
function lexer:save () return { self.i; _G.table.cat(self.peeked) } end
|
||||
|
||||
----------------------------------------------------------------------
|
||||
-- Restore the stream's state, as saved by method [save].
|
||||
----------------------------------------------------------------------
|
||||
-- FIXME there are more fields than that to restore
|
||||
function lexer:restore (s) self.i=s[1]; self.peeked=s[2] end
|
||||
|
||||
----------------------------------------------------------------------
|
||||
-- Resynchronize: cancel any token in self.peeked, by emptying the
|
||||
-- list and resetting the indexes
|
||||
----------------------------------------------------------------------
|
||||
function lexer:sync()
|
||||
local p1 = self.peeked[1]
|
||||
if p1 then
|
||||
li = p1.lineinfo.first
|
||||
self.line, self.i = li[1], li[3]
|
||||
self.column_offset = self.i - li[2]
|
||||
self.peeked = { }
|
||||
self.attached_comments = p1.lineinfo.first.comments or { }
|
||||
end
|
||||
end
|
||||
|
||||
----------------------------------------------------------------------
|
||||
-- Take the source and offset of an old lexer.
|
||||
----------------------------------------------------------------------
|
||||
function lexer:takeover(old)
|
||||
self:sync()
|
||||
self.line, self.column_offset, self.i, self.src, self.attached_comments =
|
||||
old.line, old.column_offset, old.i, old.src, old.attached_comments
|
||||
return self
|
||||
end
|
||||
|
||||
-- function lexer:lineinfo()
|
||||
-- if self.peeked[1] then return self.peeked[1].lineinfo.first
|
||||
-- else return { self.line, self.i-self.column_offset, self.i } end
|
||||
-- end
|
||||
|
||||
|
||||
----------------------------------------------------------------------
|
||||
-- Return the current position in the sources. This position is between
|
||||
-- two tokens, and can be within a space / comment area, and therefore
|
||||
-- have a non-null width. :lineinfo_left() returns the beginning of the
|
||||
-- separation area, :lineinfo_right() returns the end of that area.
|
||||
--
|
||||
-- ____ last consummed token ____ first unconsummed token
|
||||
-- / /
|
||||
-- XXXXX <spaces and comments> YYYYY
|
||||
-- \____ \____
|
||||
-- :lineinfo_left() :lineinfo_right()
|
||||
----------------------------------------------------------------------
|
||||
function lexer:lineinfo_right()
|
||||
return self:peek(1).lineinfo.first
|
||||
end
|
||||
|
||||
function lexer:lineinfo_left()
|
||||
return self.lineinfo_last
|
||||
end
|
||||
|
||||
----------------------------------------------------------------------
|
||||
-- Create a new lexstream.
|
||||
----------------------------------------------------------------------
|
||||
function lexer:newstream (src_or_stream, name)
|
||||
name = name or "?"
|
||||
if type(src_or_stream)=='table' then -- it's a stream
|
||||
return setmetatable ({ }, self) :takeover (src_or_stream)
|
||||
elseif type(src_or_stream)=='string' then -- it's a source string
|
||||
local src = src_or_stream
|
||||
local stream = {
|
||||
src_name = name; -- Name of the file
|
||||
src = src; -- The source, as a single string
|
||||
peeked = { }; -- Already peeked, but not discarded yet, tokens
|
||||
i = 1; -- Character offset in src
|
||||
line = 1; -- Current line number
|
||||
column_offset = 0; -- distance from beginning of file to last '\n'
|
||||
attached_comments = { },-- comments accumulator
|
||||
lineinfo_last = { 1, 1, 1, name }
|
||||
}
|
||||
setmetatable (stream, self)
|
||||
|
||||
-- skip initial sharp-bang for unix scripts
|
||||
-- FIXME: redundant with mlp.chunk()
|
||||
if src and src :match "^#" then stream.i = src :find "\n" + 1 end
|
||||
return stream
|
||||
else
|
||||
assert(false, ":newstream() takes a source string or a stream, not a "..
|
||||
type(src_or_stream))
|
||||
end
|
||||
end
|
||||
|
||||
----------------------------------------------------------------------
|
||||
-- if there's no ... args, return the token a (whose truth value is
|
||||
-- true) if it's a `Keyword{ }, or nil. If there are ... args, they
|
||||
-- have to be strings. if the token a is a keyword, and it's content
|
||||
-- is one of the ... args, then returns it (it's truth value is
|
||||
-- true). If no a keyword or not in ..., return nil.
|
||||
----------------------------------------------------------------------
|
||||
function lexer:is_keyword (a, ...)
|
||||
if not a or a.tag ~= "Keyword" then return false end
|
||||
local words = {...}
|
||||
if #words == 0 then return a[1] end
|
||||
for _, w in ipairs (words) do
|
||||
if w == a[1] then return w end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
----------------------------------------------------------------------
|
||||
-- Cause an error if the next token isn't a keyword whose content
|
||||
-- is listed among ... args (which have to be strings).
|
||||
----------------------------------------------------------------------
|
||||
function lexer:check (...)
|
||||
local words = {...}
|
||||
local a = self:next()
|
||||
local function err ()
|
||||
error ("Got " .. tostring (a) ..
|
||||
", expected one of these keywords : '" ..
|
||||
_G.table.concat (words,"', '") .. "'") end
|
||||
|
||||
if not a or a.tag ~= "Keyword" then err () end
|
||||
if #words == 0 then return a[1] end
|
||||
for _, w in ipairs (words) do
|
||||
if w == a[1] then return w end
|
||||
end
|
||||
err ()
|
||||
end
|
||||
|
||||
----------------------------------------------------------------------
|
||||
--
|
||||
----------------------------------------------------------------------
|
||||
function lexer:clone()
|
||||
local clone = {
|
||||
alpha = table.deep_copy(self.alpha),
|
||||
sym = table.deep_copy(self.sym) }
|
||||
setmetatable(clone, self)
|
||||
clone.__index = clone
|
||||
return clone
|
||||
end
|
||||
440
lualibs/metalua/lopcodes.lua
Normal file
440
lualibs/metalua/lopcodes.lua
Normal file
@@ -0,0 +1,440 @@
|
||||
----------------------------------------------------------------------
|
||||
--
|
||||
-- WARNING! You're entering a hackish area, proceed at your own risks!
|
||||
--
|
||||
-- This code results from the borrowing, then ruthless abuse, of
|
||||
-- Yueliang's implementation of Lua 5.0 compiler. I claim
|
||||
-- responsibility for all of the ugly, dirty stuff that you might spot
|
||||
-- in it.
|
||||
--
|
||||
-- Eventually, this code will be rewritten, either in Lua or more
|
||||
-- probably in C. Meanwhile, if you're interested into digging
|
||||
-- metalua's sources, this is not the best part to invest your time
|
||||
-- on.
|
||||
--
|
||||
-- End of warning.
|
||||
--
|
||||
----------------------------------------------------------------------
|
||||
|
||||
--[[--------------------------------------------------------------------
|
||||
|
||||
$Id$
|
||||
|
||||
lopcodes.lua
|
||||
Lua 5 virtual machine opcodes in Lua
|
||||
This file is part of Yueliang.
|
||||
|
||||
Copyright (c) 2005 Kein-Hong Man <khman@users.sf.net>
|
||||
The COPYRIGHT file describes the conditions
|
||||
under which this software may be distributed.
|
||||
|
||||
See the ChangeLog for more information.
|
||||
|
||||
------------------------------------------------------------------------
|
||||
|
||||
[FF] Slightly modified, mainly to produce Lua 5.1 bytecode.
|
||||
|
||||
----------------------------------------------------------------------]]
|
||||
|
||||
--[[--------------------------------------------------------------------
|
||||
-- Notes:
|
||||
-- * an Instruction is a table with OP, A, B, C, Bx elements; this
|
||||
-- should allow instruction handling to work with doubles and ints
|
||||
-- * Added:
|
||||
-- luaP:Instruction(i): convert field elements to a 4-char string
|
||||
-- luaP:DecodeInst(x): convert 4-char string into field elements
|
||||
-- * WARNING luaP:Instruction outputs instructions encoded in little-
|
||||
-- endian form and field size and positions are hard-coded
|
||||
----------------------------------------------------------------------]]
|
||||
|
||||
module("bytecode", package.seeall)
|
||||
|
||||
local function debugf() end
|
||||
|
||||
luaP = { }
|
||||
|
||||
--[[
|
||||
===========================================================================
|
||||
We assume that instructions are unsigned numbers.
|
||||
All instructions have an opcode in the first 6 bits.
|
||||
Instructions can have the following fields:
|
||||
'A' : 8 bits
|
||||
'B' : 9 bits
|
||||
'C' : 9 bits
|
||||
'Bx' : 18 bits ('B' and 'C' together)
|
||||
'sBx' : signed Bx
|
||||
|
||||
A signed argument is represented in excess K; that is, the number
|
||||
value is the unsigned value minus K. K is exactly the maximum value
|
||||
for that argument (so that -max is represented by 0, and +max is
|
||||
represented by 2*max), which is half the maximum for the corresponding
|
||||
unsigned argument.
|
||||
===========================================================================
|
||||
--]]
|
||||
|
||||
luaP.OpMode = {"iABC", "iABx", "iAsBx"} -- basic instruction format
|
||||
|
||||
------------------------------------------------------------------------
|
||||
-- size and position of opcode arguments.
|
||||
-- * WARNING size and position is hard-coded elsewhere in this script
|
||||
------------------------------------------------------------------------
|
||||
luaP.SIZE_C = 9
|
||||
luaP.SIZE_B = 9
|
||||
luaP.SIZE_Bx = luaP.SIZE_C + luaP.SIZE_B
|
||||
luaP.SIZE_A = 8
|
||||
|
||||
luaP.SIZE_OP = 6
|
||||
|
||||
luaP.POS_C = luaP.SIZE_OP
|
||||
luaP.POS_B = luaP.POS_C + luaP.SIZE_C
|
||||
luaP.POS_Bx = luaP.POS_C
|
||||
luaP.POS_A = luaP.POS_B + luaP.SIZE_B
|
||||
|
||||
--FF from 5.1
|
||||
luaP.BITRK = 2^(luaP.SIZE_B - 1)
|
||||
function luaP:ISK(x) return x >= self.BITRK end
|
||||
luaP.MAXINDEXRK = luaP.BITRK - 1
|
||||
function luaP:RKASK(x)
|
||||
if x < self.BITRK then return x+self.BITRK else return x end
|
||||
end
|
||||
|
||||
|
||||
|
||||
------------------------------------------------------------------------
|
||||
-- limits for opcode arguments.
|
||||
-- we use (signed) int to manipulate most arguments,
|
||||
-- so they must fit in BITS_INT-1 bits (-1 for sign)
|
||||
------------------------------------------------------------------------
|
||||
-- removed "#if SIZE_Bx < BITS_INT-1" test, assume this script is
|
||||
-- running on a Lua VM with double or int as LUA_NUMBER
|
||||
|
||||
luaP.MAXARG_Bx = math.ldexp(1, luaP.SIZE_Bx) - 1
|
||||
luaP.MAXARG_sBx = math.floor(luaP.MAXARG_Bx / 2) -- 'sBx' is signed
|
||||
|
||||
luaP.MAXARG_A = math.ldexp(1, luaP.SIZE_A) - 1
|
||||
luaP.MAXARG_B = math.ldexp(1, luaP.SIZE_B) - 1
|
||||
luaP.MAXARG_C = math.ldexp(1, luaP.SIZE_C) - 1
|
||||
|
||||
-- creates a mask with 'n' 1 bits at position 'p'
|
||||
-- MASK1(n,p) deleted
|
||||
-- creates a mask with 'n' 0 bits at position 'p'
|
||||
-- MASK0(n,p) deleted
|
||||
|
||||
--[[--------------------------------------------------------------------
|
||||
Visual representation for reference:
|
||||
|
||||
31 | | | 0 bit position
|
||||
+-----+-----+-----+----------+
|
||||
| B | C | A | Opcode | iABC format
|
||||
+-----+-----+-----+----------+
|
||||
- 9 - 9 - 8 - 6 - field sizes
|
||||
+-----+-----+-----+----------+
|
||||
| [s]Bx | A | Opcode | iABx | iAsBx format
|
||||
+-----+-----+-----+----------+
|
||||
----------------------------------------------------------------------]]
|
||||
|
||||
------------------------------------------------------------------------
|
||||
-- the following macros help to manipulate instructions
|
||||
-- * changed to a table object representation, very clean compared to
|
||||
-- the [nightmare] alternatives of using a number or a string
|
||||
------------------------------------------------------------------------
|
||||
|
||||
-- these accept or return opcodes in the form of string names
|
||||
function luaP:GET_OPCODE(i) return self.ROpCode[i.OP] end
|
||||
function luaP:SET_OPCODE(i, o) i.OP = self.OpCode[o] end
|
||||
|
||||
function luaP:GETARG_A(i) return i.A end
|
||||
function luaP:SETARG_A(i, u) i.A = u end
|
||||
|
||||
function luaP:GETARG_B(i) return i.B end
|
||||
function luaP:SETARG_B(i, b) i.B = b end
|
||||
|
||||
function luaP:GETARG_C(i) return i.C end
|
||||
function luaP:SETARG_C(i, b) i.C = b end
|
||||
|
||||
function luaP:GETARG_Bx(i) return i.Bx end
|
||||
function luaP:SETARG_Bx(i, b) i.Bx = b end
|
||||
|
||||
function luaP:GETARG_sBx(i) return i.Bx - self.MAXARG_sBx end
|
||||
function luaP:SETARG_sBx(i, b) i.Bx = b + self.MAXARG_sBx end
|
||||
|
||||
function luaP:CREATE_ABC(o,a,b,c)
|
||||
return {OP = self.OpCode[o], A = a, B = b, C = c}
|
||||
end
|
||||
|
||||
function luaP:CREATE_ABx(o,a,bc)
|
||||
return {OP = self.OpCode[o], A = a, Bx = bc}
|
||||
end
|
||||
|
||||
------------------------------------------------------------------------
|
||||
-- Bit shuffling stuffs
|
||||
------------------------------------------------------------------------
|
||||
|
||||
if false and pcall (require, 'bit') then
|
||||
------------------------------------------------------------------------
|
||||
-- Return a 4-char string little-endian encoded form of an instruction
|
||||
------------------------------------------------------------------------
|
||||
function luaP:Instruction(i)
|
||||
--FIXME
|
||||
end
|
||||
else
|
||||
------------------------------------------------------------------------
|
||||
-- Version without bit manipulation library.
|
||||
------------------------------------------------------------------------
|
||||
local p2 = {1,2,4,8,16,32,64,128,256, 512, 1024, 2048, 4096}
|
||||
-- keeps [n] bits from [x]
|
||||
local function keep (x, n) return x % p2[n+1] end
|
||||
-- shifts bits of [x] [n] places to the right
|
||||
local function srb (x,n) return math.floor (x / p2[n+1]) end
|
||||
-- shifts bits of [x] [n] places to the left
|
||||
local function slb (x,n) return x * p2[n+1] end
|
||||
|
||||
------------------------------------------------------------------------
|
||||
-- Return a 4-char string little-endian encoded form of an instruction
|
||||
------------------------------------------------------------------------
|
||||
function luaP:Instruction(i)
|
||||
-- printf("Instr->string: %s %s", self.opnames[i.OP], table.tostring(i))
|
||||
local c0, c1, c2, c3
|
||||
-- change to OP/A/B/C format if needed
|
||||
if i.Bx then i.C = keep (i.Bx, 9); i.B = srb (i.Bx, 9) end
|
||||
-- c0 = 6B from opcode + 2LSB from A (flushed to MSB)
|
||||
c0 = i.OP + slb (keep (i.A, 2), 6)
|
||||
-- c1 = 6MSB from A + 2LSB from C (flushed to MSB)
|
||||
c1 = srb (i.A, 2) + slb (keep (i.C, 2), 6)
|
||||
-- c2 = 7MSB from C + 1LSB from B (flushed to MSB)
|
||||
c2 = srb (i.C, 2) + slb (keep (i.B, 1), 7)
|
||||
-- c3 = 8MSB from B
|
||||
c3 = srb (i.B, 1)
|
||||
--printf ("Instruction: %s %s", self.opnames[i.OP], tostringv (i))
|
||||
--printf ("Bin encoding: %x %x %x %x", c0, c1, c2, c3)
|
||||
return string.char(c0, c1, c2, c3)
|
||||
end
|
||||
end
|
||||
------------------------------------------------------------------------
|
||||
-- decodes a 4-char little-endian string into an instruction struct
|
||||
------------------------------------------------------------------------
|
||||
function luaP:DecodeInst(x)
|
||||
error "Not implemented"
|
||||
end
|
||||
|
||||
------------------------------------------------------------------------
|
||||
-- invalid register that fits in 8 bits
|
||||
------------------------------------------------------------------------
|
||||
luaP.NO_REG = luaP.MAXARG_A
|
||||
|
||||
------------------------------------------------------------------------
|
||||
-- R(x) - register
|
||||
-- Kst(x) - constant (in constant table)
|
||||
-- RK(x) == if x < MAXSTACK then R(x) else Kst(x-MAXSTACK)
|
||||
------------------------------------------------------------------------
|
||||
|
||||
------------------------------------------------------------------------
|
||||
-- grep "ORDER OP" if you change these enums
|
||||
------------------------------------------------------------------------
|
||||
|
||||
--[[--------------------------------------------------------------------
|
||||
Lua virtual machine opcodes (enum OpCode):
|
||||
------------------------------------------------------------------------
|
||||
name args description
|
||||
------------------------------------------------------------------------
|
||||
OP_MOVE A B R(A) := R(B)
|
||||
OP_LOADK A Bx R(A) := Kst(Bx)
|
||||
OP_LOADBOOL A B C R(A) := (Bool)B; if (C) PC++
|
||||
OP_LOADNIL A B R(A) := ... := R(B) := nil
|
||||
OP_GETUPVAL A B R(A) := UpValue[B]
|
||||
OP_GETGLOBAL A Bx R(A) := Gbl[Kst(Bx)]
|
||||
OP_GETTABLE A B C R(A) := R(B)[RK(C)]
|
||||
OP_SETGLOBAL A Bx Gbl[Kst(Bx)] := R(A)
|
||||
OP_SETUPVAL A B UpValue[B] := R(A)
|
||||
OP_SETTABLE A B C R(A)[RK(B)] := RK(C)
|
||||
OP_NEWTABLE A B C R(A) := {} (size = B,C)
|
||||
OP_SELF A B C R(A+1) := R(B); R(A) := R(B)[RK(C)]
|
||||
OP_ADD A B C R(A) := RK(B) + RK(C)
|
||||
OP_SUB A B C R(A) := RK(B) - RK(C)
|
||||
OP_MUL A B C R(A) := RK(B) * RK(C)
|
||||
OP_DIV A B C R(A) := RK(B) / RK(C)
|
||||
OP_POW A B C R(A) := RK(B) ^ RK(C)
|
||||
OP_UNM A B R(A) := -R(B)
|
||||
OP_NOT A B R(A) := not R(B)
|
||||
OP_CONCAT A B C R(A) := R(B).. ... ..R(C)
|
||||
OP_JMP sBx PC += sBx
|
||||
OP_EQ A B C if ((RK(B) == RK(C)) ~= A) then pc++
|
||||
OP_LT A B C if ((RK(B) < RK(C)) ~= A) then pc++
|
||||
OP_LE A B C if ((RK(B) <= RK(C)) ~= A) then pc++
|
||||
OP_TEST A B C if (R(B) <=> C) then R(A) := R(B) else pc++
|
||||
OP_CALL A B C R(A), ... ,R(A+C-2) := R(A)(R(A+1), ... ,R(A+B-1))
|
||||
OP_TAILCALL A B C return R(A)(R(A+1), ... ,R(A+B-1))
|
||||
OP_RETURN A B return R(A), ... ,R(A+B-2) (see note)
|
||||
OP_FORLOOP A sBx R(A)+=R(A+2); if R(A) <?= R(A+1) then PC+= sBx
|
||||
OP_TFORLOOP A C R(A+2), ... ,R(A+2+C) := R(A)(R(A+1), R(A+2));
|
||||
if R(A+2) ~= nil then pc++
|
||||
OP_TFORPREP A sBx if type(R(A)) == table then R(A+1):=R(A), R(A):=next;
|
||||
PC += sBx
|
||||
OP_SETLIST A Bx R(A)[Bx-Bx%FPF+i] := R(A+i), 1 <= i <= Bx%FPF+1
|
||||
OP_SETLISTO A Bx (see note)
|
||||
OP_CLOSE A close all variables in the stack up to (>=) R(A)
|
||||
OP_CLOSURE A Bx R(A) := closure(KPROTO[Bx], R(A), ... ,R(A+n))
|
||||
----------------------------------------------------------------------]]
|
||||
|
||||
luaP.opnames = {} -- opcode names
|
||||
luaP.OpCode = {} -- lookup name -> number
|
||||
luaP.ROpCode = {} -- lookup number -> name
|
||||
|
||||
local i = 0
|
||||
for v in string.gfind([[
|
||||
MOVE -- 0
|
||||
LOADK
|
||||
LOADBOOL
|
||||
LOADNIL
|
||||
GETUPVAL
|
||||
GETGLOBAL -- 5
|
||||
GETTABLE
|
||||
SETGLOBAL
|
||||
SETUPVAL
|
||||
SETTABLE
|
||||
NEWTABLE -- 10
|
||||
SELF
|
||||
ADD
|
||||
SUB
|
||||
MUL
|
||||
DIV -- 15
|
||||
MOD
|
||||
POW
|
||||
UNM
|
||||
NOT
|
||||
LEN -- 20
|
||||
CONCAT
|
||||
JMP
|
||||
EQ
|
||||
LT
|
||||
LE -- 25
|
||||
TEST
|
||||
TESTSET
|
||||
CALL
|
||||
TAILCALL
|
||||
RETURN -- 30
|
||||
FORLOOP
|
||||
FORPREP
|
||||
TFORLOOP
|
||||
SETLIST
|
||||
CLOSE -- 35
|
||||
CLOSURE
|
||||
VARARG
|
||||
]], "[%a]+") do
|
||||
local n = "OP_"..v
|
||||
luaP.opnames[i] = v
|
||||
luaP.OpCode[n] = i
|
||||
luaP.ROpCode[i] = n
|
||||
i = i + 1
|
||||
end
|
||||
luaP.NUM_OPCODES = i
|
||||
|
||||
--[[
|
||||
===========================================================================
|
||||
Notes:
|
||||
(1) 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'.
|
||||
|
||||
(2) In OP_RETURN, if (B == 0) then return up to 'top'
|
||||
|
||||
(3) For comparisons, B specifies what conditions the test should accept.
|
||||
|
||||
(4) All 'skips' (pc++) assume that next instruction is a jump
|
||||
|
||||
(5) OP_SETLISTO is used when the last item in a table constructor is a
|
||||
function, so the number of elements set is up to top of stack
|
||||
===========================================================================
|
||||
--]]
|
||||
|
||||
------------------------------------------------------------------------
|
||||
-- masks for instruction properties
|
||||
------------------------------------------------------------------------
|
||||
-- was enum OpModeMask:
|
||||
luaP.OpModeBreg = 2 -- B is a register
|
||||
luaP.OpModeBrk = 3 -- B is a register/constant
|
||||
luaP.OpModeCrk = 4 -- C is a register/constant
|
||||
luaP.OpModesetA = 5 -- instruction set register A
|
||||
luaP.OpModeK = 6 -- Bx is a constant
|
||||
luaP.OpModeT = 1 -- operator is a test
|
||||
|
||||
------------------------------------------------------------------------
|
||||
-- get opcode mode, e.g. "iABC"
|
||||
------------------------------------------------------------------------
|
||||
function luaP:getOpMode(m)
|
||||
--printv(m)
|
||||
--printv(self.OpCode[m])
|
||||
--printv(self.opmodes [self.OpCode[m]+1])
|
||||
return self.OpMode[tonumber(string.sub(self.opmodes[self.OpCode[m] + 1], 7, 7))]
|
||||
end
|
||||
|
||||
------------------------------------------------------------------------
|
||||
-- test an instruction property flag
|
||||
-- * b is a string, e.g. "OpModeBreg"
|
||||
------------------------------------------------------------------------
|
||||
function luaP:testOpMode(m, b)
|
||||
return (string.sub(self.opmodes[self.OpCode[m] + 1], self[b], self[b]) == "1")
|
||||
end
|
||||
|
||||
-- number of list items to accumulate before a SETLIST instruction
|
||||
-- (must be a power of 2)
|
||||
-- * used in lparser, lvm, ldebug, ltests
|
||||
luaP.LFIELDS_PER_FLUSH = 50 --FF updated to match 5.1
|
||||
|
||||
-- luaP_opnames[] is set above, as the luaP.opnames table
|
||||
-- opmode(t,b,bk,ck,sa,k,m) deleted
|
||||
|
||||
--[[--------------------------------------------------------------------
|
||||
Legend for luaP:opmodes:
|
||||
1 T -> T (is a test?)
|
||||
2 B -> B is a register
|
||||
3 b -> B is an RK register/constant combination
|
||||
4 C -> C is an RK register/constant combination
|
||||
5 A -> register A is set by the opcode
|
||||
6 K -> Bx is a constant
|
||||
7 m -> 1 if iABC layout,
|
||||
2 if iABx layout,
|
||||
3 if iAsBx layout
|
||||
----------------------------------------------------------------------]]
|
||||
|
||||
luaP.opmodes = {
|
||||
-- TBbCAKm opcode
|
||||
"0100101", -- OP_MOVE 0
|
||||
"0000112", -- OP_LOADK
|
||||
"0000101", -- OP_LOADBOOL
|
||||
"0100101", -- OP_LOADNIL
|
||||
"0000101", -- OP_GETUPVAL
|
||||
"0000112", -- OP_GETGLOBAL 5
|
||||
"0101101", -- OP_GETTABLE
|
||||
"0000012", -- OP_SETGLOBAL
|
||||
"0000001", -- OP_SETUPVAL
|
||||
"0011001", -- OP_SETTABLE
|
||||
"0000101", -- OP_NEWTABLE 10
|
||||
"0101101", -- OP_SELF
|
||||
"0011101", -- OP_ADD
|
||||
"0011101", -- OP_SUB
|
||||
"0011101", -- OP_MUL
|
||||
"0011101", -- OP_DIV 15
|
||||
"0011101", -- OP_MOD
|
||||
"0011101", -- OP_POW
|
||||
"0100101", -- OP_UNM
|
||||
"0100101", -- OP_NOT
|
||||
"0100101", -- OP_LEN 20
|
||||
"0101101", -- OP_CONCAT
|
||||
"0000003", -- OP_JMP
|
||||
"1011001", -- OP_EQ
|
||||
"1011001", -- OP_LT
|
||||
"1011001", -- OP_LE 25
|
||||
"1000101", -- OP_TEST
|
||||
"1100101", -- OP_TESTSET
|
||||
"0000001", -- OP_CALL
|
||||
"0000001", -- OP_TAILCALL
|
||||
"0000001", -- OP_RETURN 30
|
||||
"0000003", -- OP_FORLOOP
|
||||
"0000103", -- OP_FORPREP
|
||||
"1000101", -- OP_TFORLOOP
|
||||
"0000001", -- OP_SETLIST
|
||||
"0000001", -- OP_CLOSE 35
|
||||
"0000102", -- OP_CLOSURE
|
||||
"0000101" -- OP_VARARG
|
||||
}
|
||||
60
lualibs/metalua/metalua.lua
Normal file
60
lualibs/metalua/metalua.lua
Normal file
@@ -0,0 +1,60 @@
|
||||
-- construct proper path to load metalua modules to build
|
||||
-- an abstract syntax tree (AST)
|
||||
local file = debug.getinfo(1, "S").source
|
||||
if string.find(file, "@") == 1 then file = string.sub(file, 2) end
|
||||
package.path = package.path .. ';' .. string.gsub(file, "metalua%.lua$", "?.lua")
|
||||
|
||||
-- these modules are sufficient to build an AST from a source file/string
|
||||
require "lexer"
|
||||
require "gg"
|
||||
require "mlp_lexer"
|
||||
require "mlp_misc"
|
||||
require "mlp_table"
|
||||
require "mlp_meta"
|
||||
require "mlp_expr"
|
||||
require "mlp_stat"
|
||||
|
||||
-- these modules are needed to convert an AST into bytecode to execute
|
||||
require "lcode"
|
||||
require "ldump"
|
||||
require "lopcodes"
|
||||
require "compile"
|
||||
|
||||
-- this is the compiler module that builds bytecode from an AST
|
||||
local mlc = { }
|
||||
|
||||
function mlc.function_of_ast (ast)
|
||||
local proto = bytecode.metalua_compile(ast)
|
||||
local dump = bytecode.dump_string(proto)
|
||||
local func = string.undump(dump)
|
||||
return func
|
||||
end
|
||||
|
||||
function mlc.ast_of_luastring (src, file)
|
||||
local lx = mlp.lexer:newstream(src, file or "(string)")
|
||||
local ast = mlp.chunk(lx)
|
||||
return ast
|
||||
end
|
||||
|
||||
function mlc.function_of_luastring (src, file)
|
||||
local ast = mlc.ast_of_luastring(src, file)
|
||||
local func = mlc.function_of_ast(ast)
|
||||
return func
|
||||
end
|
||||
|
||||
function mlc.function_of_luafile (name)
|
||||
local f = io.open(name, 'r')
|
||||
local src = f:read('*a')
|
||||
f:close()
|
||||
return mlc.function_of_luastring(src, "@"..name)
|
||||
end
|
||||
|
||||
_G.mlc = mlc
|
||||
|
||||
--[[
|
||||
-- Can be used with the following code:
|
||||
require "metalua"
|
||||
local ast = mlc.ast_of_luastring(src)
|
||||
local f = mlc.function_of_ast(ast)
|
||||
f()
|
||||
]]
|
||||
104
lualibs/metalua/metalua/base.lua
Normal file
104
lualibs/metalua/metalua/base.lua
Normal file
@@ -0,0 +1,104 @@
|
||||
----------------------------------------------------------------------
|
||||
----------------------------------------------------------------------
|
||||
--
|
||||
-- Base library extension
|
||||
--
|
||||
----------------------------------------------------------------------
|
||||
----------------------------------------------------------------------
|
||||
|
||||
if not metalua then rawset(getfenv(), 'metalua', { }) end
|
||||
metalua.version = "v-0.5"
|
||||
|
||||
if not rawpairs then
|
||||
rawpairs, rawipairs, rawtype = pairs, ipairs, type
|
||||
end
|
||||
|
||||
function pairs(x)
|
||||
assert(type(x)=='table', 'pairs() expects a table')
|
||||
local mt = getmetatable(x)
|
||||
if mt then
|
||||
local mtp = mt.__pairs
|
||||
if mtp then return mtp(x) end
|
||||
end
|
||||
return rawpairs(x)
|
||||
end
|
||||
|
||||
function ipairs(x)
|
||||
assert(type(x)=='table', 'ipairs() expects a table')
|
||||
local mt = getmetatable(x)
|
||||
if mt then
|
||||
local mti = mt.__ipairs
|
||||
if mti then return mti(x) end
|
||||
end
|
||||
return rawipairs(x)
|
||||
end
|
||||
|
||||
--[[
|
||||
function type(x)
|
||||
local mt = getmetatable(x)
|
||||
if mt then
|
||||
local mtt = mt.__type
|
||||
if mtt then return mtt end
|
||||
end
|
||||
return rawtype(x)
|
||||
end
|
||||
]]
|
||||
|
||||
function min (a, ...)
|
||||
for n in values{...} do if n<a then a=n end end
|
||||
return a
|
||||
end
|
||||
|
||||
function max (a, ...)
|
||||
for n in values{...} do if n>a then a=n end end
|
||||
return a
|
||||
end
|
||||
|
||||
function o (...)
|
||||
local args = {...}
|
||||
local function g (...)
|
||||
local result = {...}
|
||||
for i=#args, 1, -1 do result = {args[i](unpack(result))} end
|
||||
return unpack (result)
|
||||
end
|
||||
return g
|
||||
end
|
||||
|
||||
function id (...) return ... end
|
||||
function const (k) return function () return k end end
|
||||
|
||||
function printf(...) return print(string.format(...)) end
|
||||
function eprintf(...)
|
||||
io.stderr:write(string.format(...).."\n")
|
||||
end
|
||||
|
||||
function ivalues (x)
|
||||
assert(type(x)=='table', 'ivalues() expects a table')
|
||||
local i = 1
|
||||
local function iterator ()
|
||||
local r = x[i]; i=i+1; return r
|
||||
end
|
||||
return iterator
|
||||
end
|
||||
|
||||
|
||||
function values (x)
|
||||
assert(type(x)=='table', 'values() expects a table')
|
||||
local function iterator (state)
|
||||
local it
|
||||
state.content, it = next(state.list, state.content)
|
||||
return it
|
||||
end
|
||||
return iterator, { list = x }
|
||||
end
|
||||
|
||||
function keys (x)
|
||||
assert(type(x)=='table', 'keys() expects a table')
|
||||
local function iterator (state)
|
||||
local it = next(state.list, state.content)
|
||||
state.content = it
|
||||
return it
|
||||
end
|
||||
return iterator, { list = x }
|
||||
end
|
||||
|
||||
3
lualibs/metalua/metalua/runtime.lua
Normal file
3
lualibs/metalua/metalua/runtime.lua
Normal file
@@ -0,0 +1,3 @@
|
||||
require 'metalua.base'
|
||||
require 'metalua.table2'
|
||||
require 'metalua.string2'
|
||||
43
lualibs/metalua/metalua/string2.lua
Normal file
43
lualibs/metalua/metalua/string2.lua
Normal file
@@ -0,0 +1,43 @@
|
||||
----------------------------------------------------------------------
|
||||
----------------------------------------------------------------------
|
||||
--
|
||||
-- String module extension
|
||||
--
|
||||
----------------------------------------------------------------------
|
||||
----------------------------------------------------------------------
|
||||
|
||||
-- Courtesy of lua-users.org
|
||||
function string.split(str, pat)
|
||||
local t = {}
|
||||
local fpat = "(.-)" .. pat
|
||||
local last_end = 1
|
||||
local s, e, cap = string.find(str, fpat, 1)
|
||||
while s do
|
||||
if s ~= 1 or cap ~= "" then
|
||||
table.insert(t,cap)
|
||||
end
|
||||
last_end = e+1
|
||||
s, e, cap = string.find(str, fpat, last_end)
|
||||
end
|
||||
if last_end <= string.len(str) then
|
||||
cap = string.sub(str, last_end)
|
||||
table.insert(t, cap)
|
||||
end
|
||||
return t
|
||||
end
|
||||
|
||||
-- "match" is regularly used as a keyword for pattern matching,
|
||||
-- so here is an always available substitute.
|
||||
string.strmatch = string["match"]
|
||||
|
||||
-- change a compiled string into a function
|
||||
function string.undump(str)
|
||||
if str:strmatch '^\027LuaQ' or str:strmatch '^#![^\n]+\n\027LuaQ' then
|
||||
local f = (lua_loadstring or loadstring)(str)
|
||||
return f
|
||||
else
|
||||
error "Not a chunk dump"
|
||||
end
|
||||
end
|
||||
|
||||
return string
|
||||
380
lualibs/metalua/metalua/table2.lua
Normal file
380
lualibs/metalua/metalua/table2.lua
Normal file
@@ -0,0 +1,380 @@
|
||||
---------------------------------------------------------------------
|
||||
----------------------------------------------------------------------
|
||||
--
|
||||
-- Table module extension
|
||||
--
|
||||
----------------------------------------------------------------------
|
||||
----------------------------------------------------------------------
|
||||
|
||||
-- todo: table.scan (scan1?) fold1? flip?
|
||||
|
||||
function table.transpose(t)
|
||||
local tt = { }
|
||||
for a, b in pairs(t) do tt[b] = a end
|
||||
return tt
|
||||
end
|
||||
|
||||
function table.iforeach(f, ...)
|
||||
-- assert (type (f) == "function") [wouldn't allow metamethod __call]
|
||||
local nargs = select("#", ...)
|
||||
if nargs==1 then -- Quick iforeach (most common case), just one table arg
|
||||
local t = ...
|
||||
assert (type (t) == "table")
|
||||
for i = 1, #t do
|
||||
local result = f (t[i])
|
||||
-- If the function returns non-false, stop iteration
|
||||
if result then return result end
|
||||
end
|
||||
else -- advanced case: boundaries and/or multiple tables
|
||||
|
||||
-- fargs: arguments fot a single call to f
|
||||
-- first, last: indexes of the first & last elements mapped in each table
|
||||
-- arg1: index of the first table in args
|
||||
|
||||
-- 1 - find boundaries if any
|
||||
local args, fargs, first, last, arg1 = {...}, { }
|
||||
if type(args[1]) ~= "number" then first, arg1 = 1, 1 -- no boundary
|
||||
elseif type(args[2]) ~= "number" then first, last, arg1 = 1, args[1], 2
|
||||
else first, last, arg1 = args[1], args[2], 3 end
|
||||
assert (nargs >= arg1) -- at least one table
|
||||
-- 2 - determine upper boundary if not given
|
||||
if not last then for i = arg1, nargs do
|
||||
assert (type (args[i]) == "table")
|
||||
last = max (#args[i], last)
|
||||
end end
|
||||
-- 3 - remove non-table arguments from args, adjust nargs
|
||||
if arg1>1 then args = { select(arg1, unpack(args)) }; nargs = #args end
|
||||
|
||||
-- 4 - perform the iteration
|
||||
for i = first, last do
|
||||
for j = 1, nargs do fargs[j] = args[j][i] end -- build args list
|
||||
local result = f (unpack (fargs)) -- here is the call
|
||||
-- If the function returns non-false, stop iteration
|
||||
if result then return result end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function table.imap (f, ...)
|
||||
local result, idx = { }, 1
|
||||
local function g(...) result[idx] = f(...); idx=idx+1 end
|
||||
table.iforeach(g, ...)
|
||||
return result
|
||||
end
|
||||
|
||||
function table.ifold (f, acc, ...)
|
||||
local function g(...) acc = f (acc,...) end
|
||||
table.iforeach (g, ...)
|
||||
return acc
|
||||
end
|
||||
|
||||
-- function table.ifold1 (f, ...)
|
||||
-- return table.ifold (f, acc, 2, false, ...)
|
||||
-- end
|
||||
|
||||
function table.izip(...)
|
||||
local function g(...) return {...} end
|
||||
return table.imap(g, ...)
|
||||
end
|
||||
|
||||
function table.ifilter(f, t)
|
||||
local yes, no = { }, { }
|
||||
for i=1,#t do table.insert (f(t[i]) and yes or no, t[i]) end
|
||||
return yes, no
|
||||
end
|
||||
|
||||
function table.icat(...)
|
||||
local result = { }
|
||||
for t in values {...} do
|
||||
for x in values (t) do
|
||||
table.insert (result, x)
|
||||
end
|
||||
end
|
||||
return result
|
||||
end
|
||||
|
||||
function table.iflatten (x) return table.icat (unpack (x)) end
|
||||
|
||||
function table.irev (t)
|
||||
local result, nt = { }, #t
|
||||
for i=0, nt-1 do result[nt-i] = t[i+1] end
|
||||
return result
|
||||
end
|
||||
|
||||
function table.isub (t, ...)
|
||||
local ti, u = table.insert, { }
|
||||
local args, nargs = {...}, select("#", ...)
|
||||
for i=1, nargs/2 do
|
||||
local a, b = args[2*i-1], args[2*i]
|
||||
for i=a, b, a<=b and 1 or -1 do ti(u, t[i]) end
|
||||
end
|
||||
return u
|
||||
end
|
||||
|
||||
function table.iall (f, ...)
|
||||
local result = true
|
||||
local function g(...) return not f(...) end
|
||||
return not table.iforeach(g, ...)
|
||||
--return result
|
||||
end
|
||||
|
||||
function table.iany (f, ...)
|
||||
local function g(...) return not f(...) end
|
||||
return not table.iall(g, ...)
|
||||
end
|
||||
|
||||
function table.shallow_copy(x)
|
||||
local y={ }
|
||||
for k, v in pairs(x) do y[k]=v end
|
||||
return y
|
||||
end
|
||||
|
||||
-- Warning, this is implementation dependent: it relies on
|
||||
-- the fact the [next()] enumerates the array-part before the hash-part.
|
||||
function table.cat(...)
|
||||
local y={ }
|
||||
for x in values{...} do
|
||||
-- cat array-part
|
||||
for _, v in ipairs(x) do table.insert(y,v) end
|
||||
-- cat hash-part
|
||||
local lx, k = #x
|
||||
if lx>0 then k=next(x,lx) else k=next(x) end
|
||||
while k do y[k]=x[k]; k=next(x,k) end
|
||||
end
|
||||
return y
|
||||
end
|
||||
|
||||
function table.deep_copy(x)
|
||||
local tracker = { }
|
||||
local function aux (x)
|
||||
if type(x) == "table" then
|
||||
local y=tracker[x]
|
||||
if y then return y end
|
||||
y = { }; tracker[x] = y
|
||||
setmetatable (y, getmetatable (x))
|
||||
for k,v in pairs(x) do y[aux(k)] = aux(v) end
|
||||
return y
|
||||
else return x end
|
||||
end
|
||||
return aux(x)
|
||||
end
|
||||
|
||||
function table.override(dst, src)
|
||||
for k, v in pairs(src) do dst[k] = v end
|
||||
for i = #src+1, #dst do dst[i] = nil end
|
||||
return dst
|
||||
end
|
||||
|
||||
|
||||
function table.range(a,b,c)
|
||||
if not b then assert(not(c)); b=a; a=1
|
||||
elseif not c then c = (b>=a) and 1 or -1 end
|
||||
local result = { }
|
||||
for i=a, b, c do table.insert(result, i) end
|
||||
return result
|
||||
end
|
||||
|
||||
-- FIXME: new_indent seems to be always nil?!
|
||||
-- FIXME: accumulator function should be configurable,
|
||||
-- so that print() doesn't need to bufferize the whole string
|
||||
-- before starting to print.
|
||||
function table.tostring(t, ...)
|
||||
local PRINT_HASH, HANDLE_TAG, FIX_INDENT, LINE_MAX, INITIAL_INDENT = true, true
|
||||
for _, x in ipairs {...} do
|
||||
if type(x) == "number" then
|
||||
if not LINE_MAX then LINE_MAX = x
|
||||
else INITIAL_INDENT = x end
|
||||
elseif x=="nohash" then PRINT_HASH = false
|
||||
elseif x=="notag" then HANDLE_TAG = false
|
||||
else
|
||||
local n = string['match'](x, "^indent%s*(%d*)$")
|
||||
if n then FIX_INDENT = tonumber(n) or 3 end
|
||||
end
|
||||
end
|
||||
LINE_MAX = LINE_MAX or math.huge
|
||||
INITIAL_INDENT = INITIAL_INDENT or 1
|
||||
|
||||
local current_offset = 0 -- indentation level
|
||||
local xlen_cache = { } -- cached results for xlen()
|
||||
local acc_list = { } -- Generated bits of string
|
||||
local function acc(...) -- Accumulate a bit of string
|
||||
local x = table.concat{...}
|
||||
current_offset = current_offset + #x
|
||||
table.insert(acc_list, x)
|
||||
end
|
||||
local function valid_id(x)
|
||||
-- FIXME: we should also reject keywords; but the list of
|
||||
-- current keywords is not fixed in metalua...
|
||||
return type(x) == "string"
|
||||
and string['match'](x, "^[a-zA-Z_][a-zA-Z0-9_]*$")
|
||||
end
|
||||
|
||||
-- Compute the number of chars it would require to display the table
|
||||
-- on a single line. Helps to decide whether some carriage returns are
|
||||
-- required. Since the size of each sub-table is required many times,
|
||||
-- it's cached in [xlen_cache].
|
||||
local xlen_type = { }
|
||||
local function xlen(x, nested)
|
||||
nested = nested or { }
|
||||
if x==nil then return #"nil" end
|
||||
--if nested[x] then return #tostring(x) end -- already done in table
|
||||
local len = xlen_cache[x]
|
||||
if len then return len end
|
||||
local f = xlen_type[type(x)]
|
||||
if not f then return #tostring(x) end
|
||||
len = f (x, nested)
|
||||
xlen_cache[x] = len
|
||||
return len
|
||||
end
|
||||
|
||||
-- optim: no need to compute lengths if I'm not going to use them
|
||||
-- anyway.
|
||||
if LINE_MAX == math.huge then xlen = function() return 0 end end
|
||||
|
||||
xlen_type["nil"] = function () return 3 end
|
||||
function xlen_type.number (x) return #tostring(x) end
|
||||
function xlen_type.boolean (x) return x and 4 or 5 end
|
||||
function xlen_type.string (x) return #string.format("%q",x) end
|
||||
function xlen_type.table (adt, nested)
|
||||
|
||||
-- Circular references detection
|
||||
if nested [adt] then return #tostring(adt) end
|
||||
nested [adt] = true
|
||||
|
||||
local has_tag = HANDLE_TAG and valid_id(adt.tag)
|
||||
local alen = #adt
|
||||
local has_arr = alen>0
|
||||
local has_hash = false
|
||||
local x = 0
|
||||
|
||||
if PRINT_HASH then
|
||||
-- first pass: count hash-part
|
||||
for k, v in pairs(adt) do
|
||||
if k=="tag" and has_tag then
|
||||
-- this is the tag -> do nothing!
|
||||
elseif type(k)=="number" and k<=alen and math.fmod(k,1)==0 then
|
||||
-- array-part pair -> do nothing!
|
||||
else
|
||||
has_hash = true
|
||||
if valid_id(k) then x=x+#k
|
||||
else x = x + xlen (k, nested) + 2 end -- count surrounding brackets
|
||||
x = x + xlen (v, nested) + 5 -- count " = " and ", "
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
for i = 1, alen do x = x + xlen (adt[i], nested) + 2 end -- count ", "
|
||||
|
||||
nested[adt] = false -- No more nested calls
|
||||
|
||||
if not (has_tag or has_arr or has_hash) then return 3 end
|
||||
if has_tag then x=x+#adt.tag+1 end
|
||||
if not (has_arr or has_hash) then return x end
|
||||
if not has_hash and alen==1 and type(adt[1])~="table" then
|
||||
return x-2 -- substract extraneous ", "
|
||||
end
|
||||
return x+2 -- count "{ " and " }", substract extraneous ", "
|
||||
end
|
||||
|
||||
-- Recursively print a (sub) table at given indentation level.
|
||||
-- [newline] indicates whether newlines should be inserted.
|
||||
local function rec (adt, nested, indent)
|
||||
if not FIX_INDENT then indent = current_offset end
|
||||
local function acc_newline()
|
||||
acc ("\n"); acc (string.rep (" ", indent))
|
||||
current_offset = indent
|
||||
end
|
||||
local x = { }
|
||||
x["nil"] = function() acc "nil" end
|
||||
function x.number() acc (tostring (adt)) end
|
||||
--function x.string() acc (string.format ("%q", adt)) end
|
||||
function x.string() acc ((string.format ("%q", adt):gsub("\\\n", "\\n"))) end
|
||||
function x.boolean() acc (adt and "true" or "false") end
|
||||
function x.table()
|
||||
if nested[adt] then acc(tostring(adt)); return end
|
||||
nested[adt] = true
|
||||
|
||||
|
||||
local has_tag = HANDLE_TAG and valid_id(adt.tag)
|
||||
local alen = #adt
|
||||
local has_arr = alen>0
|
||||
local has_hash = false
|
||||
|
||||
if has_tag then acc("`"); acc(adt.tag) end
|
||||
|
||||
-- First pass: handle hash-part
|
||||
if PRINT_HASH then
|
||||
for k, v in pairs(adt) do
|
||||
-- pass if the key belongs to the array-part or is the "tag" field
|
||||
if not (k=="tag" and HANDLE_TAG) and
|
||||
not (type(k)=="number" and k<=alen and math.fmod(k,1)==0) then
|
||||
|
||||
-- Is it the first time we parse a hash pair?
|
||||
if not has_hash then
|
||||
acc "{ "
|
||||
if not FIX_INDENT then indent = current_offset end
|
||||
else acc ", " end
|
||||
|
||||
-- Determine whether a newline is required
|
||||
local is_id, expected_len = valid_id(k)
|
||||
if is_id then expected_len = #k + xlen (v, nested) + #" = , "
|
||||
else expected_len = xlen (k, nested) +
|
||||
xlen (v, nested) + #"[] = , " end
|
||||
if has_hash and expected_len + current_offset > LINE_MAX
|
||||
then acc_newline() end
|
||||
|
||||
-- Print the key
|
||||
if is_id then acc(k); acc " = "
|
||||
else acc "["; rec (k, nested, indent+(FIX_INDENT or 0)); acc "] = " end
|
||||
|
||||
-- Print the value
|
||||
rec (v, nested, indent+(FIX_INDENT or 0))
|
||||
has_hash = true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Now we know whether there's a hash-part, an array-part, and a tag.
|
||||
-- Tag and hash-part are already printed if they're present.
|
||||
if not has_tag and not has_hash and not has_arr then acc "{ }";
|
||||
elseif has_tag and not has_hash and not has_arr then -- nothing, tag already in acc
|
||||
else
|
||||
assert (has_hash or has_arr)
|
||||
local no_brace = false
|
||||
if has_hash and has_arr then acc ", "
|
||||
elseif has_tag and not has_hash and alen==1 and type(adt[1])~="table" then
|
||||
-- No brace required; don't print "{", remember not to print "}"
|
||||
acc (" "); rec (adt[1], nested, indent+(FIX_INDENT or 0))
|
||||
no_brace = true
|
||||
elseif not has_hash then
|
||||
-- Braces required, but not opened by hash-part handler yet
|
||||
acc "{ "
|
||||
if not FIX_INDENT then indent = current_offset end
|
||||
end
|
||||
|
||||
-- 2nd pass: array-part
|
||||
if not no_brace and has_arr then
|
||||
rec (adt[1], nested, indent+(FIX_INDENT or 0))
|
||||
for i=2, alen do
|
||||
acc ", ";
|
||||
if current_offset + xlen (adt[i], { }) > LINE_MAX
|
||||
then acc_newline() end
|
||||
rec (adt[i], nested, indent+(FIX_INDENT or 0))
|
||||
end
|
||||
end
|
||||
if not no_brace then acc " }" end
|
||||
end
|
||||
nested[adt] = false -- No more nested calls
|
||||
end
|
||||
local y = x[type(adt)]
|
||||
if y then y() else acc(tostring(adt)) end
|
||||
end
|
||||
--printf("INITIAL_INDENT = %i", INITIAL_INDENT)
|
||||
current_offset = INITIAL_INDENT or 0
|
||||
rec(t, { }, 0)
|
||||
return table.concat (acc_list)
|
||||
end
|
||||
|
||||
function table.print(...) return print(table.tostring(...)) end
|
||||
|
||||
return table
|
||||
213
lualibs/metalua/mlp_expr.lua
Normal file
213
lualibs/metalua/mlp_expr.lua
Normal file
@@ -0,0 +1,213 @@
|
||||
----------------------------------------------------------------------
|
||||
-- Metalua: $Id: mlp_expr.lua,v 1.7 2006/11/15 09:07:50 fab13n Exp $
|
||||
--
|
||||
-- Summary: metalua parser, expression parser. This is part of the
|
||||
-- definition of module [mlp].
|
||||
--
|
||||
----------------------------------------------------------------------
|
||||
--
|
||||
-- Copyright (c) 2006, Fabien Fleutot <metalua@gmail.com>.
|
||||
--
|
||||
-- This software is released under the MIT Licence, see licence.txt
|
||||
-- for details.
|
||||
--
|
||||
----------------------------------------------------------------------
|
||||
-- History:
|
||||
-- $Log: mlp_expr.lua,v $
|
||||
-- Revision 1.7 2006/11/15 09:07:50 fab13n
|
||||
-- debugged meta operators.
|
||||
-- Added command line options handling.
|
||||
--
|
||||
-- Revision 1.6 2006/11/10 02:11:17 fab13n
|
||||
-- compiler faithfulness to 5.1 improved
|
||||
-- gg.expr extended
|
||||
-- mlp.expr refactored
|
||||
--
|
||||
-- Revision 1.5 2006/11/09 09:39:57 fab13n
|
||||
-- some cleanup
|
||||
--
|
||||
-- Revision 1.4 2006/11/07 21:29:02 fab13n
|
||||
-- improved quasi-quoting
|
||||
--
|
||||
-- Revision 1.3 2006/11/07 04:38:00 fab13n
|
||||
-- first bootstrapping version.
|
||||
--
|
||||
-- Revision 1.2 2006/11/05 15:08:34 fab13n
|
||||
-- updated code generation, to be compliant with 5.1
|
||||
--
|
||||
----------------------------------------------------------------------
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
--
|
||||
-- Exported API:
|
||||
-- * [mlp.expr()]
|
||||
-- * [mlp.expr_list()]
|
||||
-- * [mlp.func_val()]
|
||||
--
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
--require "gg"
|
||||
--require "mlp_misc"
|
||||
--require "mlp_table"
|
||||
--require "mlp_meta"
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- These function wrappers (eta-expansions ctually) are just here to break
|
||||
-- some circular dependencies between mlp_xxx.lua files.
|
||||
--------------------------------------------------------------------------------
|
||||
local function _expr (lx) return mlp.expr (lx) end
|
||||
local function _table_content (lx) return mlp.table_content (lx) end
|
||||
local function block (lx) return mlp.block (lx) end
|
||||
local function stat (lx) return mlp.stat (lx) end
|
||||
|
||||
module ("mlp", package.seeall)
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- Non-empty expression list. Actually, this isn't used here, but that's
|
||||
-- handy to give to users.
|
||||
--------------------------------------------------------------------------------
|
||||
expr_list = gg.list{ _expr, separators = "," }
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- Helpers for function applications / method applications
|
||||
--------------------------------------------------------------------------------
|
||||
func_args_content = gg.list {
|
||||
name = "function arguments",
|
||||
_expr, separators = ",", terminators = ")" }
|
||||
|
||||
-- Used to parse methods
|
||||
method_args = gg.multisequence{
|
||||
name = "function argument(s)",
|
||||
{ "{", table_content, "}" },
|
||||
{ "(", func_args_content, ")", builder = fget(1) },
|
||||
{ "+{", quote_content, "}" },
|
||||
function(lx) local r = opt_string(lx); return r and {r} or { } end }
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- [func_val] parses a function, from opening parameters parenthese to
|
||||
-- "end" keyword included. Used for anonymous functions as well as
|
||||
-- function declaration statements (both local and global).
|
||||
--
|
||||
-- It's wrapped in a [_func_val] eta expansion, so that when expr
|
||||
-- parser uses the latter, they will notice updates of [func_val]
|
||||
-- definitions.
|
||||
--------------------------------------------------------------------------------
|
||||
func_params_content = gg.list{ name="function parameters",
|
||||
gg.multisequence{ { "...", builder = "Dots" }, id },
|
||||
separators = ",", terminators = {")", "|"} }
|
||||
|
||||
local _func_params_content = function (lx) return func_params_content(lx) end
|
||||
|
||||
func_val = gg.sequence { name="function body",
|
||||
"(", func_params_content, ")", block, "end", builder = "Function" }
|
||||
|
||||
local _func_val = function (lx) return func_val(lx) end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- Default parser for primary expressions
|
||||
--------------------------------------------------------------------------------
|
||||
function id_or_literal (lx)
|
||||
local a = lx:next()
|
||||
if a.tag~="Id" and a.tag~="String" and a.tag~="Number" then
|
||||
local msg
|
||||
if a.tag=='Eof' then
|
||||
msg = "End of file reached when an expression was expected"
|
||||
elseif a.tag=='Keyword' then
|
||||
msg = "An expression was expected, and `"..a[1]..
|
||||
"' can't start an expression"
|
||||
else
|
||||
msg = "Unexpected expr token " .. _G.table.tostring (a, 'nohash')
|
||||
end
|
||||
gg.parse_error (lx, msg)
|
||||
end
|
||||
return a
|
||||
end
|
||||
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- Builder generator for operators. Wouldn't be worth it if "|x|" notation
|
||||
-- were allowed, but then lua 5.1 wouldn't compile it
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
-- opf1 = |op| |_,a| `Op{ op, a }
|
||||
local function opf1 (op) return
|
||||
function (_,a) return { tag="Op", op, a } end end
|
||||
|
||||
-- opf2 = |op| |a,_,b| `Op{ op, a, b }
|
||||
local function opf2 (op) return
|
||||
function (a,_,b) return { tag="Op", op, a, b } end end
|
||||
|
||||
-- opf2r = |op| |a,_,b| `Op{ op, b, a } -- (args reversed)
|
||||
local function opf2r (op) return
|
||||
function (a,_,b) return { tag="Op", op, b, a } end end
|
||||
|
||||
local function op_ne(a, _, b)
|
||||
-- The first version guarantees to return the same code as Lua,
|
||||
-- but it relies on the non-standard 'ne' operator, which has been
|
||||
-- suppressed from the official AST grammar (although still supported
|
||||
-- in practice by the compiler).
|
||||
-- return { tag="Op", "ne", a, b }
|
||||
return { tag="Op", "not", { tag="Op", "eq", a, b, lineinfo= {
|
||||
first = a.lineinfo.first, last = b.lineinfo.last } } }
|
||||
end
|
||||
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
--
|
||||
-- complete expression
|
||||
--
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
-- FIXME: set line number. In [expr] transformers probably
|
||||
|
||||
expr = gg.expr { name = "expression",
|
||||
|
||||
primary = gg.multisequence{ name="expr primary",
|
||||
{ "(", _expr, ")", builder = "Paren" },
|
||||
{ "function", _func_val, builder = fget(1) },
|
||||
{ "-{", splice_content, "}", builder = fget(1) },
|
||||
{ "+{", quote_content, "}", builder = fget(1) },
|
||||
{ "nil", builder = "Nil" },
|
||||
{ "true", builder = "True" },
|
||||
{ "false", builder = "False" },
|
||||
{ "...", builder = "Dots" },
|
||||
table,
|
||||
id_or_literal },
|
||||
|
||||
infix = { name="expr infix op",
|
||||
{ "+", prec = 60, builder = opf2 "add" },
|
||||
{ "-", prec = 60, builder = opf2 "sub" },
|
||||
{ "*", prec = 70, builder = opf2 "mul" },
|
||||
{ "/", prec = 70, builder = opf2 "div" },
|
||||
{ "%", prec = 70, builder = opf2 "mod" },
|
||||
{ "^", prec = 90, builder = opf2 "pow", assoc = "right" },
|
||||
{ "..", prec = 40, builder = opf2 "concat", assoc = "right" },
|
||||
{ "==", prec = 30, builder = opf2 "eq" },
|
||||
{ "~=", prec = 30, builder = op_ne },
|
||||
{ "<", prec = 30, builder = opf2 "lt" },
|
||||
{ "<=", prec = 30, builder = opf2 "le" },
|
||||
{ ">", prec = 30, builder = opf2r "lt" },
|
||||
{ ">=", prec = 30, builder = opf2r "le" },
|
||||
{ "and",prec = 20, builder = opf2 "and" },
|
||||
{ "or", prec = 10, builder = opf2 "or" } },
|
||||
|
||||
prefix = { name="expr prefix op",
|
||||
{ "not", prec = 80, builder = opf1 "not" },
|
||||
{ "#", prec = 80, builder = opf1 "len" },
|
||||
{ "-", prec = 80, builder = opf1 "unm" } },
|
||||
|
||||
suffix = { name="expr suffix op",
|
||||
{ "[", _expr, "]", builder = function (tab, idx)
|
||||
return {tag="Index", tab, idx[1]} end},
|
||||
{ ".", id, builder = function (tab, field)
|
||||
return {tag="Index", tab, id2string(field[1])} end },
|
||||
{ "(", func_args_content, ")", builder = function(f, args)
|
||||
return {tag="Call", f, unpack(args[1])} end },
|
||||
{ "{", _table_content, "}", builder = function (f, arg)
|
||||
return {tag="Call", f, arg[1]} end},
|
||||
{ ":", id, method_args, builder = function (obj, post)
|
||||
return {tag="Invoke", obj, id2string(post[1]), unpack(post[2])} end},
|
||||
{ "+{", quote_content, "}", builder = function (f, arg)
|
||||
return {tag="Call", f, arg[1] } end },
|
||||
default = { name="opt_string_arg", parse = mlp.opt_string, builder = function(f, arg)
|
||||
return {tag="Call", f, arg } end } } }
|
||||
89
lualibs/metalua/mlp_ext.lua
Normal file
89
lualibs/metalua/mlp_ext.lua
Normal file
@@ -0,0 +1,89 @@
|
||||
--------------------------------------------------------------------------------
|
||||
--
|
||||
-- Non-Lua syntax extensions
|
||||
--
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
module ("mlp", package.seeall)
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- Alebraic Datatypes
|
||||
--------------------------------------------------------------------------------
|
||||
local function adt (lx)
|
||||
local tagval = id (lx) [1]
|
||||
local tagkey = {tag="Pair", {tag="String", "tag"}, {tag="String", tagval} }
|
||||
if lx:peek().tag == "String" or lx:peek().tag == "Number" then
|
||||
return { tag="Table", tagkey, lx:next() }
|
||||
elseif lx:is_keyword (lx:peek(), "{") then
|
||||
local x = table (lx)
|
||||
_G.table.insert (x, 1, tagkey)
|
||||
return x
|
||||
else return { tag="Table", tagkey } end
|
||||
end
|
||||
|
||||
expr:add{ "`", adt, builder = fget(1) }
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- Anonymous lambda
|
||||
--------------------------------------------------------------------------------
|
||||
local lambda_expr = gg.sequence{
|
||||
"|", func_params_content, "|", expr,
|
||||
builder= function (x)
|
||||
local li = x[2].lineinfo
|
||||
return { tag="Function", x[1],
|
||||
{ {tag="Return", x[2], lineinfo=li }, lineinfo=li } }
|
||||
end }
|
||||
|
||||
-- In an earlier version, lambda_expr took an expr_list rather than an expr
|
||||
-- after the 2nd bar. However, it happened to be much more of a burden than an
|
||||
-- help, So finally I disabled it. If you want to return several results,
|
||||
-- use the long syntax.
|
||||
--------------------------------------------------------------------------------
|
||||
-- local lambda_expr = gg.sequence{
|
||||
-- "|", func_params_content, "|", expr_list,
|
||||
-- builder= function (x)
|
||||
-- return {tag="Function", x[1], { {tag="Return", unpack(x[2]) } } } end }
|
||||
|
||||
expr:add (lambda_expr)
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- Allows to write "a `f` b" instead of "f(a, b)". Taken from Haskell.
|
||||
-- This is not part of Lua 5.1 syntax, so it's added to the expression
|
||||
-- afterwards, so that it's easier to disable.
|
||||
--------------------------------------------------------------------------------
|
||||
local function expr_in_backquotes (lx) return expr(lx, 35) end
|
||||
|
||||
expr.infix:add{ name = "infix function",
|
||||
"`", expr_in_backquotes, "`", prec = 35, assoc="left",
|
||||
builder = function(a, op, b) return {tag="Call", op[1], a, b} end }
|
||||
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- table.override assignment
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
mlp.lexer:add "<-"
|
||||
stat.assignments["<-"] = function (a, b)
|
||||
assert( #a==1 and #b==1, "No multi-args for '<-'")
|
||||
return { tag="Call", { tag="Index", { tag="Id", "table" },
|
||||
{ tag="String", "override" } },
|
||||
a[1], b[1]}
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- C-style op+assignments
|
||||
--------------------------------------------------------------------------------
|
||||
local function op_assign(kw, op)
|
||||
local function rhs(a, b)
|
||||
return { tag="Op", op, a, b }
|
||||
end
|
||||
local function f(a,b)
|
||||
return { tag="Set", a, _G.table.imap(rhs, a, b) }
|
||||
end
|
||||
mlp.lexer:add (kw)
|
||||
mlp.stat.assignments[kw] = f
|
||||
end
|
||||
|
||||
_G.table.iforeach (op_assign,
|
||||
{"+=", "-=", "*=", "/="},
|
||||
{"add", "sub", "mul", "div"})
|
||||
32
lualibs/metalua/mlp_lexer.lua
Normal file
32
lualibs/metalua/mlp_lexer.lua
Normal file
@@ -0,0 +1,32 @@
|
||||
----------------------------------------------------------------------
|
||||
-- Metalua: $Id: mll.lua,v 1.3 2006/11/15 09:07:50 fab13n Exp $
|
||||
--
|
||||
-- Summary: Source file lexer. ~~Currently only works on strings.
|
||||
-- Some API refactoring is needed.
|
||||
--
|
||||
----------------------------------------------------------------------
|
||||
--
|
||||
-- Copyright (c) 2006-2007, Fabien Fleutot <metalua@gmail.com>.
|
||||
--
|
||||
-- This software is released under the MIT Licence, see licence.txt
|
||||
-- for details.
|
||||
--
|
||||
----------------------------------------------------------------------
|
||||
|
||||
module ("mlp", package.seeall)
|
||||
|
||||
require "lexer"
|
||||
|
||||
local mlp_lexer = lexer.lexer:clone()
|
||||
|
||||
local keywords = {
|
||||
"and", "break", "do", "else", "elseif",
|
||||
"end", "false", "for", "function", "if",
|
||||
"in", "local", "nil", "not", "or", "repeat",
|
||||
"return", "then", "true", "until", "while",
|
||||
"...", "..", "==", ">=", "<=", "~=",
|
||||
"+{", "-{" }
|
||||
|
||||
for w in values(keywords) do mlp_lexer:add(w) end
|
||||
|
||||
_M.lexer = mlp_lexer
|
||||
118
lualibs/metalua/mlp_meta.lua
Normal file
118
lualibs/metalua/mlp_meta.lua
Normal file
@@ -0,0 +1,118 @@
|
||||
----------------------------------------------------------------------
|
||||
-- Metalua: $Id: mlp_meta.lua,v 1.4 2006/11/15 09:07:50 fab13n Exp $
|
||||
--
|
||||
-- Summary: Meta-operations: AST quasi-quoting and splicing
|
||||
--
|
||||
----------------------------------------------------------------------
|
||||
--
|
||||
-- Copyright (c) 2006, Fabien Fleutot <metalua@gmail.com>.
|
||||
--
|
||||
-- This software is released under the MIT Licence, see licence.txt
|
||||
-- for details.
|
||||
--
|
||||
----------------------------------------------------------------------
|
||||
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
--
|
||||
-- Exported API:
|
||||
-- * [mlp.splice_content()]
|
||||
-- * [mlp.quote_content()]
|
||||
--
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
module ("mlp", package.seeall)
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- External splicing: compile an AST into a chunk, load and evaluate
|
||||
-- that chunk, and replace the chunk by its result (which must also be
|
||||
-- an AST).
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
function splice (ast)
|
||||
local f = mlc.function_of_ast(ast, '=splice')
|
||||
local result=f()
|
||||
return result
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- Going from an AST to an AST representing that AST
|
||||
-- the only key being lifted in this version is ["tag"]
|
||||
--------------------------------------------------------------------------------
|
||||
function quote (t)
|
||||
--print("QUOTING:", _G.table.tostring(t, 60))
|
||||
local cases = { }
|
||||
function cases.table (t)
|
||||
local mt = { tag = "Table" }
|
||||
--_G.table.insert (mt, { tag = "Pair", quote "quote", { tag = "True" } })
|
||||
if t.tag == "Splice" then
|
||||
assert (#t==1, "Invalid splice")
|
||||
local sp = t[1]
|
||||
return sp
|
||||
elseif t.tag then
|
||||
_G.table.insert (mt, { tag = "Pair", quote "tag", quote (t.tag) })
|
||||
end
|
||||
for _, v in ipairs (t) do
|
||||
_G.table.insert (mt, quote(v))
|
||||
end
|
||||
return mt
|
||||
end
|
||||
function cases.number (t) return { tag = "Number", t, quote = true } end
|
||||
function cases.string (t) return { tag = "String", t, quote = true } end
|
||||
return cases [ type (t) ] (t)
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- when this variable is false, code inside [-{...}] is compiled and
|
||||
-- avaluated immediately. When it's true (supposedly when we're
|
||||
-- parsing data inside a quasiquote), [-{foo}] is replaced by
|
||||
-- [`Splice{foo}], which will be unpacked by [quote()].
|
||||
--------------------------------------------------------------------------------
|
||||
in_a_quote = false
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- Parse the inside of a "-{ ... }"
|
||||
--------------------------------------------------------------------------------
|
||||
function splice_content (lx)
|
||||
local parser_name = "expr"
|
||||
if lx:is_keyword (lx:peek(2), ":") then
|
||||
local a = lx:next()
|
||||
lx:next() -- skip ":"
|
||||
assert (a.tag=="Id", "Invalid splice parser name")
|
||||
parser_name = a[1]
|
||||
end
|
||||
local ast = mlp[parser_name](lx)
|
||||
if in_a_quote then
|
||||
--printf("SPLICE_IN_QUOTE:\n%s", _G.table.tostring(ast, "nohash", 60))
|
||||
return { tag="Splice", ast }
|
||||
else
|
||||
if parser_name == "expr" then ast = { { tag="Return", ast } }
|
||||
elseif parser_name == "stat" then ast = { ast }
|
||||
elseif parser_name ~= "block" then
|
||||
error ("splice content must be an expr, stat or block") end
|
||||
--printf("EXEC THIS SPLICE:\n%s", _G.table.tostring(ast, "nohash", 60))
|
||||
return splice (ast)
|
||||
end
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- Parse the inside of a "+{ ... }"
|
||||
--------------------------------------------------------------------------------
|
||||
function quote_content (lx)
|
||||
local parser
|
||||
if lx:is_keyword (lx:peek(2), ":") then -- +{parser: content }
|
||||
parser = mlp[id(lx)[1]]
|
||||
lx:next()
|
||||
else -- +{ content }
|
||||
parser = mlp.expr
|
||||
end
|
||||
|
||||
local prev_iq = in_a_quote
|
||||
in_a_quote = true
|
||||
--print("IN_A_QUOTE")
|
||||
local content = parser (lx)
|
||||
local q_content = quote (content)
|
||||
in_a_quote = prev_iq
|
||||
return q_content
|
||||
end
|
||||
|
||||
185
lualibs/metalua/mlp_misc.lua
Normal file
185
lualibs/metalua/mlp_misc.lua
Normal file
@@ -0,0 +1,185 @@
|
||||
----------------------------------------------------------------------
|
||||
-- Metalua: $Id: mlp_misc.lua,v 1.6 2006/11/15 09:07:50 fab13n Exp $
|
||||
--
|
||||
-- Summary: metalua parser, miscellaneous utility functions.
|
||||
--
|
||||
----------------------------------------------------------------------
|
||||
--
|
||||
-- Copyright (c) 2006, Fabien Fleutot <metalua@gmail.com>.
|
||||
--
|
||||
-- This software is released under the MIT Licence, see licence.txt
|
||||
-- for details.
|
||||
--
|
||||
----------------------------------------------------------------------
|
||||
-- History:
|
||||
-- $Log: mlp_misc.lua,v $
|
||||
-- Revision 1.6 2006/11/15 09:07:50 fab13n
|
||||
-- debugged meta operators.
|
||||
-- Added command line options handling.
|
||||
--
|
||||
-- Revision 1.5 2006/11/10 02:11:17 fab13n
|
||||
-- compiler faithfulness to 5.1 improved
|
||||
-- gg.expr extended
|
||||
-- mlp.expr refactored
|
||||
--
|
||||
-- Revision 1.4 2006/11/09 09:39:57 fab13n
|
||||
-- some cleanup
|
||||
--
|
||||
-- Revision 1.3 2006/11/07 04:38:00 fab13n
|
||||
-- first bootstrapping version.
|
||||
--
|
||||
-- Revision 1.2 2006/11/05 15:08:34 fab13n
|
||||
-- updated code generation, to be compliant with 5.1
|
||||
--
|
||||
----------------------------------------------------------------------
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
--
|
||||
-- Exported API:
|
||||
-- * [mlp.fget()]
|
||||
-- * [mlp.id()]
|
||||
-- * [mlp.opt_id()]
|
||||
-- * [mlp.id_list()]
|
||||
-- * [mlp.gensym()]
|
||||
-- * [mlp.string()]
|
||||
-- * [mlp.opt_string()]
|
||||
-- * [mlp.id2string()]
|
||||
--
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
--require "gg"
|
||||
--require "mll"
|
||||
|
||||
module ("mlp", package.seeall)
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- returns a function that takes the [n]th element of a table.
|
||||
-- if [tag] is provided, then this element is expected to be a
|
||||
-- table, and this table receives a "tag" field whose value is
|
||||
-- set to [tag].
|
||||
--
|
||||
-- The primary purpose of this is to generate builders for
|
||||
-- grammar generators. It has little purpose in metalua, as lambda has
|
||||
-- a lightweight syntax.
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
function fget (n, tag)
|
||||
assert (type (n) == "number")
|
||||
if tag then
|
||||
assert (type (tag) == "string")
|
||||
return function (x)
|
||||
assert (type (x[n]) == "table")
|
||||
return {tag=tag, unpack(x[n])} end
|
||||
else
|
||||
return function (x) return x[n] end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- Try to read an identifier (possibly as a splice), or return [false] if no
|
||||
-- id is found.
|
||||
--------------------------------------------------------------------------------
|
||||
function opt_id (lx)
|
||||
local a = lx:peek();
|
||||
if lx:is_keyword (a, "-{") then
|
||||
local v = gg.sequence{ "-{", splice_content, "}" } (lx) [1]
|
||||
if v.tag ~= "Id" and v.tag ~= "Splice" then
|
||||
gg.parse_error(lx,"Bad id splice")
|
||||
end
|
||||
return v
|
||||
elseif a.tag == "Id" then return lx:next()
|
||||
else return false end
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- Mandatory reading of an id: causes an error if it can't read one.
|
||||
--------------------------------------------------------------------------------
|
||||
function id (lx)
|
||||
return opt_id (lx) or gg.parse_error(lx,"Identifier expected")
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- Common helper function
|
||||
--------------------------------------------------------------------------------
|
||||
id_list = gg.list { primary = mlp.id, separators = "," }
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- Symbol generator: [gensym()] returns a guaranteed-to-be-unique identifier.
|
||||
-- The main purpose is to avoid variable capture in macros.
|
||||
--
|
||||
-- If a string is passed as an argument, theis string will be part of the
|
||||
-- id name (helpful for macro debugging)
|
||||
--------------------------------------------------------------------------------
|
||||
local gensymidx = 0
|
||||
|
||||
function gensym (arg)
|
||||
gensymidx = gensymidx + 1
|
||||
return { tag="Id", _G.string.format(".%i.%s", gensymidx, arg or "")}
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- Converts an identifier into a string. Hopefully one day it'll handle
|
||||
-- splices gracefully, but that proves quite tricky.
|
||||
--------------------------------------------------------------------------------
|
||||
function id2string (id)
|
||||
--print("id2string:", disp.ast(id))
|
||||
if id.tag == "Id" then id.tag = "String"; return id
|
||||
elseif id.tag == "Splice" then
|
||||
assert (in_a_quote, "can't do id2string on an outermost splice")
|
||||
error ("id2string on splice not implemented")
|
||||
-- Evaluating id[1] will produce `Id{ xxx },
|
||||
-- and we want it to produce `String{ xxx }
|
||||
-- Morally, this is what I want:
|
||||
-- return `String{ `Index{ `Splice{ id[1] }, `Number 1 } }
|
||||
-- That is, without sugar:
|
||||
return {tag="String", {tag="Index", {tag="Splice", id[1] },
|
||||
{tag="Number", 1 } } }
|
||||
else error ("Identifier expected: ".._G.table.tostring(id, 'nohash')) end
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- Read a string, possibly spliced, or return an error if it can't
|
||||
--------------------------------------------------------------------------------
|
||||
function string (lx)
|
||||
local a = lx:peek()
|
||||
if lx:is_keyword (a, "-{") then
|
||||
local v = gg.sequence{ "-{", splice_content, "}" } (lx) [1]
|
||||
if v.tag ~= "" and v.tag ~= "Splice" then
|
||||
gg.parse_error(lx,"Bad string splice")
|
||||
end
|
||||
return v
|
||||
elseif a.tag == "String" then return lx:next()
|
||||
else error "String expected" end
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- Try to read a string, or return false if it can't. No splice allowed.
|
||||
--------------------------------------------------------------------------------
|
||||
function opt_string (lx)
|
||||
return lx:peek().tag == "String" and lx:next()
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- Chunk reader: block + Eof
|
||||
--------------------------------------------------------------------------------
|
||||
function skip_initial_sharp_comment (lx)
|
||||
-- Dirty hack: I'm happily fondling lexer's private parts
|
||||
-- FIXME: redundant with lexer:newstream()
|
||||
lx :sync()
|
||||
local i = lx.src:match ("^#.-\n()", lx.i)
|
||||
if i then lx.i, lx.column_offset, lx.line = i, i, lx.line+1 end
|
||||
end
|
||||
|
||||
local function _chunk (lx)
|
||||
if lx:peek().tag == 'Eof' then return { } -- handle empty files
|
||||
else
|
||||
skip_initial_sharp_comment (lx)
|
||||
local chunk = block (lx)
|
||||
if lx:peek().tag ~= "Eof" then error "End-of-file expected" end
|
||||
return chunk
|
||||
end
|
||||
end
|
||||
|
||||
-- chunk is wrapped in a sequence so that it has a "transformer" field.
|
||||
chunk = gg.sequence { _chunk, builder = unpack }
|
||||
226
lualibs/metalua/mlp_stat.lua
Normal file
226
lualibs/metalua/mlp_stat.lua
Normal file
@@ -0,0 +1,226 @@
|
||||
----------------------------------------------------------------------
|
||||
-- Metalua: $Id: mlp_stat.lua,v 1.7 2006/11/15 09:07:50 fab13n Exp $
|
||||
--
|
||||
-- Summary: metalua parser, statement/block parser. This is part of
|
||||
-- the definition of module [mlp].
|
||||
--
|
||||
----------------------------------------------------------------------
|
||||
--
|
||||
-- Copyright (c) 2006, Fabien Fleutot <metalua@gmail.com>.
|
||||
--
|
||||
-- This software is released under the MIT Licence, see licence.txt
|
||||
-- for details.
|
||||
--
|
||||
----------------------------------------------------------------------
|
||||
--
|
||||
----------------------------------------------------------------------
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
--
|
||||
-- Exports API:
|
||||
-- * [mlp.stat()]
|
||||
-- * [mlp.block()]
|
||||
-- * [mlp.for_header()]
|
||||
--
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- eta-expansions to break circular dependency
|
||||
--------------------------------------------------------------------------------
|
||||
local expr = function (lx) return mlp.expr (lx) end
|
||||
local func_val = function (lx) return mlp.func_val (lx) end
|
||||
local expr_list = function (lx) return mlp.expr_list(lx) end
|
||||
|
||||
module ("mlp", package.seeall)
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- List of all keywords that indicate the end of a statement block. Users are
|
||||
-- likely to extend this list when designing extensions.
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
|
||||
local block_terminators = { "else", "elseif", "end", "until", ")", "}", "]" }
|
||||
|
||||
-- FIXME: this must be handled from within GG!!!
|
||||
function block_terminators:add(x)
|
||||
if type (x) == "table" then for _, y in ipairs(x) do self:add (y) end
|
||||
else _G.table.insert (self, x) end
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- list of statements, possibly followed by semicolons
|
||||
--------------------------------------------------------------------------------
|
||||
block = gg.list {
|
||||
name = "statements block",
|
||||
terminators = block_terminators,
|
||||
primary = function (lx)
|
||||
-- FIXME use gg.optkeyword()
|
||||
local x = stat (lx)
|
||||
if lx:is_keyword (lx:peek(), ";") then lx:next() end
|
||||
return x
|
||||
end }
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- Helper function for "return <expr_list>" parsing.
|
||||
-- Called when parsing return statements.
|
||||
-- The specific test for initial ";" is because it's not a block terminator,
|
||||
-- so without itgg.list would choke on "return ;" statements.
|
||||
-- We don't make a modified copy of block_terminators because this list
|
||||
-- is sometimes modified at runtime, and the return parser would get out of
|
||||
-- sync if it was relying on a copy.
|
||||
--------------------------------------------------------------------------------
|
||||
local return_expr_list_parser = gg.multisequence{
|
||||
{ ";" , builder = function() return { } end },
|
||||
default = gg.list {
|
||||
expr, separators = ",", terminators = block_terminators } }
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- for header, between [for] and [do] (exclusive).
|
||||
-- Return the `Forxxx{...} AST, without the body element (the last one).
|
||||
--------------------------------------------------------------------------------
|
||||
function for_header (lx)
|
||||
local var = mlp.id (lx)
|
||||
if lx:is_keyword (lx:peek(), "=") then
|
||||
-- Fornum: only 1 variable
|
||||
lx:next() -- skip "="
|
||||
local e = expr_list (lx)
|
||||
assert (2 <= #e and #e <= 3, "2 or 3 values in a fornum")
|
||||
return { tag="Fornum", var, unpack (e) }
|
||||
else
|
||||
-- Forin: there might be several vars
|
||||
local a = lx:is_keyword (lx:next(), ",", "in")
|
||||
if a=="in" then var_list = { var, lineinfo = var.lineinfo } else
|
||||
-- several vars; first "," skipped, read other vars
|
||||
var_list = gg.list{
|
||||
primary = id, separators = ",", terminators = "in" } (lx)
|
||||
_G.table.insert (var_list, 1, var) -- put back the first variable
|
||||
lx:next() -- skip "in"
|
||||
end
|
||||
local e = expr_list (lx)
|
||||
return { tag="Forin", var_list, e }
|
||||
end
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- Function def parser helper: id ( . id ) *
|
||||
--------------------------------------------------------------------------------
|
||||
local function fn_builder (list)
|
||||
local r = list[1]
|
||||
for i = 2, #list do r = { tag="Index", r, id2string(list[i]) } end
|
||||
return r
|
||||
end
|
||||
local func_name = gg.list{ id, separators = ".", builder = fn_builder }
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- Function def parser helper: ( : id )?
|
||||
--------------------------------------------------------------------------------
|
||||
local method_name = gg.onkeyword{ name = "method invocation", ":", id,
|
||||
transformers = { function(x) return x and id2string(x) end } }
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- Function def builder
|
||||
--------------------------------------------------------------------------------
|
||||
local function funcdef_builder(x)
|
||||
local name, method, func = x[1], x[2], x[3]
|
||||
if method then
|
||||
name = { tag="Index", name, method, lineinfo = {
|
||||
first = name.lineinfo.first,
|
||||
last = method.lineinfo.last } }
|
||||
_G.table.insert (func[1], 1, {tag="Id", "self"})
|
||||
end
|
||||
local r = { tag="Set", {name}, {func} }
|
||||
r[1].lineinfo = name.lineinfo
|
||||
r[2].lineinfo = func.lineinfo
|
||||
return r
|
||||
end
|
||||
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- if statement builder
|
||||
--------------------------------------------------------------------------------
|
||||
local function if_builder (x)
|
||||
local cb_pairs, else_block, r = x[1], x[2], {tag="If"}
|
||||
for i=1,#cb_pairs do r[2*i-1]=cb_pairs[i][1]; r[2*i]=cb_pairs[i][2] end
|
||||
if else_block then r[#r+1] = else_block end
|
||||
return r
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- produce a list of (expr,block) pairs
|
||||
--------------------------------------------------------------------------------
|
||||
local elseifs_parser = gg.list {
|
||||
gg.sequence { expr, "then", block },
|
||||
separators = "elseif",
|
||||
terminators = { "else", "end" } }
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- assignments and calls: statements that don't start with a keyword
|
||||
--------------------------------------------------------------------------------
|
||||
local function assign_or_call_stat_parser (lx)
|
||||
local e = expr_list (lx)
|
||||
local a = lx:is_keyword(lx:peek())
|
||||
local op = a and stat.assignments[a]
|
||||
if op then
|
||||
--FIXME: check that [e] is a LHS
|
||||
lx:next()
|
||||
local v = expr_list (lx)
|
||||
if type(op)=="string" then return { tag=op, e, v }
|
||||
else return op (e, v) end
|
||||
else
|
||||
assert (#e > 0)
|
||||
if #e > 1 then
|
||||
gg.parse_error (lx,
|
||||
"comma is not a valid statement separator; statement can be "..
|
||||
"separated by semicolons, or not separated at all") end
|
||||
if e[1].tag ~= "Call" and e[1].tag ~= "Invoke" then
|
||||
local typename
|
||||
if e[1].tag == 'Id' then
|
||||
typename = '("'..e[1][1]..'") is an identifier'
|
||||
elseif e[1].tag == 'Op' then
|
||||
typename = "is an arithmetic operation"
|
||||
else typename = "is of type '"..(e[1].tag or "<list>").."'" end
|
||||
|
||||
gg.parse_error (lx, "This expression " .. typename ..
|
||||
"; a statement was expected, and only function and method call "..
|
||||
"expressions can be used as statements");
|
||||
end
|
||||
return e[1]
|
||||
end
|
||||
end
|
||||
|
||||
local_stat_parser = gg.multisequence{
|
||||
-- local function <name> <func_val>
|
||||
{ "function", id, func_val, builder =
|
||||
function(x)
|
||||
local vars = { x[1], lineinfo = x[1].lineinfo }
|
||||
local vals = { x[2], lineinfo = x[2].lineinfo }
|
||||
return { tag="Localrec", vars, vals }
|
||||
end },
|
||||
-- local <id_list> ( = <expr_list> )?
|
||||
default = gg.sequence{ id_list, gg.onkeyword{ "=", expr_list },
|
||||
builder = function(x) return {tag="Local", x[1], x[2] or { } } end } }
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- statement
|
||||
--------------------------------------------------------------------------------
|
||||
stat = gg.multisequence {
|
||||
name="statement",
|
||||
{ "do", block, "end", builder =
|
||||
function (x) return { tag="Do", unpack (x[1]) } end },
|
||||
{ "for", for_header, "do", block, "end", builder =
|
||||
function (x) x[1][#x[1]+1] = x[2]; return x[1] end },
|
||||
{ "function", func_name, method_name, func_val, builder=funcdef_builder },
|
||||
{ "while", expr, "do", block, "end", builder = "While" },
|
||||
{ "repeat", block, "until", expr, builder = "Repeat" },
|
||||
{ "local", local_stat_parser, builder = fget (1) },
|
||||
{ "return", return_expr_list_parser, builder = fget (1, "Return") },
|
||||
{ "break", builder = function() return { tag="Break" } end },
|
||||
{ "-{", splice_content, "}", builder = fget(1) },
|
||||
{ "if", elseifs_parser, gg.onkeyword{ "else", block }, "end",
|
||||
builder = if_builder },
|
||||
default = assign_or_call_stat_parser }
|
||||
|
||||
stat.assignments = {
|
||||
["="] = "Set" }
|
||||
|
||||
function stat.assignments:add(k, v) self[k] = v end
|
||||
92
lualibs/metalua/mlp_table.lua
Normal file
92
lualibs/metalua/mlp_table.lua
Normal file
@@ -0,0 +1,92 @@
|
||||
----------------------------------------------------------------------
|
||||
-- Metalua: $Id: mlp_table.lua,v 1.5 2006/11/10 02:11:17 fab13n Exp $
|
||||
--
|
||||
-- Summary: metalua parser, table constructor parser. This is part
|
||||
-- of thedefinition of module [mlp].
|
||||
--
|
||||
----------------------------------------------------------------------
|
||||
--
|
||||
-- Copyright (c) 2006, Fabien Fleutot <metalua@gmail.com>.
|
||||
--
|
||||
-- This software is released under the MIT Licence, see licence.txt
|
||||
-- for details.
|
||||
--
|
||||
----------------------------------------------------------------------
|
||||
-- History:
|
||||
-- $Log: mlp_table.lua,v $
|
||||
-- Revision 1.5 2006/11/10 02:11:17 fab13n
|
||||
-- compiler faithfulness to 5.1 improved
|
||||
-- gg.expr extended
|
||||
-- mlp.expr refactored
|
||||
--
|
||||
-- Revision 1.4 2006/11/09 09:39:57 fab13n
|
||||
-- some cleanup
|
||||
--
|
||||
-- Revision 1.3 2006/11/07 04:38:00 fab13n
|
||||
-- first bootstrapping version.
|
||||
--
|
||||
-- Revision 1.2 2006/11/05 15:08:34 fab13n
|
||||
-- updated code generation, to be compliant with 5.1
|
||||
--
|
||||
----------------------------------------------------------------------
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
--
|
||||
-- Exported API:
|
||||
-- * [mlp.table_field()]
|
||||
-- * [mlp.table_content()]
|
||||
-- * [mlp.table()]
|
||||
--
|
||||
-- KNOWN BUG: doesn't handle final ";" or "," before final "}"
|
||||
--
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
--require "gg"
|
||||
--require "mll"
|
||||
--require "mlp_misc"
|
||||
|
||||
module ("mlp", package.seeall)
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- eta expansion to break circular dependencies:
|
||||
--------------------------------------------------------------------------------
|
||||
local function _expr (lx) return expr(lx) end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- [[key] = value] table field definition
|
||||
--------------------------------------------------------------------------------
|
||||
local bracket_field = gg.sequence{ "[", _expr, "]", "=", _expr, builder = "Pair" }
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- [id = value] or [value] table field definition;
|
||||
-- [[key]=val] are delegated to [bracket_field()]
|
||||
--------------------------------------------------------------------------------
|
||||
function table_field (lx)
|
||||
if lx:is_keyword (lx:peek(), "[") then return bracket_field (lx) end
|
||||
local e = _expr (lx)
|
||||
if lx:is_keyword (lx:peek(), "=") then
|
||||
lx:next(); -- skip the "="
|
||||
local key = id2string(e)
|
||||
local val = _expr(lx)
|
||||
local r = { tag="Pair", key, val }
|
||||
r.lineinfo = { first = key.lineinfo.first, last = val.lineinfo.last }
|
||||
return r
|
||||
else return e end
|
||||
end
|
||||
|
||||
local function _table_field(lx) return table_field(lx) end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- table constructor, without enclosing braces; returns a full table object
|
||||
--------------------------------------------------------------------------------
|
||||
table_content = gg.list { _table_field,
|
||||
separators = { ",", ";" }, terminators = "}", builder = "Table" }
|
||||
|
||||
local function _table_content(lx) return table_content(lx) end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- complete table constructor including [{...}]
|
||||
--------------------------------------------------------------------------------
|
||||
table = gg.sequence{ "{", _table_content, "}", builder = fget(1) }
|
||||
|
||||
|
||||
@@ -1,16 +1,28 @@
|
||||
--
|
||||
-- MobDebug 0.43
|
||||
-- Copyright Paul Kulchenko 2011
|
||||
-- MobDebug 0.44
|
||||
-- Copyright Paul Kulchenko 2011-2012
|
||||
-- Based on RemDebug 1.0 (http://www.keplerproject.org/remdebug)
|
||||
--
|
||||
|
||||
(function()
|
||||
local mobdebug = {
|
||||
_NAME = "mobdebug",
|
||||
_COPYRIGHT = "Paul Kulchenko",
|
||||
_DESCRIPTION = "Mobile Remote Debugger for the Lua programming language",
|
||||
_VERSION = "0.44"
|
||||
}
|
||||
|
||||
module("mobdebug", package.seeall)
|
||||
|
||||
_COPYRIGHT = "Paul Kulchenko"
|
||||
_DESCRIPTION = "Mobile Remote Debugger for the Lua programming language"
|
||||
_VERSION = "0.43"
|
||||
local coroutine = coroutine
|
||||
local error = error
|
||||
local getfenv = getfenv
|
||||
local loadstring = loadstring
|
||||
local io = io
|
||||
local os = os
|
||||
local pairs = pairs
|
||||
local require = require
|
||||
local setmetatable = setmetatable
|
||||
local string = string
|
||||
local tonumber = tonumber
|
||||
local mosync = mosync
|
||||
|
||||
-- this is a socket class that implements maConnect interface
|
||||
local function socketMobileLua()
|
||||
@@ -18,45 +30,45 @@ local function socketMobileLua()
|
||||
self.select = function() return {} end
|
||||
self.connect = coroutine.wrap(function(host, port)
|
||||
while true do
|
||||
local connection = maConnect("socket://" .. host .. ":" .. port)
|
||||
local connection = mosync.maConnect("socket://" .. host .. ":" .. port)
|
||||
|
||||
if connection > 0 then
|
||||
local event = SysEventCreate()
|
||||
local event = mosync.SysEventCreate()
|
||||
while true do
|
||||
maWait(0)
|
||||
maGetEvent(event)
|
||||
local eventType = SysEventGetType(event)
|
||||
if (EVENT_TYPE_CLOSE == eventType) then maExit(0) end
|
||||
if (EVENT_TYPE_CONN == eventType and
|
||||
SysEventGetConnHandle(event) == connection and
|
||||
SysEventGetConnOpType(event) == CONNOP_CONNECT) then
|
||||
mosync.maWait(0)
|
||||
mosync.maGetEvent(event)
|
||||
local eventType = mosync.SysEventGetType(event)
|
||||
if (mosync.EVENT_TYPE_CLOSE == eventType) then mosync.maExit(0) end
|
||||
if (mosync.EVENT_TYPE_CONN == eventType and
|
||||
mosync.SysEventGetConnHandle(event) == connection and
|
||||
mosync.SysEventGetConnOpType(event) == mosync.CONNOP_CONNECT) then
|
||||
-- result > 0 ? success : error
|
||||
if not (SysEventGetConnResult(event) > 0) then connection = nil end
|
||||
if not (mosync.SysEventGetConnResult(event) > 0) then connection = nil end
|
||||
break
|
||||
end
|
||||
end
|
||||
SysFree(event)
|
||||
mosync.SysFree(event)
|
||||
end
|
||||
|
||||
host, port = coroutine.yield(connection and (function ()
|
||||
local self = {}
|
||||
local outBuffer = SysAlloc(1000)
|
||||
local inBuffer = SysAlloc(1000)
|
||||
local event = SysEventCreate()
|
||||
local outBuffer = mosync.SysAlloc(1000)
|
||||
local inBuffer = mosync.SysAlloc(1000)
|
||||
local event = mosync.SysEventCreate()
|
||||
local recvBuffer = ""
|
||||
function stringToBuffer(s, buffer)
|
||||
local i = 0
|
||||
for c in s:gmatch(".") do
|
||||
i = i + 1
|
||||
local b = s:byte(i)
|
||||
SysBufferSetByte(buffer, i - 1, b)
|
||||
mosync.SysBufferSetByte(buffer, i - 1, b)
|
||||
end
|
||||
return i
|
||||
end
|
||||
function bufferToString(buffer, len)
|
||||
local s = ""
|
||||
for i = 0, len - 1 do
|
||||
local c = SysBufferGetByte(buffer, i)
|
||||
local c = mosync.SysBufferGetByte(buffer, i)
|
||||
s = s .. string.char(c)
|
||||
end
|
||||
return s
|
||||
@@ -64,16 +76,16 @@ local function socketMobileLua()
|
||||
self.send = coroutine.wrap(function(self, msg)
|
||||
while true do
|
||||
local numberOfBytes = stringToBuffer(msg, outBuffer)
|
||||
maConnWrite(connection, outBuffer, numberOfBytes)
|
||||
mosync.maConnWrite(connection, outBuffer, numberOfBytes)
|
||||
local result = 0
|
||||
while true do
|
||||
maWait(0)
|
||||
maGetEvent(event)
|
||||
local eventType = SysEventGetType(event)
|
||||
if (EVENT_TYPE_CLOSE == eventType) then maExit(0) end
|
||||
if (EVENT_TYPE_CONN == eventType and
|
||||
SysEventGetConnHandle(event) == connection and
|
||||
SysEventGetConnOpType(event) == CONNOP_WRITE) then
|
||||
mosync.maWait(0)
|
||||
mosync.maGetEvent(event)
|
||||
local eventType = mosync.SysEventGetType(event)
|
||||
if (mosync.EVENT_TYPE_CLOSE == eventType) then mosync.maExit(0) end
|
||||
if (mosync.EVENT_TYPE_CONN == eventType and
|
||||
mosync.SysEventGetConnHandle(event) == connection and
|
||||
mosync.SysEventGetConnOpType(event) == mosync.CONNOP_WRITE) then
|
||||
break
|
||||
end
|
||||
end
|
||||
@@ -85,16 +97,16 @@ local function socketMobileLua()
|
||||
local line = recvBuffer
|
||||
while (len and string.len(line) < len) -- either we need len bytes
|
||||
or (not len and not line:find("\n")) do -- or one line (if no len specified)
|
||||
maConnRead(connection, inBuffer, 1000)
|
||||
mosync.maConnRead(connection, inBuffer, 1000)
|
||||
while true do
|
||||
maWait(0)
|
||||
maGetEvent(event)
|
||||
local eventType = SysEventGetType(event)
|
||||
if (EVENT_TYPE_CLOSE == eventType) then maExit(0) end
|
||||
if (EVENT_TYPE_CONN == eventType and
|
||||
SysEventGetConnHandle(event) == connection and
|
||||
SysEventGetConnOpType(event) == CONNOP_READ) then
|
||||
local result = SysEventGetConnResult(event);
|
||||
mosync.maWait(0)
|
||||
mosync.maGetEvent(event)
|
||||
local eventType = mosync.SysEventGetType(event)
|
||||
if (mosync.EVENT_TYPE_CLOSE == eventType) then mosync.maExit(0) end
|
||||
if (mosync.EVENT_TYPE_CONN == eventType and
|
||||
mosync.SysEventGetConnHandle(event) == connection and
|
||||
mosync.SysEventGetConnOpType(event) == mosync.CONNOP_READ) then
|
||||
local result = mosync.SysEventGetConnResult(event);
|
||||
if result > 0 then line = line .. bufferToString(inBuffer, result) end
|
||||
break; -- got the event we wanted; now check if we have all we need
|
||||
end
|
||||
@@ -113,10 +125,10 @@ local function socketMobileLua()
|
||||
end)
|
||||
self.close = coroutine.wrap(function(self)
|
||||
while true do
|
||||
SysFree(inBuffer)
|
||||
SysFree(outBuffer)
|
||||
SysFree(event)
|
||||
maConnClose(connection)
|
||||
mosync.SysFree(inBuffer)
|
||||
mosync.SysFree(outBuffer)
|
||||
mosync.SysFree(event)
|
||||
mosync.maConnClose(connection)
|
||||
coroutine.yield(self)
|
||||
end
|
||||
end)
|
||||
@@ -128,7 +140,7 @@ local function socketMobileLua()
|
||||
return self
|
||||
end
|
||||
|
||||
local socket = maConnect and socketMobileLua() or (require "socket")
|
||||
local socket = mosync and socketMobileLua() or (require "socket")
|
||||
|
||||
--
|
||||
-- RemDebug 1.0 Beta
|
||||
@@ -452,12 +464,12 @@ local function debugger_loop(sfile, sline)
|
||||
end
|
||||
end
|
||||
|
||||
function connect(controller_host, controller_port)
|
||||
local function connect(controller_host, controller_port)
|
||||
return socket.connect(controller_host, controller_port)
|
||||
end
|
||||
|
||||
-- Tries to start the debug session by connecting with a controller
|
||||
function start(controller_host, controller_port)
|
||||
local function start(controller_host, controller_port)
|
||||
server = socket.connect(controller_host, controller_port)
|
||||
if server then
|
||||
local info = debug.getinfo(2, "Sl")
|
||||
@@ -473,7 +485,7 @@ function start(controller_host, controller_port)
|
||||
end
|
||||
end
|
||||
|
||||
function loop(controller_host, controller_port)
|
||||
local function loop(controller_host, controller_port)
|
||||
server = socket.connect(controller_host, controller_port)
|
||||
if server then
|
||||
local function report(trace, err)
|
||||
@@ -512,7 +524,7 @@ end
|
||||
local basedir = ""
|
||||
|
||||
-- Handles server debugging commands
|
||||
function handle(params, client)
|
||||
local function handle(params, client)
|
||||
local _, _, command = string.find(params, "^([a-z]+)")
|
||||
local file, line, watch_idx
|
||||
if command == "run" or command == "step" or command == "out"
|
||||
@@ -724,7 +736,7 @@ function handle(params, client)
|
||||
end
|
||||
|
||||
-- Starts debugging server
|
||||
function listen(host, port)
|
||||
local function listen(host, port)
|
||||
|
||||
local socket = require "socket"
|
||||
|
||||
@@ -757,4 +769,15 @@ function listen(host, port)
|
||||
end
|
||||
end
|
||||
|
||||
end)()
|
||||
-- make public functions available
|
||||
mobdebug.listen = listen
|
||||
mobdebug.loop = loop
|
||||
mobdebug.handle = handle
|
||||
mobdebug.connect = connect
|
||||
mobdebug.start = start
|
||||
|
||||
-- this is needed to make "require 'modebug'" to work when mobdebug
|
||||
-- module is loaded manually
|
||||
package.loaded.mobdebug = mobdebug
|
||||
|
||||
return mobdebug
|
||||
|
||||
@@ -112,13 +112,6 @@ return {
|
||||
line = line+1
|
||||
end
|
||||
|
||||
if (added) then
|
||||
DisplayOutput("\nTYPES\n")
|
||||
for i,v in pairs(assigns) do
|
||||
DisplayOutput(i,v,"\n")
|
||||
end
|
||||
end
|
||||
|
||||
return assigns
|
||||
end,
|
||||
|
||||
|
||||
437
spec/ptx.lua
Normal file
437
spec/ptx.lua
Normal file
@@ -0,0 +1,437 @@
|
||||
-- author: Christoph Kubisch
|
||||
---------------------------------------------------------
|
||||
|
||||
return {
|
||||
exts = {"ptx",},
|
||||
lexer = wxstc.wxSTC_LEX_CPP,
|
||||
apitype = "ptx",
|
||||
sep = "%.",
|
||||
linecomment = "//",
|
||||
|
||||
isfndef = function(str)
|
||||
local l
|
||||
local s,e,cap = string.find(str,"^%s*([A-Za-z0-9_]+%s+[A-Za-z0-9_]+%s*%(.+%))")
|
||||
if (not s) then
|
||||
s,e,cap = string.find(str,"^%s*([A-Za-z0-9_]+%s+[A-Za-z0-9_]+)%s*%(")
|
||||
end
|
||||
if (cap and (string.find(cap,"^return") or string.find(cap,"else"))) then return end
|
||||
return s,e,cap,l
|
||||
end,
|
||||
|
||||
lexerstyleconvert = {
|
||||
text = {wxstc.wxSTC_C_IDENTIFIER,
|
||||
wxstc.wxSTC_C_VERBATIM,
|
||||
wxstc.wxSTC_C_REGEX,
|
||||
wxstc.wxSTC_C_REGEX,
|
||||
wxstc.wxSTC_C_GLOBALCLASS,},
|
||||
|
||||
lexerdef = {wxstc.wxSTC_C_DEFAULT,},
|
||||
comment = {wxstc.wxSTC_C_COMMENT,
|
||||
wxstc.wxSTC_C_COMMENTLINE,
|
||||
wxstc.wxSTC_C_COMMENTDOC,
|
||||
wxstc.wxSTC_C_COMMENTLINEDOC,
|
||||
wxstc.wxSTC_C_COMMENTDOCKEYWORD,
|
||||
wxstc.wxSTC_C_COMMENTDOCKEYWORDERROR,},
|
||||
stringtxt = {wxstc.wxSTC_C_STRING,
|
||||
wxstc.wxSTC_C_CHARACTER,
|
||||
wxstc.wxSTC_C_UUID,},
|
||||
stringeol = {wxstc.wxSTC_C_STRINGEOL,},
|
||||
preprocessor= {wxstc.wxSTC_C_PREPROCESSOR,},
|
||||
operator = {wxstc.wxSTC_C_OPERATOR,},
|
||||
number = {wxstc.wxSTC_C_NUMBER,
|
||||
wxstc.wxSTC_C_WORD},
|
||||
|
||||
keywords0 = {wxstc.wxSTC_C_WORD,},
|
||||
keywords1 = {wxstc.wxSTC_C_WORD2,},
|
||||
},
|
||||
|
||||
keywords = {
|
||||
[[
|
||||
version
|
||||
target
|
||||
address_size
|
||||
|
||||
entry
|
||||
func
|
||||
|
||||
branchtargets
|
||||
calltargets
|
||||
callprototype
|
||||
|
||||
maxnreg
|
||||
maxntid
|
||||
reqntid
|
||||
minnctapersm
|
||||
maxnctapersm
|
||||
pragma
|
||||
|
||||
section
|
||||
file
|
||||
loc
|
||||
|
||||
extern
|
||||
visible
|
||||
|
||||
pragma
|
||||
|
||||
align
|
||||
file
|
||||
maxntid
|
||||
shared
|
||||
branchtargets
|
||||
func
|
||||
minnctapersm
|
||||
sreg
|
||||
callprototype
|
||||
global
|
||||
param
|
||||
target
|
||||
calltargets
|
||||
local
|
||||
pragma
|
||||
tex
|
||||
const
|
||||
loc
|
||||
reg
|
||||
version
|
||||
entry
|
||||
maxnctapersm
|
||||
reqntid
|
||||
visible
|
||||
extern
|
||||
maxnreg
|
||||
section
|
||||
|
||||
s8
|
||||
s16
|
||||
s32
|
||||
s64
|
||||
u8
|
||||
u16
|
||||
u32
|
||||
u64
|
||||
f16
|
||||
f32
|
||||
f64
|
||||
b8
|
||||
b16
|
||||
b32
|
||||
b64
|
||||
pred
|
||||
|
||||
rn
|
||||
rz
|
||||
rm
|
||||
rp
|
||||
|
||||
rni
|
||||
rzi
|
||||
rmi
|
||||
rpi
|
||||
|
||||
ca
|
||||
cg
|
||||
cs
|
||||
lu
|
||||
cv
|
||||
|
||||
wb
|
||||
cg
|
||||
cs
|
||||
wt
|
||||
|
||||
texref
|
||||
samplerref
|
||||
surfref
|
||||
|
||||
sat
|
||||
ftz
|
||||
|
||||
cc
|
||||
|
||||
hi
|
||||
lo
|
||||
wide
|
||||
|
||||
f4e
|
||||
b4e
|
||||
rc8
|
||||
ecl
|
||||
ecr
|
||||
rc16
|
||||
|
||||
finite
|
||||
infinite
|
||||
number
|
||||
notanumber
|
||||
normal
|
||||
subnormal
|
||||
|
||||
approx
|
||||
full
|
||||
|
||||
eq
|
||||
ne
|
||||
lt
|
||||
le
|
||||
gt
|
||||
ge
|
||||
|
||||
equ
|
||||
neu
|
||||
ltu
|
||||
leu
|
||||
gtu
|
||||
geu
|
||||
|
||||
num
|
||||
nan
|
||||
|
||||
ls
|
||||
hs
|
||||
|
||||
volatile
|
||||
|
||||
v2
|
||||
v4
|
||||
|
||||
L1
|
||||
L2
|
||||
|
||||
1d
|
||||
2d
|
||||
3d
|
||||
a1d
|
||||
a2d
|
||||
|
||||
width
|
||||
height
|
||||
depth
|
||||
channel_data_type
|
||||
channel_order
|
||||
normalized_coords
|
||||
|
||||
force_unnormalized_coords
|
||||
filter_mode
|
||||
addr_mode_0
|
||||
addr_mode_1
|
||||
addr_mode_2
|
||||
|
||||
trap
|
||||
clamp
|
||||
zero
|
||||
|
||||
all
|
||||
any
|
||||
uni
|
||||
ballot
|
||||
|
||||
sync
|
||||
arrive
|
||||
red
|
||||
|
||||
cta
|
||||
gl
|
||||
sys
|
||||
|
||||
and
|
||||
or
|
||||
xor
|
||||
cas
|
||||
exch
|
||||
add
|
||||
inc
|
||||
dec
|
||||
min
|
||||
max
|
||||
|
||||
b0
|
||||
b1
|
||||
b2
|
||||
b3
|
||||
h0
|
||||
h1
|
||||
wrap
|
||||
shr7
|
||||
shr15
|
||||
|
||||
byte
|
||||
4byte
|
||||
quad
|
||||
4byte
|
||||
quad
|
||||
|
||||
b8
|
||||
b32
|
||||
b64
|
||||
b32
|
||||
b64
|
||||
]],
|
||||
|
||||
-- functions
|
||||
|
||||
[[
|
||||
add
|
||||
sub
|
||||
add.cc
|
||||
addc
|
||||
sub.cc
|
||||
subc
|
||||
mul
|
||||
mad
|
||||
mul24
|
||||
mad24
|
||||
sad
|
||||
div
|
||||
rem
|
||||
abs
|
||||
neg
|
||||
min
|
||||
max
|
||||
popc
|
||||
clz
|
||||
bfind
|
||||
brev
|
||||
bfe
|
||||
bfi
|
||||
prmt
|
||||
|
||||
rcp
|
||||
sqrt
|
||||
rsqrt
|
||||
sin
|
||||
cos
|
||||
lg2
|
||||
ex2
|
||||
fma
|
||||
|
||||
set
|
||||
setp
|
||||
selp
|
||||
slct
|
||||
|
||||
and
|
||||
or
|
||||
xor
|
||||
not
|
||||
cnot
|
||||
shl
|
||||
shr
|
||||
|
||||
mov
|
||||
ld
|
||||
ldu
|
||||
st
|
||||
prefetch
|
||||
prefetchu
|
||||
isspacep
|
||||
cvta
|
||||
cvt
|
||||
|
||||
tex
|
||||
tld4
|
||||
txq
|
||||
suld
|
||||
sust
|
||||
sured
|
||||
suq
|
||||
|
||||
bra
|
||||
call
|
||||
ret
|
||||
exit
|
||||
|
||||
bar
|
||||
membar
|
||||
atom
|
||||
red
|
||||
vote
|
||||
|
||||
vadd
|
||||
vsub
|
||||
vabsdiff
|
||||
vmin
|
||||
vmax
|
||||
vshl
|
||||
vshr
|
||||
vmad
|
||||
vset
|
||||
|
||||
trap
|
||||
brkpt
|
||||
pmevent
|
||||
|
||||
%clock
|
||||
%laneid
|
||||
%lanemask_gt
|
||||
%pm0
|
||||
%pm1
|
||||
%pm2
|
||||
%pm3
|
||||
%clock64
|
||||
%lanemask_eq
|
||||
%nctaid
|
||||
%smid
|
||||
%ctaid
|
||||
%lanemask_le
|
||||
%ntid
|
||||
%tid
|
||||
%envreg0
|
||||
%envreg1
|
||||
%envreg2
|
||||
%envreg3
|
||||
%envreg4
|
||||
%envreg5
|
||||
%envreg6
|
||||
%envreg7
|
||||
%envreg8
|
||||
%envreg9
|
||||
%envreg10
|
||||
%envreg11
|
||||
%envreg12
|
||||
%envreg13
|
||||
%envreg14
|
||||
%envreg15
|
||||
%envreg16
|
||||
%envreg17
|
||||
%envreg18
|
||||
%envreg19
|
||||
%envreg20
|
||||
%envreg21
|
||||
%envreg22
|
||||
%envreg23
|
||||
%envreg24
|
||||
%envreg25
|
||||
%envreg26
|
||||
%envreg27
|
||||
%envreg28
|
||||
%envreg29
|
||||
%envreg30
|
||||
%envreg31
|
||||
%lanemask_lt
|
||||
%nsmid
|
||||
%warpid
|
||||
%gridid
|
||||
%lanemask_ge
|
||||
%nwarpid
|
||||
WARP_SZ
|
||||
nearest
|
||||
linear
|
||||
wrap
|
||||
mirror
|
||||
clamp_ogl
|
||||
clamp_to_edge
|
||||
clamp_to_border
|
||||
|
||||
sm_20
|
||||
sm_10
|
||||
sm_11
|
||||
sm_12
|
||||
sm_13
|
||||
texmode_unified
|
||||
texmode_independent
|
||||
map_f64_to_f32
|
||||
]],
|
||||
|
||||
},
|
||||
}
|
||||
@@ -13,7 +13,7 @@
|
||||
|
||||
-- style definition
|
||||
-- ----------------------------------------------------
|
||||
-- all entries are optiona
|
||||
-- all entries are optional
|
||||
stattr = {
|
||||
fg = {r,g,b}, -- foreground color 0-255
|
||||
bg = {r,g,b}, -- background color
|
||||
@@ -21,6 +21,13 @@ stattr = {
|
||||
b = false, -- bold
|
||||
u = false, -- underline
|
||||
fill = true, -- fill to lineend
|
||||
-- fn = "Lucida Console", -- font Face Name
|
||||
-- fx = 11, -- font size
|
||||
-- hs = true or {r,g,b}, -- turn hotspot on
|
||||
-- use the specified color as activeForeground
|
||||
-- use "hs = true", to turn it on without changing the color
|
||||
-- HotspotActiveUnderline and HotspotSingleLine are on automatically
|
||||
-- v = true, -- visibility for symbols of the current style
|
||||
}
|
||||
|
||||
style = {
|
||||
|
||||
@@ -5,6 +5,7 @@ local ide = ide
|
||||
local frame = ide.frame
|
||||
local notebook = frame.notebook
|
||||
local openDocuments = ide.openDocuments
|
||||
local uimgr = frame.uimgr
|
||||
|
||||
function NewFile(event)
|
||||
local editor = CreateEditor("untitled.lua")
|
||||
@@ -117,7 +118,7 @@ function OpenFile(event)
|
||||
if fileDialog:ShowModal() == wx.wxID_OK then
|
||||
if not LoadFile(fileDialog:GetPath(), nil, true) then
|
||||
wx.wxMessageBox("Unable to load file '"..fileDialog:GetPath().."'.",
|
||||
"wxLua Error",
|
||||
"Error",
|
||||
wx.wxOK + wx.wxCENTRE, ide.frame)
|
||||
end
|
||||
end
|
||||
@@ -129,8 +130,6 @@ function SaveFile(editor, filePath)
|
||||
if not filePath then
|
||||
return SaveFileAs(editor)
|
||||
else
|
||||
filePath = filePath:gsub("\\","/")
|
||||
|
||||
if (ide.config.savebak) then
|
||||
local backPath = filePath..".bak"
|
||||
os.remove(backPath)
|
||||
@@ -156,7 +155,7 @@ function SaveFile(editor, filePath)
|
||||
return true
|
||||
else
|
||||
wx.wxMessageBox("Unable to save file '"..filePath.."'.",
|
||||
"wxLua Error Saving",
|
||||
"Error",
|
||||
wx.wxOK + wx.wxCENTRE, ide.frame)
|
||||
end
|
||||
end
|
||||
@@ -190,6 +189,7 @@ function SaveFileAs(editor)
|
||||
if SaveFile(editor, filePath) then
|
||||
SetupKeywords(editor, GetFileExt(filePath))
|
||||
IndicateFunctions(editor)
|
||||
if MarkupStyle then MarkupStyle(editor) end
|
||||
saved = true
|
||||
end
|
||||
end
|
||||
@@ -218,7 +218,10 @@ local function removePage(index)
|
||||
selectIndex = selectIndex ~= index and selectIndex
|
||||
|
||||
local delid = nil
|
||||
for id, document in pairs(openDocuments) do
|
||||
for id, document in pairsSorted(openDocuments,
|
||||
function(a, b) -- sort by document index
|
||||
return openDocuments[a].index < openDocuments[b].index
|
||||
end) do
|
||||
local wasselected = document.index == selectIndex
|
||||
if document.index < index then
|
||||
prevIndex = document.index
|
||||
@@ -271,12 +274,7 @@ function SaveModifiedDialog(editor, allow_cancel)
|
||||
local filePath = document.filePath
|
||||
local fileName = document.fileName
|
||||
if document.isModified then
|
||||
local message
|
||||
if fileName then
|
||||
message = "Save changes to '"..fileName.."' before exiting?"
|
||||
else
|
||||
message = "Save changes to 'untitled' before exiting?"
|
||||
end
|
||||
local message = "Do you want to save the changes to '"..(fileName or 'untitled').."'?"
|
||||
local dlg_styles = wx.wxYES_NO + wx.wxCENTRE + wx.wxICON_QUESTION
|
||||
if allow_cancel then dlg_styles = dlg_styles + wx.wxCANCEL end
|
||||
local dialog = wx.wxMessageDialog(ide.frame, message,
|
||||
@@ -470,6 +468,22 @@ function SetOpenFiles(nametab,index)
|
||||
notebook:SetSelection(index or 0)
|
||||
end
|
||||
|
||||
local beforeFullScreenPerspective
|
||||
function ShowFullScreen(setFullScreen)
|
||||
if setFullScreen then
|
||||
beforeFullScreenPerspective = uimgr:SavePerspective()
|
||||
uimgr:GetPane("bottomnotebook"):Show(false)
|
||||
uimgr:GetPane("projpanel"):Show(false)
|
||||
elseif beforeFullScreenPerspective then
|
||||
uimgr:LoadPerspective(beforeFullScreenPerspective)
|
||||
beforeFullScreenPerspective = nil
|
||||
end
|
||||
|
||||
uimgr:GetPane("toolBar"):Show(not setFullScreen)
|
||||
uimgr:Update()
|
||||
frame:ShowFullScreen(setFullScreen)
|
||||
end
|
||||
|
||||
function CloseWindow(event)
|
||||
exitingProgram = true -- don't handle focus events
|
||||
|
||||
@@ -479,6 +493,7 @@ function CloseWindow(event)
|
||||
return
|
||||
end
|
||||
|
||||
ShowFullScreen(false)
|
||||
SettingsSaveProjectSession(FileTreeGetProjects())
|
||||
SettingsSaveFileSession(GetOpenFiles())
|
||||
SettingsSaveView()
|
||||
|
||||
@@ -97,8 +97,11 @@ debugger.listen = function()
|
||||
local options = debugger.options or {}
|
||||
local wxfilepath = GetEditorFileAndCurInfo()
|
||||
local startfile = options.startfile or wxfilepath:GetFullPath()
|
||||
local basedir = options.basedir or wxfilepath:GetPath(wx.wxPATH_GET_VOLUME)
|
||||
debugger.basedir = basedir
|
||||
local basedir = options.basedir
|
||||
or FileTreeGetDir()
|
||||
or wxfilepath:GetPath(wx.wxPATH_GET_VOLUME + wx.wxPATH_GET_SEPARATOR)
|
||||
-- guarantee that the path has a trailing separator
|
||||
debugger.basedir = wx.wxFileName.DirName(basedir):GetFullPath()
|
||||
debugger.server = copas.wrap(skt)
|
||||
debugger.socket = skt
|
||||
debugger.loop = false
|
||||
@@ -124,7 +127,8 @@ debugger.listen = function()
|
||||
end
|
||||
|
||||
if (options.run) then
|
||||
activateDocument(debugger.handle("run"))
|
||||
local file, line = debugger.handle("run")
|
||||
activateDocument(file, line)
|
||||
else
|
||||
local file, line = debugger.handle("load " .. startfile)
|
||||
-- "load" can work in two ways: (1) it can load the requested file
|
||||
@@ -139,23 +143,23 @@ debugger.listen = function()
|
||||
else
|
||||
-- try to find a proper file based on file name
|
||||
-- first check using basedir that was set based on current file path
|
||||
-- if not found, check using project directory and reset basedir
|
||||
if not activated then
|
||||
local fullPath = debugger.basedir..string_Pathsep..file
|
||||
activated = activateDocument(fullPath, line)
|
||||
activated = activateDocument(debugger.basedir..file, line)
|
||||
end
|
||||
|
||||
local projectDir = FileTreeGetDir()
|
||||
if not activated and projectDir then
|
||||
debugger.basedir = projectDir:gsub(string_Pathsep .. "$", "")
|
||||
debugger.handle("basedir " .. debugger.basedir)
|
||||
fullPath = projectDir .. file
|
||||
activated = activateDocument(fullPath, line)
|
||||
-- if not found, check using full file path and reset basedir
|
||||
if not activated then
|
||||
local path = wxfilepath:GetPath(wx.wxPATH_GET_VOLUME + wx.wxPATH_GET_SEPARATOR)
|
||||
activated = activateDocument(path..file, line)
|
||||
if activated then
|
||||
debugger.basedir = path
|
||||
debugger.handle("basedir " .. debugger.basedir)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if not activated then
|
||||
DisplayOutput("Can't find file '" .. file .. "' to activate for debugging; try opening the file before debugging.\n")
|
||||
DisplayOutput("Can't find file '" .. file .. "' to activate for debugging; open the file before debugging.\n")
|
||||
return debugger.terminate()
|
||||
end
|
||||
else
|
||||
@@ -193,16 +197,19 @@ end
|
||||
|
||||
debugger.exec = function(command)
|
||||
if debugger.server and not debugger.running then
|
||||
|
||||
copas.addthread(function ()
|
||||
local out
|
||||
while true do
|
||||
local file, line, err = debugger.handle(command)
|
||||
local file, line, err = debugger.handle(out or command)
|
||||
if out then out = nil end
|
||||
if line == nil then
|
||||
if err then DisplayOutput(err .. "\n") end
|
||||
DebuggerStop()
|
||||
return
|
||||
else
|
||||
if debugger.basedir and not wx.wxIsAbsolutePath(file) then
|
||||
file = debugger.basedir .. "/" .. file
|
||||
file = debugger.basedir .. file
|
||||
end
|
||||
if activateDocument(file, line) then
|
||||
if debugger.loop then
|
||||
@@ -212,7 +219,7 @@ debugger.exec = function(command)
|
||||
return
|
||||
end
|
||||
else
|
||||
command = "out" -- redo now trying to get out of this file
|
||||
out = "out" -- redo now trying to get out of this file
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -289,9 +296,9 @@ function DebuggerKillClient()
|
||||
-- (at least on Windows Vista SP2)
|
||||
local ret = wx.wxProcess.Kill(debugger.pid, wx.wxSIGKILL, wx.wxKILL_CHILDREN)
|
||||
if ret == wx.wxKILL_OK then
|
||||
DisplayOutput("Stopped debuggee process (pid: "..debugger.pid..").\n")
|
||||
DisplayOutput("Stopped process (pid: "..debugger.pid..").\n")
|
||||
elseif ret ~= wx.wxKILL_NO_PROCESS then
|
||||
DisplayOutput("Unable to kill debuggee process (pid: "..debugger.pid.."), code "..tostring(ret)..".\n")
|
||||
DisplayOutput("Unable to stop process (pid: "..debugger.pid.."), code "..tostring(ret)..".\n")
|
||||
end
|
||||
debugger.pid = nil
|
||||
end
|
||||
@@ -375,7 +382,7 @@ function DebuggerCreateWatchWindow()
|
||||
return -1
|
||||
end
|
||||
|
||||
watchWindow:Connect( wx.wxEVT_CLOSE_WINDOW,
|
||||
watchWindow:Connect(wx.wxEVT_CLOSE_WINDOW,
|
||||
function (event)
|
||||
DebuggerCloseWatchWindow()
|
||||
watchWindow = nil
|
||||
|
||||
@@ -118,10 +118,10 @@ end
|
||||
-- ----------------------------------------------------------------------------
|
||||
-- Get/Set notebook editor page, use nil for current page, returns nil if none
|
||||
function GetEditor(selection)
|
||||
local editor = nil
|
||||
if selection == nil then
|
||||
selection = notebook:GetSelection()
|
||||
end
|
||||
local editor
|
||||
if (selection >= 0) and (selection < notebook:GetPageCount()) and (notebook:GetPage(selection):GetClassInfo():GetClassName()=="wxStyledTextCtrl") then
|
||||
editor = notebook:GetPage(selection):DynamicCast("wxStyledTextCtrl")
|
||||
end
|
||||
@@ -214,10 +214,8 @@ function CreateEditor(name)
|
||||
|
||||
if (ide.config.editor.usewrap) then
|
||||
editor:SetWrapMode(wxstc.wxSTC_WRAP_WORD)
|
||||
editor:SetWrapStartIndent(2)
|
||||
editor:SetWrapStartIndent(0)
|
||||
editor:SetWrapVisualFlagsLocation(wxstc.wxSTC_WRAPVISUALFLAGLOC_END_BY_TEXT)
|
||||
editor:SetWrapVisualFlags(wxstc.wxSTC_WRAPVISUALFLAG_START)
|
||||
editor:WrapCount(80)
|
||||
end
|
||||
|
||||
editor:SetCaretLineVisible(ide.config.editor.caretline and 1 or 0)
|
||||
@@ -379,10 +377,22 @@ function CreateEditor(name)
|
||||
for e,iv in ipairs(editor.ev) do
|
||||
local line = editor:LineFromPosition(iv[1])
|
||||
IndicateFunctions(editor,line,line+iv[2])
|
||||
if MarkupStyle then MarkupStyle(editor,line,line+iv[2]) end
|
||||
end
|
||||
editor.ev = {}
|
||||
end)
|
||||
|
||||
editor:Connect(wx.wxEVT_LEFT_DOWN,
|
||||
function (event)
|
||||
if MarkupHotspotClick then
|
||||
local position = editor:PositionFromPointClose(event:GetX(),event:GetY())
|
||||
if position ~= wx.wxSTC_INVALID_POSITION then
|
||||
if MarkupHotspotClick(position, editor) then return end
|
||||
end
|
||||
end
|
||||
event:Skip()
|
||||
end)
|
||||
|
||||
editor:Connect(wx.wxEVT_SET_FOCUS,
|
||||
function (event)
|
||||
event:Skip()
|
||||
@@ -392,13 +402,6 @@ function CreateEditor(name)
|
||||
ide.in_evt_focus = false
|
||||
end)
|
||||
|
||||
--[[
|
||||
editor:Connect(wxstc.wxEVT_STC_POSCHANGED,
|
||||
function (event)
|
||||
-- brace checking
|
||||
|
||||
end)
|
||||
]]
|
||||
if notebook:AddPage(editor, name, true) then
|
||||
local id = editor:GetId()
|
||||
local document = {}
|
||||
@@ -441,8 +444,6 @@ end
|
||||
function IndicateFunctions(editor, lines, linee)
|
||||
if (not (edcfg.showfncall and editor.spec and editor.spec.isfncall)) then return end
|
||||
|
||||
--DisplayOutput("indicate: "..tostring(lines).." "..tostring(linee).."\n")
|
||||
|
||||
local es = editor:GetEndStyled()
|
||||
local lines = lines or 0
|
||||
local linee = linee or editor:GetLineCount()-1
|
||||
@@ -480,15 +481,8 @@ function IndicateFunctions(editor, lines, linee)
|
||||
if (f) then
|
||||
local p = ls+f+off
|
||||
local s = bit.band(editor:GetStyleAt(p),31)
|
||||
if (not (isinvalid[s])) then
|
||||
|
||||
editor:StartStyling(p,INDICS_MASK)
|
||||
editor:SetStyling(t-f,INDIC0_MASK + 1)
|
||||
else
|
||||
editor:StartStyling(p,INDICS_MASK)
|
||||
editor:SetStyling(t-f,0)
|
||||
end
|
||||
|
||||
editor:StartStyling(p,INDICS_MASK)
|
||||
editor:SetStyling(t-f,isinvalid[s] and 0 or (INDIC0_MASK + 1))
|
||||
off = off + t
|
||||
end
|
||||
from = t and (t+1)
|
||||
@@ -500,7 +494,6 @@ end
|
||||
function SetupKeywords(editor, ext, forcespec, styles, font, fontitalic)
|
||||
local lexerstyleconvert = nil
|
||||
local spec = forcespec or GetSpec(ext)
|
||||
--print(ext..":"..tostring(spec.apitype))
|
||||
-- found a spec setup lexers and keywords
|
||||
if spec then
|
||||
editor:SetLexer(spec.lexer or wxstc.wxSTC_LEX_NULL)
|
||||
|
||||
@@ -287,8 +287,8 @@ projpanel.projcombobox = projcombobox
|
||||
projpanel.projtree = projtree
|
||||
|
||||
function FileTreeGetDir()
|
||||
-- atm only projtree
|
||||
return projpanel:IsShown() and (filetree.newfiledir .. string_Pathsep)
|
||||
return projpanel:IsShown() and filetree.newfiledir
|
||||
and wx.wxFileName.DirName(filetree.newfiledir):GetFullPath()
|
||||
end
|
||||
|
||||
function FileTreeSetProjects(tab)
|
||||
@@ -306,7 +306,7 @@ local function findItem(tree, match)
|
||||
local node = projtree:GetRootItem()
|
||||
local label = tree:GetItemText(node)
|
||||
|
||||
local s, e = string.find(match, label .. string_Pathsep)
|
||||
local s, e = string.find(match, label)
|
||||
if not s or s ~= 1 then return end
|
||||
|
||||
for token in string.gmatch(string.sub(match,e+1), "[^%"..string_Pathsep.."]+") do
|
||||
|
||||
@@ -44,7 +44,10 @@ ide.ofontItalic = ofontItalic
|
||||
local function createFrame()
|
||||
frame = wx.wxFrame(wx.NULL, wx.wxID_ANY, GetIDEString("editor"),
|
||||
wx.wxDefaultPosition, wx.wxSize(1000, 700))
|
||||
frame:DragAcceptFiles(true)
|
||||
-- wrap into protected call as DragAcceptFiles fails on MacOS with
|
||||
-- wxwidgets 2.8.12 even though it should work according to change notes
|
||||
-- for 2.8.10: "Implemented wxWindow::DragAcceptFiles() on all platforms."
|
||||
pcall(function() frame:DragAcceptFiles(true) end)
|
||||
frame:Connect(wx.wxEVT_DROP_FILES,function(evt)
|
||||
local files = evt:GetFiles()
|
||||
if not files or #files == 0 then return end
|
||||
|
||||
153
src/editor/markup.lua
Normal file
153
src/editor/markup.lua
Normal file
@@ -0,0 +1,153 @@
|
||||
-- Copyright (C) Paul Kulchenko 2011-2012
|
||||
-- update styles for comment markup
|
||||
local styles = ide.config.styles
|
||||
local comment = styles.comment
|
||||
local MD_MARK_ITAL = '_' -- italic
|
||||
local MD_MARK_BOLD = '*' -- bold
|
||||
local MD_MARK_LINK = '~' -- link
|
||||
local MD_MARK_HEAD = '!' -- header
|
||||
local MD_MARK_CODE = '@' -- code
|
||||
local MD_MARK_BOXD = '|' -- highlight
|
||||
local MD_MARK_LSEP = ';' -- link separator (between text and link)
|
||||
local MD_MARK_MARK = ' ' -- separator
|
||||
local MD_LINK_NEWWINDOW = '+' -- indicator to open a new window for links
|
||||
local markup = {
|
||||
[MD_MARK_BOXD] = {st=25, fg={127,0,127}, bg=comment.bg, b=true},
|
||||
[MD_MARK_CODE] = {st=26, fg={127,127,127}, bg=comment.bg, fs=9},
|
||||
[MD_MARK_HEAD] = {st=27, fg=comment.fg, bg=comment.bg, fn="Lucida Console", b=true},
|
||||
[MD_MARK_LINK] = {st=28, fg=comment.fg, bg=comment.bg, u=true, hs={0, 0, 127}},
|
||||
[MD_MARK_BOLD] = {st=29, fg=comment.fg, bg=comment.bg, b=true, fs=11},
|
||||
[MD_MARK_ITAL] = {st=30, fg=comment.fg, bg=comment.bg, i=true},
|
||||
[MD_MARK_MARK] = {st=31, fg=comment.fg, bg=comment.bg, v=false},
|
||||
}
|
||||
|
||||
local MD_MARK_PTRN = '' -- combination of all markup marks that can start styling
|
||||
for key,value in pairs(markup) do
|
||||
styles[key] = value
|
||||
if key ~= MD_MARK_MARK then MD_MARK_PTRN = MD_MARK_PTRN .. "%" .. key end
|
||||
end
|
||||
|
||||
function MarkupHotspotClick(pos, editor)
|
||||
-- check if this is "our" hotspot event
|
||||
if bit.band(editor:GetStyleAt(pos),31) ~= markup[MD_MARK_LINK].st then
|
||||
-- not "our" style, so nothing to do for us here
|
||||
return
|
||||
end
|
||||
local line = editor:LineFromPosition(pos)
|
||||
local tx = editor:GetLine(line)
|
||||
pos = pos + 1 - editor:PositionFromLine(line) -- turn into relative position
|
||||
|
||||
-- find the separator on the right side of the position
|
||||
local poss = string.find(tx, MD_MARK_LSEP, pos, true)
|
||||
local pose = string.find(tx, MD_MARK_LINK, pos, true)
|
||||
|
||||
if (poss and pose) then
|
||||
local text = string.sub(tx, poss+1, pose-1)
|
||||
local filepath = ide.openDocuments[editor:GetId()].filePath
|
||||
local _,_,shell = string.find(text, [[^macro:shell%((.*%S)%)$]])
|
||||
local _,_,http = string.find(text, [[^(http:%S+)$]])
|
||||
local _,_,command = string.find(text, [[^macro:(%w+)$]])
|
||||
local bottomnotebook = ide.frame.bottomnotebook
|
||||
if shell then
|
||||
ShellExecuteCode(shell)
|
||||
elseif command == 'run' then -- run the current file
|
||||
ProjectRun()
|
||||
elseif command == 'debug' then -- debug the current file
|
||||
ProjectDebug()
|
||||
elseif http then -- open the URL in a new browser window
|
||||
wx.wxLaunchDefaultBrowser(http, 0)
|
||||
else
|
||||
-- check if requested to open in a new window
|
||||
local newwindow = string.find(text, MD_LINK_NEWWINDOW, 1, true) -- plain search
|
||||
if newwindow then text = string.gsub(text, "^%" .. MD_LINK_NEWWINDOW, "") end
|
||||
local name = wx.wxFileName(filepath):GetPath(wx.wxPATH_GET_VOLUME
|
||||
+ wx.wxPATH_GET_SEPARATOR) .. text
|
||||
-- load/activate file
|
||||
local filename = wx.wxFileName(name)
|
||||
filename:Normalize() -- remove .., ., and other similar elements
|
||||
if filename:FileExists() and
|
||||
(newindow or SaveModifiedDialog(editor, true) ~= wx.wxID_CANCEL) then
|
||||
LoadFile(filename,not newwindow and editor or nil,true)
|
||||
end
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
local function ismarkup (tx)
|
||||
local start = 1
|
||||
while true do
|
||||
-- find a separator first
|
||||
local st,_,sep = string.find(tx, "(["..MD_MARK_PTRN.."])", start)
|
||||
if not st then return end
|
||||
|
||||
local s,e,cap
|
||||
if sep == MD_MARK_HEAD then
|
||||
s,e,cap = string.find(tx,"^(%"..MD_MARK_HEAD..".+[%w%p])", start)
|
||||
elseif sep == MD_MARK_LINK then
|
||||
local any = "[^%"..MD_MARK_LINK.."%"..MD_MARK_LSEP.."]-"
|
||||
s,e,cap = string.find(tx,"^(%"..MD_MARK_LINK.."[%w%p]"..any.."[%w%p]"..
|
||||
"%"..MD_MARK_LSEP.."[%w%p]"..any.."[%w%p]"..
|
||||
"%"..MD_MARK_LINK..")", st)
|
||||
else
|
||||
s,e,cap = string.find(tx,"^(%"..sep.."[%w%p][^%"..sep.."]-[%w%p]%"..sep..")", st)
|
||||
if not s then s,e,cap = string.find(tx,"^(%"..sep.."[^%s%"..sep.."]%"..sep..")", st) end
|
||||
end
|
||||
if s then return s,e,cap,sep end
|
||||
start = st+1
|
||||
end
|
||||
end
|
||||
|
||||
function MarkupStyle(editor, lines, linee)
|
||||
local lines = lines or 0
|
||||
if (lines < 0) then return end
|
||||
|
||||
-- always style to the end as there may be comments that need re-styling
|
||||
-- technically, this should be GetLineCount()-1, but we want to style
|
||||
-- beyond the last line to make sure it is styled correctly
|
||||
local linee = linee or editor:GetLineCount()-1
|
||||
local linef = editor:GetLineCount()
|
||||
|
||||
local iscomment = {}
|
||||
for i,v in pairs(editor.spec.iscomment) do
|
||||
iscomment[i] = v
|
||||
end
|
||||
|
||||
for line=lines,linef do
|
||||
local tx = editor:GetLine(line)
|
||||
local ls = editor:PositionFromLine(line)
|
||||
|
||||
editor:StartStyling(ls, 0)
|
||||
|
||||
local from = 1
|
||||
local off = -1
|
||||
|
||||
while from do
|
||||
tx = string.sub(tx,from)
|
||||
local f,t,w,mark = ismarkup(tx)
|
||||
|
||||
if (f) then
|
||||
local p = ls+f+off
|
||||
local s = bit.band(editor:GetStyleAt(p), 31)
|
||||
if iscomment[s] then
|
||||
editor:StartStyling(p, 31)
|
||||
editor:SetStyling(1, markup[MD_MARK_MARK].st)
|
||||
local endmark = 1
|
||||
if mark == MD_MARK_HEAD then
|
||||
endmark = 0
|
||||
elseif mark == MD_MARK_LINK then
|
||||
local pipe = w:find(MD_MARK_LSEP)
|
||||
if pipe then
|
||||
endmark = #w-pipe+1
|
||||
end
|
||||
end
|
||||
editor:SetStyling(t-f-endmark, markup[mark].st or markup[MD_MARK_MARK].st)
|
||||
editor:SetStyling(endmark, markup[MD_MARK_MARK].st)
|
||||
end
|
||||
|
||||
off = off + t
|
||||
end
|
||||
from = t and (t+1)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -99,7 +99,10 @@ frame:Connect(ID_SAVEALL, wx.wxEVT_UPDATE_UI,
|
||||
event:Enable(atLeastOneModifiedDocument)
|
||||
end)
|
||||
|
||||
frame:Connect(ID_CLOSE, wx.wxEVT_COMMAND_MENU_SELECTED, ClosePage)
|
||||
frame:Connect(ID_CLOSE, wx.wxEVT_COMMAND_MENU_SELECTED,
|
||||
function (event)
|
||||
ClosePage() -- this will find the current editor
|
||||
end)
|
||||
frame:Connect(ID_CLOSE, wx.wxEVT_UPDATE_UI,
|
||||
function (event)
|
||||
event:Enable((GetEditor() ~= nil) and (debugger.server == nil))
|
||||
|
||||
@@ -11,6 +11,8 @@ local menuBar = frame.menuBar
|
||||
local openDocuments = ide.openDocuments
|
||||
local debugger = ide.debugger
|
||||
local filetree = ide.filetree
|
||||
local bottomnotebook = frame.bottomnotebook
|
||||
local uimgr = frame.uimgr
|
||||
|
||||
------------------------
|
||||
-- Interpreters and Menu
|
||||
@@ -39,14 +41,13 @@ do
|
||||
targetMenu = wx.wxMenu(targetargs)
|
||||
end
|
||||
|
||||
|
||||
local debugTab = {
|
||||
{ ID_RUN, "&Run\tF6", "Execute the current project/file" },
|
||||
{ ID_COMPILE, "&Compile\tF7", "Test compile the Lua file" },
|
||||
{ ID_START_DEBUG, "Start &Debugging\tF5", "Start a debugging session" },
|
||||
{ ID_ATTACH_DEBUG, "&Start Debugger Server\tShift-F6", "Allow a client to start a debugging session" },
|
||||
{ },
|
||||
{ ID_STOP_DEBUG, "S&top Debugging\tShift-F12", "Stop and end the debugging session" },
|
||||
{ ID_STOP_DEBUG, "S&top Debugging\tShift-F12", "Stop the currently running process" },
|
||||
{ ID_STEP, "St&ep\tF11", "Step into the next line" },
|
||||
{ ID_STEP_OVER, "Step &Over\tF10", "Step over the next line" },
|
||||
{ ID_STEP_OUT, "Step O&ut\tShift-F10", "Step out of the current function" },
|
||||
@@ -54,13 +55,14 @@ local debugTab = {
|
||||
{ ID_BREAK, "&Break", "Stop execution of the program at the next executed line of code" },
|
||||
{ },
|
||||
{ ID_TOGGLEBREAKPOINT, "Toggle &Breakpoint\tF9", "Toggle Breakpoint" },
|
||||
--{ ID "view.debug.callstack", "V&iew Call Stack", "View the LUA call stack" },
|
||||
--{ ID "view.debug.callstack", "V&iew Call Stack", "View the call stack" },
|
||||
{ },
|
||||
{ ID_CLEAROUTPUT, "C&lear Output Window", "Clear the output window before compiling or debugging", wx.wxITEM_CHECK },
|
||||
}
|
||||
|
||||
local debugMenu = wx.wxMenu(debugTab)
|
||||
local debugMenuRun = {start="Start &Debugging\tF5", continue="Co&ntinue\tF5"}
|
||||
local debugMenuStop = {debugging="S&top Debugging\tShift-F12", process="S&top Process\tShift-F12"}
|
||||
|
||||
local targetDirMenu = wx.wxMenu{
|
||||
{ID "debug.projectdir.choose","Choose ..."},
|
||||
@@ -177,11 +179,40 @@ local function getNameToRun()
|
||||
return wx.wxFileName(openDocuments[id].filePath)
|
||||
end
|
||||
|
||||
local function activateOutput()
|
||||
if not ide.config.activateoutput then return end
|
||||
-- show output/errorlog pane
|
||||
uimgr:GetPane("bottomnotebook"):Show(true)
|
||||
uimgr:Update()
|
||||
-- activate output/errorlog window
|
||||
local index = bottomnotebook:GetPageIndex(bottomnotebook.errorlog)
|
||||
if bottomnotebook:GetSelection() ~= index then
|
||||
bottomnotebook:SetSelection(index)
|
||||
end
|
||||
end
|
||||
|
||||
local function runInterpreter(wfilename, withdebugger)
|
||||
activateOutput()
|
||||
|
||||
ClearAllCurrentLineMarkers()
|
||||
if not wfilename then return end
|
||||
local pid = ide.interpreter:frun(wfilename, withdebugger)
|
||||
if withdebugger then debugger.pid = pid end
|
||||
debugger.pid = pid
|
||||
end
|
||||
|
||||
function ProjectRun()
|
||||
runInterpreter(getNameToRun())
|
||||
end
|
||||
|
||||
function ProjectDebug()
|
||||
if (debugger.server ~= nil) then
|
||||
if (not debugger.running) then
|
||||
ClearAllCurrentLineMarkers()
|
||||
debugger.run()
|
||||
end
|
||||
else
|
||||
runInterpreter(getNameToRun(), true)
|
||||
end
|
||||
end
|
||||
|
||||
-----------------------
|
||||
@@ -202,22 +233,20 @@ frame:Connect(ID_TOGGLEBREAKPOINT, wx.wxEVT_UPDATE_UI,
|
||||
frame:Connect(ID_COMPILE, wx.wxEVT_COMMAND_MENU_SELECTED,
|
||||
function (event)
|
||||
local editor = GetEditor()
|
||||
activateOutput()
|
||||
CompileProgram(editor)
|
||||
end)
|
||||
frame:Connect(ID_COMPILE, wx.wxEVT_UPDATE_UI,
|
||||
function (event)
|
||||
local editor = GetEditor()
|
||||
event:Enable((debugger.server == nil) and (editor ~= nil))
|
||||
event:Enable((debugger.server == nil and debugger.pid == nil) and (editor ~= nil))
|
||||
end)
|
||||
|
||||
frame:Connect(ID_RUN, wx.wxEVT_COMMAND_MENU_SELECTED,
|
||||
function (event)
|
||||
runInterpreter(getNameToRun())
|
||||
end)
|
||||
frame:Connect(ID_RUN, wx.wxEVT_COMMAND_MENU_SELECTED, ProjectRun)
|
||||
frame:Connect(ID_RUN, wx.wxEVT_UPDATE_UI,
|
||||
function (event)
|
||||
local editor = GetEditor()
|
||||
event:Enable((debugger.server == nil) and (editor ~= nil))
|
||||
event:Enable((debugger.server == nil and debugger.pid == nil) and (editor ~= nil))
|
||||
end)
|
||||
|
||||
frame:Connect(ID_ATTACH_DEBUG, wx.wxEVT_COMMAND_MENU_SELECTED,
|
||||
@@ -232,42 +261,34 @@ frame:Connect(ID_ATTACH_DEBUG, wx.wxEVT_UPDATE_UI,
|
||||
(not debugger.listening) and (debugger.server == nil) and (editor ~= nil))
|
||||
end)
|
||||
|
||||
local lastcontinue
|
||||
frame:Connect(ID_START_DEBUG, wx.wxEVT_COMMAND_MENU_SELECTED,
|
||||
function (event)
|
||||
if (debugger.server ~= nil) then
|
||||
if (not debugger.running) then
|
||||
ClearAllCurrentLineMarkers()
|
||||
debugger.run()
|
||||
end
|
||||
else
|
||||
runInterpreter(getNameToRun(), true)
|
||||
end
|
||||
end)
|
||||
frame:Connect(ID_START_DEBUG, wx.wxEVT_COMMAND_MENU_SELECTED, ProjectDebug)
|
||||
frame:Connect(ID_START_DEBUG, wx.wxEVT_UPDATE_UI,
|
||||
function (event)
|
||||
local editor = GetEditor()
|
||||
event:Enable((ide.interpreter) and (ide.interpreter.hasdebugger) and
|
||||
((debugger.server == nil) or (debugger.server ~= nil and not debugger.running)) and (editor ~= nil))
|
||||
local curcontinue = (debugger.server ~= nil)
|
||||
if curcontinue == lastcontinue then return end
|
||||
lastcontinue = curcontinue
|
||||
if curcontinue then
|
||||
debugMenu:SetLabel(ID_START_DEBUG, debugMenuRun.continue)
|
||||
else
|
||||
debugMenu:SetLabel(ID_START_DEBUG, debugMenuRun.start)
|
||||
((debugger.server == nil and debugger.pid == nil) or (debugger.server ~= nil and not debugger.running)) and (editor ~= nil))
|
||||
local label = (debugger.server ~= nil)
|
||||
and debugMenuRun.continue or debugMenuRun.start
|
||||
if debugMenu:GetLabel(ID_START_DEBUG) ~= label then
|
||||
debugMenu:SetLabel(ID_START_DEBUG, label)
|
||||
end
|
||||
end)
|
||||
|
||||
frame:Connect(ID_STOP_DEBUG, wx.wxEVT_COMMAND_MENU_SELECTED,
|
||||
function (event)
|
||||
ClearAllCurrentLineMarkers()
|
||||
debugger.terminate()
|
||||
if debugger.server then debugger.terminate() end
|
||||
if debugger.pid then DebuggerKillClient() end
|
||||
end)
|
||||
frame:Connect(ID_STOP_DEBUG, wx.wxEVT_UPDATE_UI,
|
||||
function (event)
|
||||
local editor = GetEditor()
|
||||
event:Enable((debugger.server ~= nil) and (editor ~= nil))
|
||||
event:Enable((debugger.server ~= nil or debugger.pid ~= nil) and (editor ~= nil))
|
||||
local label = (debugger.server == nil and debugger.pid ~= nil)
|
||||
and debugMenuStop.process or debugMenuStop.debugging
|
||||
if debugMenu:GetLabel(ID_STOP_DEBUG) ~= label then
|
||||
debugMenu:SetLabel(ID_STOP_DEBUG, label)
|
||||
end
|
||||
end)
|
||||
|
||||
frame:Connect(ID_STEP, wx.wxEVT_COMMAND_MENU_SELECTED,
|
||||
|
||||
@@ -11,11 +11,12 @@ local debugger = ide.debugger
|
||||
local viewMenu = wx.wxMenu{
|
||||
-- NYI { ID "view.preferences", "&Preferences...", "Brings up dialog for settings (TODO)" },
|
||||
-- NYI { },
|
||||
{ ID "view.filetree.show", "Project/&FileTree Window", "View the project/filetree window" },
|
||||
{ ID "view.output.show", "&Output/Shell Window", "View the output/shell window" },
|
||||
{ ID "view.filetree.show", "Project/&FileTree Window\tCtrl-Alt-P", "View the project/filetree window" },
|
||||
{ ID "view.output.show", "&Output/Shell Window\tCtrl-Alt-O", "View the output/shell window" },
|
||||
{ ID "view.debug.watches", "&Watch Window", "View the Watch window" },
|
||||
{ },
|
||||
{ ID "view.defaultlayout", "&Default Layout", "Reset to default ui layout"},
|
||||
{ ID "view.defaultlayout", "&Default Layout", "Reset to default layout"},
|
||||
{ ID "view.fullscreen", "Full &Screen\tCtrl-Alt-F", "Switch to or from full screen mode"},
|
||||
{ ID "view.style.loadconfig", "&Load Config Style...", "Load and apply style from config file (must contain .styles)"},
|
||||
}
|
||||
menuBar:Append(viewMenu, "&View")
|
||||
@@ -45,6 +46,9 @@ frame:Connect(ID "view.filetree.show", wx.wxEVT_COMMAND_MENU_SELECTED,
|
||||
uimgr:Update()
|
||||
end)
|
||||
|
||||
frame:Connect(ID "view.fullscreen", wx.wxEVT_COMMAND_MENU_SELECTED,
|
||||
function (event) ShowFullScreen(not frame:IsFullScreen()) end)
|
||||
|
||||
frame:Connect(ID "view.debug.watches", wx.wxEVT_COMMAND_MENU_SELECTED,
|
||||
function (event)
|
||||
if not debugger.watchWindow then
|
||||
|
||||
@@ -26,10 +26,9 @@ out:SetViewWhiteSpace(ide.config.editor.whitespace and true or false)
|
||||
out:SetIndentationGuides(true)
|
||||
|
||||
out:SetWrapMode(wxstc.wxSTC_WRAP_WORD)
|
||||
out:SetWrapStartIndent(2)
|
||||
out:SetWrapStartIndent(0)
|
||||
out:SetWrapVisualFlagsLocation(wxstc.wxSTC_WRAPVISUALFLAGLOC_END_BY_TEXT)
|
||||
out:SetWrapVisualFlags(wxstc.wxSTC_WRAPVISUALFLAG_START)
|
||||
out:WrapCount(80)
|
||||
out:SetWrapVisualFlags(wxstc.wxSTC_WRAPVISUALFLAG_END)
|
||||
|
||||
out:MarkerDefine(CURRENT_LINE_MARKER, wxstc.wxSTC_MARK_CHARACTER+string.byte('>'), wx.wxBLACK, wx.wxColour(240, 240, 240))
|
||||
out:MarkerDefine(BREAKPOINT_MARKER, wxstc.wxSTC_MARK_BACKGROUND, wx.wxBLACK, wx.wxColour(255, 220, 220))
|
||||
@@ -137,6 +136,7 @@ local function shellPrint(marker, ...)
|
||||
|
||||
out:EmptyUndoBuffer() -- don't allow the user to undo shell text
|
||||
out:GotoPos(out:GetLength())
|
||||
out:EnsureVisibleEnforcePolicy(out:GetLineCount()-1)
|
||||
end
|
||||
|
||||
DisplayShell = function (...)
|
||||
@@ -238,7 +238,6 @@ local env = createenv()
|
||||
local function executeShellCode(tx)
|
||||
if tx == nil or tx == '' then return end
|
||||
|
||||
DisplayShellDirect('\n')
|
||||
DisplayShellPrompt('')
|
||||
|
||||
local addedret = false
|
||||
@@ -260,10 +259,22 @@ local function executeShellCode(tx)
|
||||
DisplayShellErr(err)
|
||||
elseif fn then
|
||||
setfenv(fn,env)
|
||||
|
||||
-- set the project dir as the current dir to allow "require" calls
|
||||
-- to work from shell
|
||||
local projectDir, cwd = FileTreeGetDir()
|
||||
if projectDir then
|
||||
cwd = wx.wxFileName.GetCwd()
|
||||
wx.wxFileName.SetCwd(projectDir)
|
||||
end
|
||||
|
||||
local ok, res = xpcall(fn,
|
||||
function(err)
|
||||
DisplayShellErr(filterTraceError(debug.traceback(err), addedret))
|
||||
end)
|
||||
|
||||
-- restore the current dir
|
||||
if projectDir then wx.wxFileName.SetCwd(cwd) end
|
||||
|
||||
if ok and (addedret or res ~= nil) then DisplayShell(res) end
|
||||
end
|
||||
@@ -272,16 +283,27 @@ end
|
||||
function ShellSupportRemote(client,uid)
|
||||
remotesend = client
|
||||
|
||||
-- change the name of the tab: console is the second page in the notebook
|
||||
bottomnotebook:SetPageText(1,
|
||||
client and "Remote console" or "Local console")
|
||||
local index = bottomnotebook:GetPageIndex(out)
|
||||
if index then
|
||||
bottomnotebook:SetPageText(index,
|
||||
client and "Remote console" or "Local console")
|
||||
end
|
||||
end
|
||||
|
||||
function ShellExecuteCode(wfilename)
|
||||
function ShellExecuteFile(wfilename)
|
||||
if (not wfilename) then return end
|
||||
local cmd = 'dofile([['..wfilename:GetFullPath()..']])'
|
||||
DisplayShellDirect(cmd)
|
||||
executeShellCode(cmd)
|
||||
ShellExecuteCode(cmd)
|
||||
end
|
||||
|
||||
function ShellExecuteCode(code)
|
||||
local index = bottomnotebook:GetPageIndex(bottomnotebook.shellbox)
|
||||
if ide.config.activateoutput and bottomnotebook:GetSelection() ~= index then
|
||||
bottomnotebook:SetSelection(index)
|
||||
end
|
||||
|
||||
DisplayShellDirect(code)
|
||||
executeShellCode(code)
|
||||
end
|
||||
|
||||
local function displayShellIntro()
|
||||
@@ -350,6 +372,7 @@ out:Connect(wx.wxEVT_KEY_DOWN,
|
||||
out:ClearAll()
|
||||
displayShellIntro()
|
||||
else
|
||||
DisplayShellDirect('\n')
|
||||
executeShellCode(promptText)
|
||||
end
|
||||
currentHistory = getPromptLine() -- reset history
|
||||
|
||||
@@ -11,6 +11,10 @@
|
||||
-- b bold - boolean
|
||||
-- i italic - boolean
|
||||
-- fill fill to end - boolean
|
||||
-- fn font Face Name - string ("Lucida Console")
|
||||
-- fx font size - number (11)
|
||||
-- hs turn hotspot on - true or {r,g,b} 0-255
|
||||
-- v visibility for symbols of the current style - boolean
|
||||
|
||||
function StylesGetDefault()
|
||||
return {
|
||||
@@ -148,6 +152,21 @@ function StylesApplyToEditor(styles,editor,font,fontitalic,lexerconvert)
|
||||
editor:StyleSetUnderline(id, style.u or false)
|
||||
editor:StyleSetEOLFilled(id, style.fill or false)
|
||||
|
||||
if style.fn then editor:StyleSetFaceName(id, style.fn) end
|
||||
if style.fs then editor:StyleSetSize(id, style.fs) end
|
||||
if style.v ~= nil then editor:StyleSetVisible(id, style.v) end
|
||||
|
||||
if style.hs then
|
||||
editor:StyleSetHotSpot(id, 1)
|
||||
-- if passed a color (table) as value, set it as foreground
|
||||
if type(style.hs) == 'table' then
|
||||
local color = wx.wxColour(unpack(style.hs))
|
||||
editor:SetHotspotActiveForeground(1, color)
|
||||
end
|
||||
editor:SetHotspotActiveUnderline(1)
|
||||
editor:SetHotspotSingleLine(1)
|
||||
end
|
||||
|
||||
if (style.fg or defaultfg) then
|
||||
editor:StyleSetForeground(id, style.fg and wx.wxColour(unpack(style.fg)) or defaultfg)
|
||||
end
|
||||
@@ -179,6 +198,10 @@ function StylesApplyToEditor(styles,editor,font,fontitalic,lexerconvert)
|
||||
for n,outid in pairs(targets) do
|
||||
applystyle(style,outid)
|
||||
end
|
||||
-- allow to specify style numbers, but exclude those styles
|
||||
-- that may conflict with indicator numbers
|
||||
elseif (style.st and style.st > 8 and style.st < wxstc.wxSTC_STYLE_DEFAULT) then
|
||||
applystyle(style,style.st)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -222,7 +245,7 @@ function LoadConfigStyle()
|
||||
|
||||
if not (cfgfn and (cfg.styles or cfg.stylesoutshell)) then
|
||||
wx.wxMessageBox("Unable to load config style '"..fileDialog:GetPath().."'.",
|
||||
"wxLua Error",
|
||||
"Error",
|
||||
wx.wxOK + wx.wxCENTRE, ide.frame)
|
||||
else
|
||||
if (cfg.styles) then
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
package.cpath = package.cpath..';bin/?.dll;bin/clibs/?.dll;bin/clibs/?/?.dll;bin/clibs/?/?/?.dll'
|
||||
package.cpath = package.cpath..';bin/?.so;bin/clibs/?.so;bin/clibs/?/?.so;bin/clibs/?/?/?.so'
|
||||
package.path = package.path..'lualibs/?.lua;lualibs/?/?.lua;lualibs/?/init.lua;lualibs/?/?/?.lua;lualibs/?/?/init.lua'
|
||||
package.path = package.path..';lualibs/?.lua;lualibs/?/?.lua;lualibs/?/init.lua;lualibs/?/?/?.lua;lualibs/?/?/init.lua'
|
||||
|
||||
require("wx")
|
||||
require("bit")
|
||||
@@ -43,6 +43,7 @@ ide = {
|
||||
strategy = 2,
|
||||
},
|
||||
|
||||
activateoutput = false, -- activate output/console on Run/Debug/Compile
|
||||
filehistorylength = 20,
|
||||
projecthistorylength = 15,
|
||||
savebak = false,
|
||||
|
||||
@@ -173,3 +173,17 @@ function FileSysGet(dir,spec)
|
||||
end
|
||||
return content
|
||||
end
|
||||
|
||||
function pairsSorted(t, f)
|
||||
local a = {}
|
||||
for n in pairs(t) do table.insert(a, n) end
|
||||
table.sort(a, f)
|
||||
local i = 0 -- iterator variable
|
||||
local iter = function () -- iterator function
|
||||
i = i + 1
|
||||
if a[i] == nil then return nil
|
||||
else return a[i], t[a[i]]
|
||||
end
|
||||
end
|
||||
return iter
|
||||
end
|
||||
|
||||
14
tools/dx.lua
14
tools/dx.lua
@@ -15,6 +15,7 @@ return dxpath and {
|
||||
{ },
|
||||
{ ID "dx.compile.input", "&Custom Args", "when set a popup for custom compiler args will be envoked", wx.wxITEM_CHECK },
|
||||
{ ID "dx.compile.legacy", "&Legacy", "when set compiles in legacy mode", wx.wxITEM_CHECK },
|
||||
{ ID "dx.compile.backwards", "&Backwards Compatibility", "when set compiles in backwards compatibility mode", wx.wxITEM_CHECK },
|
||||
{ },
|
||||
{ ID "dx.compile.vertex", "Compile &Vertex", "Compile Vertex shader (select entry word)" },
|
||||
{ ID "dx.compile.fragment", "Compile &Fragment", "Compile pixel shader (select entry word)" },
|
||||
@@ -26,7 +27,9 @@ return dxpath and {
|
||||
|
||||
local data = {}
|
||||
data.customarg = false
|
||||
data.custom = ""
|
||||
data.legacy = false
|
||||
data.backwards = false
|
||||
data.profid = ID ("dx.profile."..dxprofile)
|
||||
data.domains = {
|
||||
[ID "dx.compile.vertex"] = 1,
|
||||
@@ -77,6 +80,10 @@ return dxpath and {
|
||||
function(event)
|
||||
data.legacy = event:IsChecked()
|
||||
end)
|
||||
frame:Connect(ID "dx.compile.backwards",wx.wxEVT_COMMAND_MENU_SELECTED,
|
||||
function(event)
|
||||
data.backwards = event:IsChecked()
|
||||
end)
|
||||
-- Compile
|
||||
local function evCompile(event)
|
||||
local filename,info = GetEditorFileAndCurInfo()
|
||||
@@ -92,19 +99,20 @@ return dxpath and {
|
||||
if (not profile[domain]) then return end
|
||||
|
||||
-- popup for custom input
|
||||
local args = data.customarg and wx.wxGetTextFromUser("Compiler Args") or ""
|
||||
args = args:len() > 0 and args or nil
|
||||
data.custom = data.customarg and wx.wxGetTextFromUser("Compiler Args","Dx",data.custom) or data.custom
|
||||
local args = data.custom:len() > 0 and data.custom or nil
|
||||
|
||||
local fullname = filename:GetFullPath()
|
||||
|
||||
local outname = fullname.."."..info.selword.."^"
|
||||
outname = args and outname..args:gsub("%s+%-",";-")..";^" or outname
|
||||
outname = args and outname..args:gsub("%s*[%-%/]",";-")..";^" or outname
|
||||
outname = outname..profile[domain]..profile.ext
|
||||
outname = '"'..outname..'"'
|
||||
|
||||
local cmdline = " /T "..profile[domain].." "
|
||||
cmdline = cmdline..(args and args.." " or "")
|
||||
cmdline = cmdline..(data.legacy and "/LD " or "")
|
||||
cmdline = cmdline..(data.backwards and "/Gec " or "")
|
||||
cmdline = cmdline..data.domaindefs[domain]
|
||||
cmdline = cmdline.."/Fc "..outname.." "
|
||||
cmdline = cmdline.."/E "..info.selword.." "
|
||||
|
||||
@@ -42,6 +42,25 @@ cfg/user.lua_for_custom_settings.txt
|
||||
interpreters/luadeb.lua
|
||||
lualibs/copas/copas.lua
|
||||
lualibs/coxpcall/coxpcall.lua
|
||||
lualibs/metalua/compile.lua
|
||||
lualibs/metalua/gg.lua
|
||||
lualibs/metalua/lcode.lua
|
||||
lualibs/metalua/ldump.lua
|
||||
lualibs/metalua/lexer.lua
|
||||
lualibs/metalua/lopcodes.lua
|
||||
lualibs/metalua/metalua
|
||||
lualibs/metalua/metalua.lua
|
||||
lualibs/metalua/mlp_expr.lua
|
||||
lualibs/metalua/mlp_ext.lua
|
||||
lualibs/metalua/mlp_lexer.lua
|
||||
lualibs/metalua/mlp_meta.lua
|
||||
lualibs/metalua/mlp_misc.lua
|
||||
lualibs/metalua/mlp_stat.lua
|
||||
lualibs/metalua/mlp_table.lua
|
||||
lualibs/metalua/metalua/base.lua
|
||||
lualibs/metalua/metalua/runtime.lua
|
||||
lualibs/metalua/metalua/string2.lua
|
||||
lualibs/metalua/metalua/table2.lua
|
||||
lualibs/mobdebug/mobdebug.lua
|
||||
lualibs/socket/ltn12.lua
|
||||
lualibs/socket/mime.lua
|
||||
@@ -62,6 +81,7 @@ src/editor/findreplace.lua
|
||||
src/editor/gui.lua
|
||||
src/editor/ids.lua
|
||||
src/editor/iofilters.lua
|
||||
src/editor/markup.lua
|
||||
src/editor/menu.lua
|
||||
src/editor/menu_edit.lua
|
||||
src/editor/menu_file.lua
|
||||
|
||||
@@ -26,6 +26,9 @@ local app = {
|
||||
wx.wxArtProvider.Push(artProvider)
|
||||
|
||||
ide.config.interpreter = "luadeb";
|
||||
|
||||
-- this needs to be in pre-init to load the styles
|
||||
dofile("src/editor/markup.lua")
|
||||
end,
|
||||
|
||||
postinit = function ()
|
||||
@@ -54,6 +57,16 @@ local app = {
|
||||
if itemid ~= wx.wxNOT_FOUND then menu:Destroy(itemid) end
|
||||
|
||||
menuBar:Check(ID_CLEAROUTPUT, true)
|
||||
|
||||
-- load welcome.lua from myprograms/ if exists
|
||||
local fn = wx.wxFileName("myprograms/welcome.lua")
|
||||
if fn:FileExists() and
|
||||
(not ide.config.path.projectdir
|
||||
or string.len(ide.config.path.projectdir) == 0) then
|
||||
fn:Normalize() -- make absolute path
|
||||
LoadFile(fn:GetFullPath(),nil,true)
|
||||
ProjectUpdateProjectDir(fn:GetPath(wx.wxPATH_GET_VOLUME))
|
||||
end
|
||||
end,
|
||||
|
||||
stringtable = {
|
||||
|
||||
@@ -12,42 +12,45 @@ singleinstance = true
|
||||
singleinstanceport = 0xe493
|
||||
|
||||
acandtip.shorttip = true
|
||||
acandtip.nodynwords = true
|
||||
|
||||
activateoutput = true
|
||||
|
||||
styles = {
|
||||
-- lexer specific (inherit fg/bg from text)
|
||||
lexerdef = {fg = {128, 128, 128},},
|
||||
comment = {fg = {0, 127, 0 },bg = {240, 240, 220}, fill= true,},
|
||||
stringtxt = {fg = {127, 0, 127},},
|
||||
stringeol = {fg = {0, 0, 0 },bg = {224, 192, 224}, fill = true, },
|
||||
preprocessor = {fg = {127, 127, 0 },},
|
||||
operator = {fg = {0, 0, 0 },},
|
||||
number = {fg = {90, 100, 0 },},
|
||||
lexerdef = {fg = {128, 128, 128}},
|
||||
comment = {fg = {32, 127, 32}, bg = {250, 250, 240}, fill= true},
|
||||
stringtxt = {fg = {127, 0, 127}},
|
||||
stringeol = {fg = {0, 0, 0}, bg = {224, 192, 224}, fill = true},
|
||||
preprocessor = {fg = {127, 127,0}},
|
||||
operator = {fg = {0, 0, 0}},
|
||||
number = {fg = {90, 100, 0}},
|
||||
|
||||
keywords0 = {fg = {0, 0, 127}, b = true,},
|
||||
keywords1 = {fg = {127, 0, 0}, b = true,},
|
||||
keywords2 = {fg = {0, 127, 0}, b = true,},
|
||||
keywords3 = {fg = {0, 0, 127}, b = true,},
|
||||
keywords4 = {fg = {127, 0, 95}, b = true,},
|
||||
keywords5 = {fg = {35, 95, 175}, b = true,},
|
||||
keywords6 = {fg = {0, 127, 127}, b = true,},
|
||||
keywords7 = {fg = {240, 255, 255}, b = true,},
|
||||
keywords0 = {fg = {0, 0, 127}, b = true},
|
||||
keywords1 = {fg = {127, 0, 0}, b = true},
|
||||
keywords2 = {fg = {0, 127, 0}, b = true},
|
||||
keywords3 = {fg = {0, 0, 127}, b = true},
|
||||
keywords4 = {fg = {127, 0, 95}, b = true},
|
||||
keywords5 = {fg = {35, 95, 175}, b = true},
|
||||
keywords6 = {fg = {0, 127, 127}, b = true},
|
||||
keywords7 = {fg = {240, 255, 255}, b = true},
|
||||
|
||||
-- common (inherit fg/bg from text)
|
||||
text = nil, -- let os pick
|
||||
linenumber = {fg = {90, 90, 80},},
|
||||
linenumber = {fg = {90, 90, 80}},
|
||||
bracematch = {fg = {0, 0, 255}, b = true},
|
||||
bracemiss = {fg = {255, 0, 0 }, b = true},
|
||||
ctrlchar = nil,
|
||||
indent = {fg = {192, 192, 192},bg = {255, 255, 255},},
|
||||
indent = {fg = {192, 192, 192}, bg = {255, 255, 255}},
|
||||
calltip = nil,
|
||||
|
||||
-- common special (need custom fg & bg)
|
||||
calltipbg = nil,
|
||||
sel = nil,
|
||||
caret = nil,
|
||||
caretlinebg = nil,
|
||||
caretlinebg = {bg = {250,250,240}},
|
||||
fold = nil,
|
||||
whitespace = nil,
|
||||
|
||||
fncall = {fg = {175,175,255}, st= wxstc.wxSTC_INDIC_BOX},
|
||||
fncall = {fg = {175,175,255}, st= wxstc.wxSTC_INDIC_TT},
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user