Compare commits

...

50 Commits
0.26 ... 0.28

Author SHA1 Message Date
Paul Kulchenko
3a18136076 Fixed 'Trace' command to continue working when a debugged file is not activated 2012-03-21 10:16:17 -07:00
Paul Kulchenko
1b057988bc Updated to work with mobdebug v0.44 2012-03-21 10:14:30 -07:00
Paul Kulchenko
92001a4a78 Added option to activate output/console when Run/Debug/Compile commands are executed 2012-03-20 21:25:38 -07:00
Paul Kulchenko
906c70248b Added full screen mode 2012-03-17 21:20:32 -07:00
Paul Kulchenko
4554c67c3e Added killing running process on IDE exit. Allowed killing a running process with Shift-F12. Disabled compilation and debugging when some process is running. 2012-03-10 22:21:36 -08:00
Paul Kulchenko
cb7ee575c7 Fixed an issue with saving a file when no project directory is set 2012-03-10 21:55:31 -08:00
Paul Kulchenko
c758d4c62a Updated MANIFEST with metalua lib 2012-03-09 22:06:18 -08:00
Paul Kulchenko
8c1c06bb16 Fixed missing semicolon in lualibs path; added path for debugger to search under lualibs 2012-03-09 22:01:20 -08:00
Paul Kulchenko
99ca5fe952 Merge branch 'master' of git://estrelaeditor.git.sourceforge.net/gitroot/estrelaeditor/estrelaeditor 2012-03-08 11:49:22 -08:00
Paul Kulchenko
1f064655cc Added metalua library 2012-03-08 11:48:57 -08:00
Paul Kulchenko
9a05cc3678 Fixed missing slash on SaveAs by enforcing trailing slash for the project path 2012-03-08 10:50:47 -08:00
Paul Kulchenko
6da3cb2c32 Disabled buffering of the output for scripts run from IDE 2012-02-27 22:49:00 -08:00
Paul Kulchenko
d2cb7cb1c2 Fixed an issue with a missing path separator, which prevented debugging from executing step commands in some cases 2012-02-22 21:54:36 -08:00
crazybutcher
0ac769ffba bugfix in luxinia1 interpreter 2012-02-16 21:52:35 +01:00
Paul Kulchenko
ef60786e48 Removed debug output when coroutines are edited 2012-02-14 23:35:56 -08:00
Paul Kulchenko
f9c15faab8 Fixed activation of a correct tab when one of the editor tabs is closed 2012-02-13 18:30:45 -08:00
Paul Kulchenko
59b28ef35c Merge branch 'master' of git://estrelaeditor.git.sourceforge.net/gitroot/estrelaeditor/estrelaeditor 2012-02-12 22:42:12 -08:00
Paul Kulchenko
9db7d4ec8a Fixed an issue with file activation from a debugger (files that are 'required' now actiate correctly) 2012-02-12 22:41:41 -08:00
Paul Kulchenko
40eaace714 Disabled dynamic words in the config (zbstudio) 2012-02-10 09:47:05 -08:00
Paul Kulchenko
fe000bb59e Wrapped DragAcceptFiles into a protected call to make it not fail on MacOS (compiled with wxwidgets 2.8.12) 2012-02-09 12:23:47 -08:00
crazybutcher
9aa220df41 Merge branch 'master' of ssh://estrelaeditor.git.sourceforge.net/gitroot/estrelaeditor/estrelaeditor 2012-02-08 20:05:33 +01:00
Paul Kulchenko
97c52f15f3 Added highlight style to markup 2012-02-07 23:41:29 -08:00
Paul Kulchenko
54f578790b Merge branch 'master' of git://estrelaeditor.git.sourceforge.net/gitroot/estrelaeditor/estrelaeditor 2012-02-01 22:29:54 -08:00
Paul Kulchenko
a51a08c1b8 Removed slash conversion in filename on Save; added markup styling on SaveAs 2012-02-01 22:29:28 -08:00
Paul Kulchenko
7620e758a0 Updated error messages to more generic ones 2012-02-01 19:52:55 -08:00
Paul Kulchenko
80da9253dd Normalized the filename path to remove '.' and such 2012-02-01 19:33:08 -08:00
Paul Kulchenko
a133fa900e Fixed the issue of ClosePage method being called with two different parameters 2012-01-31 23:00:55 -08:00
Paul Kulchenko
0fa62e544a Updated markup processing with run and debug commands, http link processing, and opening local files in a new window. Switched from hotspot click event to mouse button processing to avoid the issue with selection being active in a newly loaded page 2012-01-31 22:56:18 -08:00
Paul Kulchenko
601dbef6e5 Added Debug and Run methods to simulate menu commands 2012-01-31 22:49:09 -08:00
unknown
01b3a4fd09 Merge branch 'master' of ssh://estrelaeditor.git.sourceforge.net/gitroot/estrelaeditor/estrelaeditor 2012-01-31 12:08:36 +01:00
crazybutcher
ed0df13acb dx backwards compat mode and replace / in filenames 2012-01-31 12:06:55 +01:00
Paul Kulchenko
642fd63724 Added setting a project folder on initial start (zbstudio) 2012-01-30 18:21:51 -08:00
Paul Kulchenko
a1586cd530 Replaced hardcoded path separater with a method call 2012-01-30 18:19:18 -08:00
Paul Kulchenko
291cba2d79 Merge branch 'master' of git://estrelaeditor.git.sourceforge.net/gitroot/estrelaeditor/estrelaeditor 2012-01-30 15:58:40 -08:00
Paul Kulchenko
96bffc1322 Added markup formatting in the comments 2012-01-30 15:58:28 -08:00
Paul Kulchenko
e88710fbbc Updated debugger to use project dir as basedir if it is specified (rather than the path to the currently edited file) 2012-01-30 15:03:22 -08:00
Paul Kulchenko
99da8be0f8 Fixed the issue of the project dir being returned with two trailing slashes 2012-01-30 15:03:05 -08:00
crazybutcher
c8c654cb2b removed ptx r,g,b,a keywords 2012-01-30 22:05:47 +01:00
crazybutcher
cf68a42547 added nvidia ptx spec 2012-01-30 22:01:57 +01:00
Paul Kulchenko
5c0d4cccdd Added setting the current project directory for the shell to allow 'require' commands to work with local modules 2012-01-29 17:10:55 -08:00
Paul Kulchenko
b979b41688 Fixed an issue with activating the currenly edited file in the file tree 2012-01-29 13:55:59 -08:00
Paul Kulchenko
b807fa9a99 Enforced visibility for shell prompt 2012-01-28 11:59:18 -08:00
Paul Kulchenko
ef5b1b0e09 Removed hardcoded references to the console page/tab 2012-01-28 11:53:56 -08:00
Paul Kulchenko
04a038da45 Moved wrap flags to the line end to make them less noticeable 2012-01-28 11:52:17 -08:00
Paul Kulchenko
bc0e3190d4 Added style processing for font name, font size, visibility and hotspot attributes 2012-01-28 11:20:52 -08:00
Paul Kulchenko
16dcd084e1 Renamed ShellExecuteCode to ...File; added ShellExecuteCode to execute a fragment of code 2012-01-28 10:52:35 -08:00
crazybutcher
15f8c15039 bugfix on close-file 2012-01-25 22:00:28 +01:00
crazybutcher
bff0da36dc minor updates to license (2012) and readme (front-ends explained) 2012-01-21 14:27:03 +01:00
crazybutcher
1e933a6b75 Merge remote-tracking branch 'zbstudio/master' 2012-01-21 14:15:06 +01:00
crazybutcher
c9bb3e01ca added glfw 2.7.2 api and changed luxinia2 to use it instead of glfw3 2012-01-21 14:13:54 +01:00
46 changed files with 7902 additions and 199 deletions

View File

@@ -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
View File

@@ -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
View 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,
},
}

View File

@@ -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)

View File

@@ -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)

View File

@@ -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)

View File

@@ -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

File diff suppressed because it is too large Load Diff

756
lualibs/metalua/gg.lua Normal file
View 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

File diff suppressed because it is too large Load Diff

441
lualibs/metalua/ldump.lua Normal file
View 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
View 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

View 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
}

View 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()
]]

View 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

View File

@@ -0,0 +1,3 @@
require 'metalua.base'
require 'metalua.table2'
require 'metalua.string2'

View 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

View 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

View 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 } } }

View 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"})

View 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

View 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

View 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 }

View 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

View 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) }

View File

@@ -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

View File

@@ -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
View 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
]],
},
}

View File

@@ -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 = {

View File

@@ -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()

View File

@@ -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

View File

@@ -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)

View File

@@ -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

View File

@@ -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
View 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

View File

@@ -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))

View File

@@ -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,

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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,

View File

@@ -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

View File

@@ -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.." "

View File

@@ -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

View File

@@ -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 = {

View File

@@ -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},
}