pax_global_header00006660000000000000000000000064120427264330014515gustar00rootroot0000000000000052 comment=391ac4d3eed3d2bfe57e7d8b07e1556fb2b1b99b lua-leg-0.1.3/000077500000000000000000000000001204272643300130445ustar00rootroot00000000000000lua-leg-0.1.3/CMakeLists.txt000066400000000000000000000013641204272643300156100ustar00rootroot00000000000000# Copyright (C) 2007-2012 LuaDist. # Created by Peter Drahoš # Redistribution and use of this file is allowed according to the terms of the MIT license. # For details see the COPYRIGHT file distributed with LuaDist. # Please note that the package source code is licensed under its own license. project ( leg NONE ) cmake_minimum_required ( VERSION 2.6 ) include ( cmake/dist.cmake ) include ( lua ) install_lua_module ( leg src/init.lua ) install_lua_module ( leg.grammar src/grammar.lua ) install_lua_module ( leg.parser src/parser.lua ) install_lua_module ( leg.scanner src/scanner.lua ) install_data ( README COPYRIGHT release ) install_doc ( doc/ ) install_test ( tests/ ) install_example ( examples/ ) #FIX: paths: add_lua_test ( tests/test.lua ) lua-leg-0.1.3/COPYRIGHT000066400000000000000000000023111204272643300143340ustar00rootroot00000000000000Leg Copyright 2007 Humberto Saraiva Nazareno dos Anjos. Leg is available under the MIT license. Leg uses LPeg (http://www.inf.puc-rio.br/~roberto/lpeg.html), which is also under the MIT license. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. lua-leg-0.1.3/Makefile000066400000000000000000000022711204272643300145060ustar00rootroot00000000000000# # Makefile for Leg # $Id: Makefile,v 1.7 2007/11/26 18:41:51 hanjos Exp $ # # ===== LUA PATHS ================= # Lua's library directory LUA_LIB = /usr/local/share/lua/5.1 # ===== PROJECT INFO ============== # project info NAME = leg VERSION = 0.1.2 # project directories DOC_DIR = doc SRC_DIR = src TEST_DIR = tests # the document generator used to build the documentation in doc/. # It uses an unreleased generator, so this is for my personal use. DOCCER = lua /usr/local/share/lua/5.1/ldt/ldt.lua # ===== RULES ===================== leg: mkdir -p $(NAME) rm -f $(NAME)/*.lua cp src/*.lua $(NAME) install: # copying the source files to LUA_LIB mkdir -p $(LUA_LIB)/$(NAME) rm -f $(LUA_LIB)/$(NAME)/*.lua cp src/*.lua $(LUA_LIB)/$(NAME) clean: # removing the source files and package rm -r $(LUA_LIB)/$(NAME) documents: # generate documentation mkdir -p $(DOC_DIR) $(DOCCER) src/*.lua mv ldt/* $(DOC_DIR) rm -rf ldt bundle: # tar-ing it (this works only with version 1.19 and beyond, due # the --exclude-vcs flag) tar --create --verbose --exclude-vcs --file=../$(NAME)-$(VERSION).tar ../$(NAME) # zipping it gzip ../$(NAME)-$(VERSION).tar test: cd tests; lua test.lua lua-leg-0.1.3/README000066400000000000000000000045331204272643300137310ustar00rootroot00000000000000Leg 0.1.3 ========= This is a release of Leg, a Lua library exporting a complete Lua 5.1 grammar and a small API for user manipulation. Dependencies ------------ * You need, understandably, to have Lua 5.1 up and running to be able to use this library :) * Also, Leg uses LPeg 0.7 extensively for pattern matching, so LPeg is expected be installed. You can get LPeg at http://www.inf.puc-rio.br/~roberto/lpeg.html Basic Installation ------------------ There are three ways to install Leg: * `make install` Run `make install`, and a directory called `leg` with the source files inside will be put in a specific path. Tweak Makefile's LUA_LIB variable to indicate the appropriate path for your system; Makefile ships with it set to /usr/local/share/lua/5.1 . Make sure you have the proper permissions to access the path you want; if not or in doubt, use the `make` option below. * `make` or `make leg` A directory `leg` will be created in your working directory, with the source files inside. Just put it in a LUA_PATH-visible place and you're ready to go. * by hand If you don't have, don't want to or can't use `make`, you can just put all the files in `src` inside a directory called `leg`, and put that directory in your LUA_PATH. Read the Lua Reference Manual for the LUA_PATH and the LUA_CPATH syntax (http://www.lua.org/manual/5.1/manual.html#pdf-package.path). Copyright --------- See the file "COPYRIGHT". Bug Fixes and New Stuff ----------------------- See the file "release". Testing ------- You can either: * run `make test`; or * go to the directory `tests` and run `test.lua`. Both do the same thing. Work to do ---------- * Improve error checking: currently it is bolted on and not extensible, and different patterns react differently to mismatching: scanner.STRING throws an error when a mismatch happens, but some errors simply return false and an error message. I don't know a good way to handle this. * A better API for grammar extensions. The current one is very ad hoc, and requires some fine tuning to make sure it works correctly. Metalua's API seems interesting, and was originally based on Parsec. Investigation is under way. * The binary operators' precedences isn't enforced in the grammar; one must enforce it by hand when capturing expressions. This can be very annoying. * More thorough testing. lua-leg-0.1.3/cmake/000077500000000000000000000000001204272643300141245ustar00rootroot00000000000000lua-leg-0.1.3/cmake/FindLua.cmake000066400000000000000000000076561204272643300164660ustar00rootroot00000000000000# Locate Lua library # This module defines # LUA_EXECUTABLE, if found # LUA_FOUND, if false, do not try to link to Lua # LUA_LIBRARIES # LUA_INCLUDE_DIR, where to find lua.h # LUA_VERSION_STRING, the version of Lua found (since CMake 2.8.8) # # Note that the expected include convention is # #include "lua.h" # and not # #include # This is because, the lua location is not standardized and may exist # in locations other than lua/ #============================================================================= # Copyright 2007-2009 Kitware, Inc. # Modified to support Lua 5.2 by LuaDist 2012 # # Distributed under the OSI-approved BSD License (the "License"); # see accompanying file Copyright.txt for details. # # This software is distributed WITHOUT ANY WARRANTY; without even the # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # See the License for more information. #============================================================================= # (To distribute this file outside of CMake, substitute the full # License text for the above reference.) # # The required version of Lua can be specified using the # standard syntax, e.g. FIND_PACKAGE(Lua 5.1) # Otherwise the module will search for any available Lua implementation # Always search for non-versioned lua first (recommended) SET(_POSSIBLE_LUA_INCLUDE include include/lua) SET(_POSSIBLE_LUA_EXECUTABLE lua) SET(_POSSIBLE_LUA_LIBRARY lua) # Determine possible naming suffixes (there is no standard for this) IF(Lua_FIND_VERSION_MAJOR AND Lua_FIND_VERSION_MINOR) SET(_POSSIBLE_SUFFIXES "${Lua_FIND_VERSION_MAJOR}${Lua_FIND_VERSION_MINOR}" "${Lua_FIND_VERSION_MAJOR}.${Lua_FIND_VERSION_MINOR}" "-${Lua_FIND_VERSION_MAJOR}.${Lua_FIND_VERSION_MINOR}") ELSE(Lua_FIND_VERSION_MAJOR AND Lua_FIND_VERSION_MINOR) SET(_POSSIBLE_SUFFIXES "52" "5.2" "-5.2" "51" "5.1" "-5.1") ENDIF(Lua_FIND_VERSION_MAJOR AND Lua_FIND_VERSION_MINOR) # Set up possible search names and locations FOREACH(_SUFFIX ${_POSSIBLE_SUFFIXES}) LIST(APPEND _POSSIBLE_LUA_INCLUDE "include/lua${_SUFFIX}") LIST(APPEND _POSSIBLE_LUA_EXECUTABLE "lua${_SUFFIX}") LIST(APPEND _POSSIBLE_LUA_LIBRARY "lua${_SUFFIX}") ENDFOREACH(_SUFFIX) # Find the lua executable FIND_PROGRAM(LUA_EXECUTABLE NAMES ${_POSSIBLE_LUA_EXECUTABLE} ) # Find the lua header FIND_PATH(LUA_INCLUDE_DIR lua.h HINTS $ENV{LUA_DIR} PATH_SUFFIXES ${_POSSIBLE_LUA_INCLUDE} PATHS ~/Library/Frameworks /Library/Frameworks /usr/local /usr /sw # Fink /opt/local # DarwinPorts /opt/csw # Blastwave /opt ) # Find the lua library FIND_LIBRARY(LUA_LIBRARY NAMES ${_POSSIBLE_LUA_LIBRARY} HINTS $ENV{LUA_DIR} PATH_SUFFIXES lib64 lib PATHS ~/Library/Frameworks /Library/Frameworks /usr/local /usr /sw /opt/local /opt/csw /opt ) IF(LUA_LIBRARY) # include the math library for Unix IF(UNIX AND NOT APPLE) FIND_LIBRARY(LUA_MATH_LIBRARY m) SET( LUA_LIBRARIES "${LUA_LIBRARY};${LUA_MATH_LIBRARY}" CACHE STRING "Lua Libraries") # For Windows and Mac, don't need to explicitly include the math library ELSE(UNIX AND NOT APPLE) SET( LUA_LIBRARIES "${LUA_LIBRARY}" CACHE STRING "Lua Libraries") ENDIF(UNIX AND NOT APPLE) ENDIF(LUA_LIBRARY) # Determine Lua version IF(LUA_INCLUDE_DIR AND EXISTS "${LUA_INCLUDE_DIR}/lua.h") FILE(STRINGS "${LUA_INCLUDE_DIR}/lua.h" lua_version_str REGEX "^#define[ \t]+LUA_RELEASE[ \t]+\"Lua .+\"") STRING(REGEX REPLACE "^#define[ \t]+LUA_RELEASE[ \t]+\"Lua ([^\"]+)\".*" "\\1" LUA_VERSION_STRING "${lua_version_str}") UNSET(lua_version_str) ENDIF() INCLUDE(FindPackageHandleStandardArgs) # handle the QUIETLY and REQUIRED arguments and set LUA_FOUND to TRUE if # all listed variables are TRUE FIND_PACKAGE_HANDLE_STANDARD_ARGS(Lua REQUIRED_VARS LUA_LIBRARIES LUA_INCLUDE_DIR VERSION_VAR LUA_VERSION_STRING) MARK_AS_ADVANCED(LUA_INCLUDE_DIR LUA_LIBRARIES LUA_LIBRARY LUA_MATH_LIBRARY LUA_EXECUTABLE) lua-leg-0.1.3/cmake/dist.cmake000066400000000000000000000320741204272643300160770ustar00rootroot00000000000000# LuaDist CMake utility library. # Provides sane project defaults and macros common to LuaDist CMake builds. # # Copyright (C) 2007-2012 LuaDist. # by David Manura, Peter Drahoš # Redistribution and use of this file is allowed according to the terms of the MIT license. # For details see the COPYRIGHT file distributed with LuaDist. # Please note that the package source code is licensed under its own license. ## Extract information from dist.info if ( NOT EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/dist.info ) message ( FATAL_ERROR "Missing dist.info file (${CMAKE_CURRENT_SOURCE_DIR}/dist.info)." ) endif () file ( READ ${CMAKE_CURRENT_SOURCE_DIR}/dist.info DIST_INFO ) if ( "${DIST_INFO}" STREQUAL "" ) message ( FATAL_ERROR "Failed to load dist.info." ) endif () # Reads field `name` from dist.info string `DIST_INFO` into variable `var`. macro ( _parse_dist_field name var ) string ( REGEX REPLACE ".*${name}[ \t]?=[ \t]?[\"']([^\"']+)[\"'].*" "\\1" ${var} "${DIST_INFO}" ) if ( ${var} STREQUAL DIST_INFO ) message ( FATAL_ERROR "Failed to extract \"${var}\" from dist.info" ) endif () endmacro () # _parse_dist_field ( name DIST_NAME ) _parse_dist_field ( version DIST_VERSION ) _parse_dist_field ( license DIST_LICENSE ) _parse_dist_field ( author DIST_AUTHOR ) _parse_dist_field ( maintainer DIST_MAINTAINER ) _parse_dist_field ( url DIST_URL ) _parse_dist_field ( desc DIST_DESC ) message ( "DIST_NAME: ${DIST_NAME}") message ( "DIST_VERSION: ${DIST_VERSION}") message ( "DIST_LICENSE: ${DIST_LICENSE}") message ( "DIST_AUTHOR: ${DIST_AUTHOR}") message ( "DIST_MAINTAINER: ${DIST_MAINTAINER}") message ( "DIST_URL: ${DIST_URL}") message ( "DIST_DESC: ${DIST_DESC}") string ( REGEX REPLACE ".*depends[ \t]?=[ \t]?[\"']([^\"']+)[\"'].*" "\\1" DIST_DEPENDS ${DIST_INFO} ) if ( DIST_DEPENDS STREQUAL DIST_INFO ) set ( DIST_DEPENDS "" ) endif () message ( "DIST_DEPENDS: ${DIST_DEPENDS}") ## 2DO: Parse DIST_DEPENDS and try to install Dependencies with automatically using externalproject_add ## INSTALL DEFAULTS (Relative to CMAKE_INSTALL_PREFIX) # Primary paths set ( INSTALL_BIN bin CACHE PATH "Where to install binaries to." ) set ( INSTALL_LIB lib CACHE PATH "Where to install libraries to." ) set ( INSTALL_INC include CACHE PATH "Where to install headers to." ) set ( INSTALL_ETC etc CACHE PATH "Where to store configuration files" ) set ( INSTALL_SHARE share CACHE PATH "Directory for shared data." ) # Secondary paths option ( INSTALL_VERSION "Install runtime libraries and executables with version information." OFF) set ( INSTALL_DATA ${INSTALL_SHARE}/${DIST_NAME} CACHE PATH "Directory the package can store documentation, tests or other data in.") set ( INSTALL_DOC ${INSTALL_DATA}/doc CACHE PATH "Recommended directory to install documentation into.") set ( INSTALL_EXAMPLE ${INSTALL_DATA}/example CACHE PATH "Recommended directory to install examples into.") set ( INSTALL_TEST ${INSTALL_DATA}/test CACHE PATH "Recommended directory to install tests into.") set ( INSTALL_FOO ${INSTALL_DATA}/etc CACHE PATH "Where to install additional files") # Tweaks and other defaults # Setting CMAKE to use loose block and search for find modules in source directory set ( CMAKE_ALLOW_LOOSE_LOOP_CONSTRUCTS true ) set ( CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake" ${CMAKE_MODULE_PATH} ) option ( BUILD_SHARED_LIBS "Build shared libraries" ON ) # In MSVC, prevent warnings that can occur when using standard libraries. if ( MSVC ) add_definitions ( -D_CRT_SECURE_NO_WARNINGS ) endif () # RPath and relative linking option ( USE_RPATH "Use relative linking." ON) if ( USE_RPATH ) string ( REGEX REPLACE "[^!/]+" ".." UP_DIR ${INSTALL_BIN} ) set ( CMAKE_SKIP_BUILD_RPATH FALSE CACHE STRING "" FORCE ) set ( CMAKE_BUILD_WITH_INSTALL_RPATH FALSE CACHE STRING "" FORCE ) set ( CMAKE_INSTALL_RPATH $ORIGIN/${UP_DIR}/${INSTALL_LIB} CACHE STRING "" FORCE ) set ( CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE CACHE STRING "" FORCE ) set ( CMAKE_INSTALL_NAME_DIR @executable_path/${UP_DIR}/${INSTALL_LIB} CACHE STRING "" FORCE ) endif () ## MACROS # Parser macro macro ( parse_arguments prefix arg_names option_names) set ( DEFAULT_ARGS ) foreach ( arg_name ${arg_names} ) set ( ${prefix}_${arg_name} ) endforeach () foreach ( option ${option_names} ) set ( ${prefix}_${option} FALSE ) endforeach () set ( current_arg_name DEFAULT_ARGS ) set ( current_arg_list ) foreach ( arg ${ARGN} ) set ( larg_names ${arg_names} ) list ( FIND larg_names "${arg}" is_arg_name ) if ( is_arg_name GREATER -1 ) set ( ${prefix}_${current_arg_name} ${current_arg_list} ) set ( current_arg_name ${arg} ) set ( current_arg_list ) else () set ( loption_names ${option_names} ) list ( FIND loption_names "${arg}" is_option ) if ( is_option GREATER -1 ) set ( ${prefix}_${arg} TRUE ) else () set ( current_arg_list ${current_arg_list} ${arg} ) endif () endif () endforeach () set ( ${prefix}_${current_arg_name} ${current_arg_list} ) endmacro () # install_executable ( executable_targets ) # Installs any executables generated using "add_executable". # USE: install_executable ( lua ) # NOTE: subdirectories are NOT supported set ( CPACK_COMPONENT_RUNTIME_DISPLAY_NAME "${DIST_NAME} Runtime" ) set ( CPACK_COMPONENT_RUNTIME_DESCRIPTION "Executables and runtime libraries. Installed into ${INSTALL_BIN}." ) macro ( install_executable ) foreach ( _file ${ARGN} ) if ( INSTALL_VERSION ) set_target_properties ( ${_file} PROPERTIES VERSION ${DIST_VERSION} SOVERSION ${DIST_VERSION} ) endif () install ( TARGETS ${_file} RUNTIME DESTINATION ${INSTALL_BIN} COMPONENT Runtime ) endforeach() endmacro () # install_library ( library_targets ) # Installs any libraries generated using "add_library" into apropriate places. # USE: install_library ( libexpat ) # NOTE: subdirectories are NOT supported set ( CPACK_COMPONENT_LIBRARY_DISPLAY_NAME "${DIST_NAME} Development Libraries" ) set ( CPACK_COMPONENT_LIBRARY_DESCRIPTION "Static and import libraries needed for development. Installed into ${INSTALL_LIB} or ${INSTALL_BIN}." ) macro ( install_library ) foreach ( _file ${ARGN} ) if ( INSTALL_VERSION ) set_target_properties ( ${_file} PROPERTIES VERSION ${DIST_VERSION} SOVERSION ${DIST_VERSION} ) endif () install ( TARGETS ${_file} RUNTIME DESTINATION ${INSTALL_BIN} COMPONENT Runtime LIBRARY DESTINATION ${INSTALL_LIB} COMPONENT Runtime ARCHIVE DESTINATION ${INSTALL_LIB} COMPONENT Library ) endforeach() endmacro () # helper function for various install_* functions, for PATTERN/REGEX args. macro ( _complete_install_args ) if ( NOT("${_ARG_PATTERN}" STREQUAL "") ) set ( _ARG_PATTERN PATTERN ${_ARG_PATTERN} ) endif () if ( NOT("${_ARG_REGEX}" STREQUAL "") ) set ( _ARG_REGEX REGEX ${_ARG_REGEX} ) endif () endmacro () # install_header ( files/directories [INTO destination] ) # Install a directories or files into header destination. # USE: install_header ( lua.h luaconf.h ) or install_header ( GL ) # USE: install_header ( mylib.h INTO mylib ) # For directories, supports optional PATTERN/REGEX arguments like install(). set ( CPACK_COMPONENT_HEADER_DISPLAY_NAME "${DIST_NAME} Development Headers" ) set ( CPACK_COMPONENT_HEADER_DESCRIPTION "Headers needed for development. Installed into ${INSTALL_INC}." ) macro ( install_header ) parse_arguments ( _ARG "INTO;PATTERN;REGEX" "" ${ARGN} ) _complete_install_args() foreach ( _file ${_ARG_DEFAULT_ARGS} ) if ( IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/${_file}" ) install ( DIRECTORY ${_file} DESTINATION ${INSTALL_INC}/${_ARG_INTO} COMPONENT Header ${_ARG_PATTERN} ${_ARG_REGEX} ) else () install ( FILES ${_file} DESTINATION ${INSTALL_INC}/${_ARG_INTO} COMPONENT Header ) endif () endforeach() endmacro () # install_data ( files/directories [INTO destination] ) # This installs additional data files or directories. # USE: install_data ( extra data.dat ) # USE: install_data ( image1.png image2.png INTO images ) # For directories, supports optional PATTERN/REGEX arguments like install(). set ( CPACK_COMPONENT_DATA_DISPLAY_NAME "${DIST_NAME} Data" ) set ( CPACK_COMPONENT_DATA_DESCRIPTION "Application data. Installed into ${INSTALL_DATA}." ) macro ( install_data ) parse_arguments ( _ARG "INTO;PATTERN;REGEX" "" ${ARGN} ) _complete_install_args() foreach ( _file ${_ARG_DEFAULT_ARGS} ) if ( IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/${_file}" ) install ( DIRECTORY ${_file} DESTINATION ${INSTALL_DATA}/${_ARG_INTO} COMPONENT Data ${_ARG_PATTERN} ${_ARG_REGEX} ) else () install ( FILES ${_file} DESTINATION ${INSTALL_DATA}/${_ARG_INTO} COMPONENT Data ) endif () endforeach() endmacro () # INSTALL_DOC ( files/directories [INTO destination] ) # This installs documentation content # USE: install_doc ( doc/ doc.pdf ) # USE: install_doc ( index.html INTO html ) # For directories, supports optional PATTERN/REGEX arguments like install(). set ( CPACK_COMPONENT_DOCUMENTATION_DISPLAY_NAME "${DIST_NAME} Documentation" ) set ( CPACK_COMPONENT_DOCUMENTATION_DESCRIPTION "Application documentation. Installed into ${INSTALL_DOC}." ) macro ( install_doc ) parse_arguments ( _ARG "INTO;PATTERN;REGEX" "" ${ARGN} ) _complete_install_args() foreach ( _file ${_ARG_DEFAULT_ARGS} ) if ( IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/${_file}" ) install ( DIRECTORY ${_file} DESTINATION ${INSTALL_DOC}/${_ARG_INTO} COMPONENT Documentation ${_ARG_PATTERN} ${_ARG_REGEX} ) else () install ( FILES ${_file} DESTINATION ${INSTALL_DOC}/${_ARG_INTO} COMPONENT Documentation ) endif () endforeach() endmacro () # install_example ( files/directories [INTO destination] ) # This installs additional examples # USE: install_example ( examples/ exampleA ) # USE: install_example ( super_example super_data INTO super) # For directories, supports optional PATTERN/REGEX argument like install(). set ( CPACK_COMPONENT_EXAMPLE_DISPLAY_NAME "${DIST_NAME} Examples" ) set ( CPACK_COMPONENT_EXAMPLE_DESCRIPTION "Examples and their associated data. Installed into ${INSTALL_EXAMPLE}." ) macro ( install_example ) parse_arguments ( _ARG "INTO;PATTERN;REGEX" "" ${ARGN} ) _complete_install_args() foreach ( _file ${_ARG_DEFAULT_ARGS} ) if ( IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/${_file}" ) install ( DIRECTORY ${_file} DESTINATION ${INSTALL_EXAMPLE}/${_ARG_INTO} COMPONENT Example ${_ARG_PATTERN} ${_ARG_REGEX} ) else () install ( FILES ${_file} DESTINATION ${INSTALL_EXAMPLE}/${_ARG_INTO} COMPONENT Example ) endif () endforeach() endmacro () # install_test ( files/directories [INTO destination] ) # This installs tests and test files, DOES NOT EXECUTE TESTS # USE: install_test ( my_test data.sql ) # USE: install_test ( feature_x_test INTO x ) # For directories, supports optional PATTERN/REGEX argument like install(). set ( CPACK_COMPONENT_TEST_DISPLAY_NAME "${DIST_NAME} Tests" ) set ( CPACK_COMPONENT_TEST_DESCRIPTION "Tests and associated data. Installed into ${INSTALL_TEST}." ) macro ( install_test ) parse_arguments ( _ARG "INTO;PATTERN;REGEX" "" ${ARGN} ) _complete_install_args() foreach ( _file ${_ARG_DEFAULT_ARGS} ) if ( IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/${_file}" ) install ( DIRECTORY ${_file} DESTINATION ${INSTALL_TEST}/${_ARG_INTO} COMPONENT Test ${_ARG_PATTERN} ${_ARG_REGEX} ) else () install ( FILES ${_file} DESTINATION ${INSTALL_TEST}/${_ARG_INTO} COMPONENT Test ) endif () endforeach() endmacro () # install_foo ( files/directories [INTO destination] ) # This installs optional or otherwise unneeded content # USE: install_foo ( etc/ example.doc ) # USE: install_foo ( icon.png logo.png INTO icons) # For directories, supports optional PATTERN/REGEX argument like install(). set ( CPACK_COMPONENT_OTHER_DISPLAY_NAME "${DIST_NAME} Unspecified Content" ) set ( CPACK_COMPONENT_OTHER_DESCRIPTION "Other unspecified content. Installed into ${INSTALL_FOO}." ) macro ( install_foo ) parse_arguments ( _ARG "INTO;PATTERN;REGEX" "" ${ARGN} ) _complete_install_args() foreach ( _file ${_ARG_DEFAULT_ARGS} ) if ( IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/${_file}" ) install ( DIRECTORY ${_file} DESTINATION ${INSTALL_FOO}/${_ARG_INTO} COMPONENT Other ${_ARG_PATTERN} ${_ARG_REGEX} ) else () install ( FILES ${_file} DESTINATION ${INSTALL_FOO}/${_ARG_INTO} COMPONENT Other ) endif () endforeach() endmacro () ## CTest defaults ## CPack defaults set ( CPACK_GENERATOR "ZIP" ) set ( CPACK_STRIP_FILES TRUE ) set ( CPACK_PACKAGE_NAME "${DIST_NAME}" ) set ( CPACK_PACKAGE_VERSION "${DIST_VERSION}") set ( CPACK_PACKAGE_VENDOR "LuaDist" ) set ( CPACK_COMPONENTS_ALL Runtime Library Header Data Documentation Example Other ) include ( CPack ) lua-leg-0.1.3/cmake/lua.cmake000066400000000000000000000266271204272643300157240ustar00rootroot00000000000000# LuaDist CMake utility library for Lua. # # Copyright (C) 2007-2012 LuaDist. # by David Manura, Peter Drahos # Redistribution and use of this file is allowed according to the terms of the MIT license. # For details see the COPYRIGHT file distributed with LuaDist. # Please note that the package source code is licensed under its own license. set ( INSTALL_LMOD ${INSTALL_LIB}/lua CACHE PATH "Directory to install Lua modules." ) set ( INSTALL_CMOD ${INSTALL_LIB}/lua CACHE PATH "Directory to install Lua binary modules." ) option ( SKIP_LUA_WRAPPER "Do not build and install Lua executable wrappers." OFF) # List of (Lua module name, file path) pairs. # Used internally by add_lua_test. Built by add_lua_module. set ( _lua_modules ) # utility function: appends path `path` to path `basepath`, properly # handling cases when `path` may be relative or absolute. macro ( _append_path basepath path result ) if ( IS_ABSOLUTE "${path}" ) set ( ${result} "${path}" ) else () set ( ${result} "${basepath}/${path}" ) endif () endmacro () # install_lua_executable ( target source ) # Automatically generate a binary if srlua package is available # The application or its source will be placed into /bin # If the application source did not have .lua suffix then it will be added # USE: lua_executable ( sputnik src/sputnik.lua ) macro ( install_lua_executable _name _source ) get_filename_component ( _source_name ${_source} NAME_WE ) # Find srlua and glue find_program( SRLUA_EXECUTABLE NAMES srlua ) find_program( GLUE_EXECUTABLE NAMES glue ) # Executable output set ( _exe ${CMAKE_CURRENT_BINARY_DIR}/${_name}${CMAKE_EXECUTABLE_SUFFIX} ) if ( NOT SKIP_LUA_WRAPPER AND SRLUA_EXECUTABLE AND GLUE_EXECUTABLE ) # Generate binary gluing the lua code to srlua, this is a robuust approach for most systems add_custom_command( OUTPUT ${_exe} COMMAND ${GLUE_EXECUTABLE} ARGS ${SRLUA_EXECUTABLE} ${_source} ${_exe} DEPENDS ${_source} WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} VERBATIM ) # Make sure we have a target associated with the binary add_custom_target(${_name} ALL DEPENDS ${_exe} ) # Install with run permissions install ( PROGRAMS ${_exe} DESTINATION ${INSTALL_BIN} COMPONENT Runtime) # Also install source as optional resurce install ( FILES ${_source} DESTINATION ${INSTALL_FOO} COMPONENT Other ) else() # Install into bin as is but without the lua suffix, we assume the executable uses UNIX shebang/hash-bang magic install ( PROGRAMS ${_source} DESTINATION ${INSTALL_BIN} RENAME ${_source_name} COMPONENT Runtime ) endif() endmacro () macro ( _lua_module_helper is_install _name ) parse_arguments ( _MODULE "LINK;ALL_IN_ONE" "" ${ARGN} ) # _target is CMake-compatible target name for module (e.g. socket_core). # _module is relative path of target (e.g. socket/core), # without extension (e.g. .lua/.so/.dll). # _MODULE_SRC is list of module source files (e.g. .lua and .c files). # _MODULE_NAMES is list of module names (e.g. socket.core). if ( _MODULE_ALL_IN_ONE ) string ( REGEX REPLACE "\\..*" "" _target "${_name}" ) string ( REGEX REPLACE "\\..*" "" _module "${_name}" ) set ( _target "${_target}_all_in_one") set ( _MODULE_SRC ${_MODULE_ALL_IN_ONE} ) set ( _MODULE_NAMES ${_name} ${_MODULE_DEFAULT_ARGS} ) else () string ( REPLACE "." "_" _target "${_name}" ) string ( REPLACE "." "/" _module "${_name}" ) set ( _MODULE_SRC ${_MODULE_DEFAULT_ARGS} ) set ( _MODULE_NAMES ${_name} ) endif () if ( NOT _MODULE_SRC ) message ( FATAL_ERROR "no module sources specified" ) endif () list ( GET _MODULE_SRC 0 _first_source ) get_filename_component ( _ext ${_first_source} EXT ) if ( _ext STREQUAL ".lua" ) # Lua source module list ( LENGTH _MODULE_SRC _len ) if ( _len GREATER 1 ) message ( FATAL_ERROR "more than one source file specified" ) endif () set ( _module "${_module}.lua" ) get_filename_component ( _module_dir ${_module} PATH ) get_filename_component ( _module_filename ${_module} NAME ) _append_path ( "${CMAKE_CURRENT_SOURCE_DIR}" "${_first_source}" _module_path ) list ( APPEND _lua_modules "${_name}" "${_module_path}" ) if ( ${is_install} ) install ( FILES ${_first_source} DESTINATION ${INSTALL_LMOD}/${_module_dir} RENAME ${_module_filename} COMPONENT Runtime ) endif () else () # Lua C binary module enable_language ( C ) find_package ( Lua REQUIRED ) include_directories ( ${LUA_INCLUDE_DIR} ) set ( _module "${_module}${CMAKE_SHARED_MODULE_SUFFIX}" ) get_filename_component ( _module_dir ${_module} PATH ) get_filename_component ( _module_filenamebase ${_module} NAME_WE ) foreach ( _thisname ${_MODULE_NAMES} ) list ( APPEND _lua_modules "${_thisname}" "${CMAKE_CURRENT_BINARY_DIR}/\${CMAKE_CFG_INTDIR}/${_module}" ) endforeach () add_library( ${_target} MODULE ${_MODULE_SRC}) target_link_libraries ( ${_target} ${LUA_LIBRARY} ${_MODULE_LINK} ) set_target_properties ( ${_target} PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${_module_dir}" PREFIX "" OUTPUT_NAME "${_module_filenamebase}" ) if ( ${is_install} ) install ( TARGETS ${_target} DESTINATION ${INSTALL_CMOD}/${_module_dir} COMPONENT Runtime) endif () endif () endmacro () # add_lua_module # Builds a Lua source module into a destination locatable by Lua # require syntax. # Binary modules are also supported where this function takes sources and # libraries to compile separated by LINK keyword. # USE: add_lua_module ( socket.http src/http.lua ) # USE2: add_lua_module ( mime.core src/mime.c ) # USE3: add_lua_module ( socket.core ${SRC_SOCKET} LINK ${LIB_SOCKET} ) # USE4: add_lua_module ( ssl.context ssl.core ALL_IN_ONE src/context.c src/ssl.c ) # This form builds an "all-in-one" module (e.g. ssl.so or ssl.dll containing # both modules ssl.context and ssl.core). The CMake target name will be # ssl_all_in_one. # Also sets variable _module_path (relative path where module typically # would be installed). macro ( add_lua_module ) _lua_module_helper ( 0 ${ARGN} ) endmacro () # install_lua_module # This is the same as `add_lua_module` but also installs the module. # USE: install_lua_module ( socket.http src/http.lua ) # USE2: install_lua_module ( mime.core src/mime.c ) # USE3: install_lua_module ( socket.core ${SRC_SOCKET} LINK ${LIB_SOCKET} ) macro ( install_lua_module ) _lua_module_helper ( 1 ${ARGN} ) endmacro () # Builds string representing Lua table mapping Lua modules names to file # paths. Used internally. macro ( _make_module_table _outvar ) set ( ${_outvar} ) list ( LENGTH _lua_modules _n ) if ( ${_n} GREATER 0 ) # avoids cmake complaint foreach ( _i RANGE 1 ${_n} 2 ) list ( GET _lua_modules ${_i} _path ) math ( EXPR _ii ${_i}-1 ) list ( GET _lua_modules ${_ii} _name ) set ( ${_outvar} "${_table} ['${_name}'] = '${_path}'\;\n") endforeach () endif () set ( ${_outvar} "local modules = { ${_table}}" ) endmacro () # add_lua_test ( _testfile [ WORKING_DIRECTORY _working_dir ] ) # Runs Lua script `_testfile` under CTest tester. # Optional named argument `WORKING_DIRECTORY` is current working directory to # run test under (defaults to ${CMAKE_CURRENT_BINARY_DIR}). # Both paths, if relative, are relative to ${CMAKE_CURRENT_SOURCE_DIR}. # Any modules previously defined with install_lua_module are automatically # preloaded (via package.preload) prior to running the test script. # Under LuaDist, set test=true in config.lua to enable testing. # USE: add_lua_test ( test/test1.lua [args...] [WORKING_DIRECTORY dir]) macro ( add_lua_test _testfile ) if ( NOT SKIP_TESTING ) parse_arguments ( _ARG "WORKING_DIRECTORY" "" ${ARGN} ) include ( CTest ) find_program ( LUA NAMES lua lua.bat ) get_filename_component ( TESTFILEABS ${_testfile} ABSOLUTE ) get_filename_component ( TESTFILENAME ${_testfile} NAME ) get_filename_component ( TESTFILEBASE ${_testfile} NAME_WE ) # Write wrapper script. # Note: One simple way to allow the script to find modules is # to just put them in package.preload. set ( TESTWRAPPER ${CMAKE_CURRENT_BINARY_DIR}/${TESTFILENAME} ) _make_module_table ( _table ) set ( TESTWRAPPERSOURCE "local CMAKE_CFG_INTDIR = ... or '.' ${_table} local function preload_modules(modules) for name, path in pairs(modules) do if path:match'%.lua' then package.preload[name] = assert(loadfile(path)) else local name = name:gsub('.*%-', '') -- remove any hyphen prefix local symbol = 'luaopen_' .. name:gsub('%.', '_') --improve: generalize to support all-in-one loader? local path = path:gsub('%$%{CMAKE_CFG_INTDIR%}', CMAKE_CFG_INTDIR) package.preload[name] = assert(package.loadlib(path, symbol)) end end end preload_modules(modules) arg[0] = '${TESTFILEABS}' table.remove(arg, 1) return assert(loadfile '${TESTFILEABS}')(unpack(arg)) " ) if ( _ARG_WORKING_DIRECTORY ) get_filename_component ( TESTCURRENTDIRABS ${_ARG_WORKING_DIRECTORY} ABSOLUTE ) # note: CMake 2.6 (unlike 2.8) lacks WORKING_DIRECTORY parameter. set ( _pre ${CMAKE_COMMAND} -E chdir "${TESTCURRENTDIRABS}" ) endif () file ( WRITE ${TESTWRAPPER} ${TESTWRAPPERSOURCE}) add_test ( NAME ${TESTFILEBASE} COMMAND ${_pre} ${LUA} ${TESTWRAPPER} "${CMAKE_CFG_INTDIR}" ${_ARG_DEFAULT_ARGS} ) endif () # see also http://gdcm.svn.sourceforge.net/viewvc/gdcm/Sandbox/CMakeModules/UsePythonTest.cmake # Note: ${CMAKE_CFG_INTDIR} is a command-line argument to allow proper # expansion by the native build tool. endmacro () # Converts Lua source file `_source` to binary string embedded in C source # file `_target`. Optionally compiles Lua source to byte code (not available # under LuaJIT2, which doesn't have a bytecode loader). Additionally, Lua # versions of bin2c [1] and luac [2] may be passed respectively as additional # arguments. # # [1] http://lua-users.org/wiki/BinToCee # [2] http://lua-users.org/wiki/LuaCompilerInLua function ( add_lua_bin2c _target _source ) find_program ( LUA NAMES lua lua.bat ) execute_process ( COMMAND ${LUA} -e "string.dump(function()end)" RESULT_VARIABLE _LUA_DUMP_RESULT ERROR_QUIET ) if ( NOT ${_LUA_DUMP_RESULT} ) SET ( HAVE_LUA_DUMP true ) endif () message ( "-- string.dump=${HAVE_LUA_DUMP}" ) if ( ARGV2 ) get_filename_component ( BIN2C ${ARGV2} ABSOLUTE ) set ( BIN2C ${LUA} ${BIN2C} ) else () find_program ( BIN2C NAMES bin2c bin2c.bat ) endif () if ( HAVE_LUA_DUMP ) if ( ARGV3 ) get_filename_component ( LUAC ${ARGV3} ABSOLUTE ) set ( LUAC ${LUA} ${LUAC} ) else () find_program ( LUAC NAMES luac luac.bat ) endif () endif ( HAVE_LUA_DUMP ) message ( "-- bin2c=${BIN2C}" ) message ( "-- luac=${LUAC}" ) get_filename_component ( SOURCEABS ${_source} ABSOLUTE ) if ( HAVE_LUA_DUMP ) get_filename_component ( SOURCEBASE ${_source} NAME_WE ) add_custom_command ( OUTPUT ${_target} DEPENDS ${_source} COMMAND ${LUAC} -o ${CMAKE_CURRENT_BINARY_DIR}/${SOURCEBASE}.lo ${SOURCEABS} COMMAND ${BIN2C} ${CMAKE_CURRENT_BINARY_DIR}/${SOURCEBASE}.lo ">${_target}" ) else () add_custom_command ( OUTPUT ${_target} DEPENDS ${SOURCEABS} COMMAND ${BIN2C} ${_source} ">${_target}" ) endif () endfunction() lua-leg-0.1.3/dist.info000066400000000000000000000005161204272643300146660ustar00rootroot00000000000000--- This file is part of LuaDist project -- status: stable name = 'leg' version = '0.1.3' desc = "Leg offers a complete Lua 5.1 grammar, along with a small API for user manipulation." author = "Humberto Anjo" license = "MIT" url = "http://leg.luaforge.net/" maintainer = "Peter Kapec" depends = { "lpeg >= 0.10", "lua ~> 5.1", } lua-leg-0.1.3/doc/000077500000000000000000000000001204272643300136115ustar00rootroot00000000000000lua-leg-0.1.3/doc/doc.css000066400000000000000000000101771204272643300150760ustar00rootroot00000000000000body { margin-left: 1em; margin-right: 1em; font-family: Arial, geneva, serif; background-color:#ffffff; margin:0px; font-size: 11pt; } body, td, th { color:#000000; } code { font-family: "Andale Mono", monospace; } .example { background-color: rgb(245, 245, 245); border-top-width: 1px; border-right-width: 1px; border-bottom-width: 1px; border-left-width: 1px; border-top-style: solid; border-right-style: solid; border-bottom-style: solid; border-left-style: solid; border-top-color: silver; border-right-color: silver; border-bottom-color: silver; border-left-color: silver; padding: 1em; margin-left: 1em; margin-right: 1em; font-family: "Andale Mono", monospace; font-size: smaller; } tt { font-family: "Andale Mono", monospace; } a { body, td, th { font-size: 11pt; } } h1 { font-size:1.5em; } h2 { font-size:1.25em; } h3 { font-size:1.00em; } h3 { padding-top: 1em; } h4 { font-size:0.90em; } /* h1, h2, h3, h4 { margin-left: 0em; } #product_logo { } #product_name { } #product_description { } */ textarea, pre, tt { font-size:10pt; } small { font-size:0.85em; } a:link { font-weight:bold; color: #004080; text-decoration: none; } a:visited { font-weight:bold; color: #006699; text-decoration: none; } a:link:hover { text-decoration:underline; } hr { color:#cccccc } img { border-width: 0px; } p { margin-left: 1em; } p.name { font-family: "Andale Mono", monospace; padding-top: 1em; margin-left: 0em; } blockquote { margin-left: 3em; } hr { margin-left: 0em; background: #00007f; border: 0px; height: 1px; } ul { list-style-type: disc; } table.index { border: 1px #00007f; } table.index td { text-align: left; vertical-align: top; } table.index ul { padding-top: 0em; margin-top: 0em; } table.stats { font-size: 8pt; border: 3px solid black; margin-left: 0px; margin-right: 0px; float: right; clear: right; } table { border: 1px solid black; border-collapse: collapse; margin-left: auto; margin-right: auto; } th { border: 1px solid black; padding: 0.5em; } td { border: 1px solid black; padding: 0.5em; } div.header, div.footer { margin-left: 0em; } #container { margin-left: 1em; margin-right: 1em; background-color: #f0f0f0; } #product { text-align: center; border-bottom: 1px solid #cccccc; background-color: #ffffff; } #product big { font-size: 2em; } #main { background-color: #f0f0f0; border-left: 2px solid #cccccc; } #navigation { float: left; width: 12em; margin: 0; vertical-align: top; background-color: #f0f0f0; overflow:visible; } #navigation h1 { background-color:#e7e7e7; font-size:1.1em; color:#000000; text-align:left; margin:0px; padding:0.2em; border-top:1px solid #dddddd; border-bottom:1px solid #dddddd; } #navigation ul { font-size:1em; list-style-type: none; padding: 0; margin: 1px; } #navigation li { text-indent: -1em; margin: 0em 0em 0em 0.5em; display: block; padding: 3px 0px 0px 12px; } #navigation li li a { padding: 0px 3px 0px -1em; } #content { margin-left: 12em; padding: 1em; border-left: 2px solid #cccccc; border-right: 2px solid #cccccc; background-color: #ffffff; } #about { clear: both; margin: 0; padding: 5px; border-top: 2px solid #cccccc; background-color: #ffffff; } @media print { body { font: 10pt "Times New Roman", "TimeNR", Times, serif; } a { font-weight:bold; color: #004080; text-decoration: underline; } #main { background-color: #ffffff; border-left: 0px; } #container { margin-left: 2%; margin-right: 2%; background-color: #ffffff; } #content { margin-left: 0px; padding: 1em; border-left: 0px; border-right: 0px; background-color: #ffffff; } #navigation { display: none; } #product_logo { display: none; } #about img { display: none; } .example { font-family: "Andale Mono", monospace; font-size: 8pt; page-break-inside: avoid; } } lua-leg-0.1.3/doc/grammar.html000066400000000000000000000525751204272643300161430ustar00rootroot00000000000000 grammar

grammar
LPeg grammar manipulation

Version: 0.1.2
Generated: November 26, 2007

Description

This module defines a handful of operations which can be applied to LPeg patterns and grammars in general.

Dependencies

Operations

Piping

Pattern matching dissociates the notion of matching from the notion of capturing: matching checks if a given string follows a certain pattern, and capturing generates values according to the match made. This division allows interesting possibilities:

  • different problems can be solved by applying different captures to the same grammar;
  • captures may be defined separately;
  • captures may be done on top of other captures.

Accounting for the first and second bullets, the grammar given in parser has no captures, enabling the user to reuse it to solve any problems that require a Lua grammar. One good example is documentation generation, described in a little more detail below.

The third bullet depicts a more interesting idea: a capture might take the result of another capture as input, doing a further transformation of the original data. This capture chaining, with the latter ones using the former's output as its input, is very similar to Unix pipelines, so this mechanism was named piping.

Completing

With piping, several levels of captures can be chained together up to the most appropriate for the task at hand. Yet some levels might require extra rules, and modifications to existing ones, to ensure proper matching.

To avoid manual copying, the new grammar should redefine only the necessary rules, copying the rest from the older grammar. This action is dubbed completing.

Applying

Once a new rule set is created and completed, and all captures are correctly piped, all that's left is to put them together, a process called applying. The result is a grammar ready for lpeg.P consumption, whose pattern will return the intended result when a match is made.

Example

Let's consider the problem of documenting a Lua module. In this case, comments must be captured before every function declaration when in the outermost scope:

 -- the code to parse
subject = [[
 -- Calculates the sum a+b. 
 -- An extra line.
 function sum (a, b)
 -- code
 end

 -- f1: assume a variable assignment is not a proper declaration for an 
 -- exported function
 f1 = function ()
 -- code
 end

 while true do
   -- this function is not in the outermost scope
   function aux() end
 end
 
 function something:other(a, ...)
   -- a global function without comments
 end
]]

In the code above only sum and something:other should be documented, as f1 isn't properly (by our standards) declared and aux is not in the outermost scope.

By combining LPeg and the modules scanner, parser and grammar, this specific problem can be solved as follows:

 -- change only the initial rule and make no captures
patt = grammar.apply(parser.rules, scanner.COMMENT^-1 * lpeg.V'GlobalFunction', nil)

 -- transform the new grammar into a LPeg pattern
patt = lpeg.P(patt)

 -- making a pattern that matches any Lua statement, also without captures
Stat = lpeg.P( grammar.apply(parser.rules, lpeg.V'Stat', nil) )

 -- a pattern which matches function declarations and skips statements in
 -- inner scopes or undesired tokens
patt = (patt + Stat + scanner.ANY)^0

 -- matching a string
patt:match(subject)

These are the relevant rules in the grammar:

GlobalFunction = 'function' * FuncName * FuncBody
FuncName     = ID * ('.' * ID)^0 * (':' * ID)^-1
FuncBody     = '(' * (ParList + EPSILON) * ')' * Block * 'end'
ParList      = NameList * (',' * '...')^-1
NameList     = ID * (',' * ID)^0
ID           = scanner.ID
EPSILON      = lpeg.P(true)

It may seem that ParList + EPSILON could be substituted for ParList^-1 (optionally match ParList), but then no captures would be made for empty parameter lists, and GlobalFunction would get all strings matched by FuncBody. The EPSILON rule acts in this manner as a placeholder in the argument list, avoiding any argument list processing in the capture function.

Since no captures are being made, lpeg.match doesn't return anything interesting. Here are some possible captures:

 -- some interesting captures bundled up in a table. Note that the table keys
 -- match the grammar rules we want to add captures to. Whatever rules aren't in
 -- the rules table below will come from parser.rules .
captures = {
 [1] = function (...)  -- the initial rule
   return '<function>'..table.concat{...}..'</function>' 
 end,
 
 GlobalFunction = function (name, parlist)
   return '<name>'..name..'</name><parlist>'..(parlist or '')..'</parlist>' 
 end,
 
 FuncName = grammar.C,  -- capture the raw text
 ParList  = grammar.C,  -- capture the raw text
 COMMENT  = scanner.comment2text,  -- remove the comment trappings
}

 -- spacing rule
local S = scanner.SPACE ^ 0

 -- rules table
rules = {
 [1]     = ((lpeg.V'COMMENT' *S) ^ 0) *S* lpeg.V'GlobalFunction',
 COMMENT = scanner.COMMENT,
}

 -- building the new grammar and adding the captures
patt = lpeg.P( grammar.apply(parser.rules, rules, captures) )

 -- a pattern that matches a sequence of patts and concatenates the results
patt = (patt + Stat + scanner.ANY)^0 / function(...) 
 return table.concat({...}, '\n\n')  -- some line breaks for easier reading
end

 -- finally, matching a string
print(patt:match(subject))

FuncBody needs no captures, as Block and all its non-terminals have none; it just needs to pass along any captures made by ParList. NameList and ID also have no captures, and the whole subject string is passed further.

The printed result is:

<function>Calculates the sum a+b. An extra line.<name>sum</name><parlist>a, b</parlist></function>

<function><name>something:other</name><parlist>a, ...</parlist></function>


Functions

anyOf (list) Returns a pattern which matches any of the patterns received.
apply (grammar, rules, captures) Completes rules with grammar and then applies captures.
C () A capture function, made so that patt / C is equivalent to m.C(patt). It's intended to be used in capture tables, such as those required by pipe and apply.
complete (dest, orig) Completes dest with orig.
copy (grammar) Creates a shallow copy of grammar.
Ct () A capture function, made so that patt / Ct is equivalent to m.Ct(patt). It's intended to be used in capture tables, such as those required by pipe and apply.
listOf (patt, sep) Returns a pattern which matches a list of patts, separated by sep.
pipe (dest, orig) Pipes the captures in orig to the ones in dest.


anyOf (list)
    Returns a pattern which matches any of the patterns received.

    Example:

    local g, s, m = require 'leg.grammar', require 'leg.scanner', require 'lpeg'
    
     -- match numbers or operators, capture the numbers
    print( (g.anyOf { '+', '-', '*', '/', m.C(s.NUMBER) }):match '34.5@23 * 56 / 45 - 45' )
     --> prints 34.5
    

    Parameters:

    • list: a list of zero or more LPeg patterns or values which can be fed to lpeg.P.

    Returns:

    • a pattern which matches any of the patterns received.


apply (grammar, rules, captures)
    Completes rules with grammar and then applies captures.

    rules can either be:

    • a single pattern, which is taken to be the new initial rule,
    • a possibly incomplete LPeg grammar, as per complete, or
    • nil, which means no new rules are added.

    captures can either be:

    • a capture table, as per pipe, or
    • nil, which means no captures are applied.

    Parameters:

    • grammar: the old grammar. It stays unmodified.
    • rules: optional, the new rules.
    • captures: optional, the final capture table.

    Returns:

    • rules, suitably augmented by grammar and captures.


C ()
    A capture function, made so that patt / C is equivalent to m.C(patt). It's intended to be used in capture tables, such as those required by pipe and apply.


complete (dest, orig)
    Completes dest with orig.

    Parameters:

    • dest: the new grammar. Must be a table.
    • orig: the original grammar. Must be a table.

    Returns:

    • dest, with new rules inherited from orig.


copy (grammar)
    Creates a shallow copy of grammar.

    Parameters:

    • grammar: a regular table.

    Returns:

    • a newly created table, with grammar's keys and values.


Ct ()
    A capture function, made so that patt / Ct is equivalent to m.Ct(patt). It's intended to be used in capture tables, such as those required by pipe and apply.


listOf (patt, sep)
    Returns a pattern which matches a list of patts, separated by sep.

    Example: matching comma-separated values:

    local g, m = require 'leg.grammar', require 'lpeg'
    
     -- separator
    local sep = m.P',' + m.P'\n'
    
     -- element: anything but sep, capture it
    local elem = m.C((1 - sep)^0)
    
     -- pattern
    local patt = g.listOf(elem, sep)
    
     -- matching
    print( patt:match [[a, b, 'christmas eve'
     d, evening; mate!
     f]])
     --> prints out "a        b       'christmas eve'  d        evening; mate! f"
    

    Parameters:

    • patt: a LPeg pattern.
    • sep: a LPeg pattern.

    Returns:

    • the following pattern:
      patt * (sep * patt)^0


pipe (dest, orig)
    Pipes the captures in orig to the ones in dest.

    dest and orig should be tables, with each key storing a capture function. Each capture in dest will be altered to use the results for the matching one in orig as input, using function composition. Should orig possess keys not in dest, dest will copy them.

    Parameters:

    • dest: a capture table.
    • orig: a capture table.

    Returns:

    • dest, suitably modified.

lua-leg-0.1.3/doc/index.html000066400000000000000000000100221204272643300156010ustar00rootroot00000000000000 Leg

Leg
LPeg-powered Lua 5.1 grammar

Version: 0.1.2
Generated: November 26, 2007

Overview

Leg is a Lua library which offers a complete Lua 5.1 grammar, along with some functions to use and modify it. Some examples of projects which could benefit from Leg are a syntax highlighter, a Luadoc-style document generator, and a macro preprocessor.

Leg uses LPeg for pattern matching, and returns LPeg patterns for user manipulation.

Leg is available under the same license as Lua 5.1.

Dependencies

Download

Leg can be downloaded from its LuaForge page.

Credits

This project is maintained by Humberto Anjos, and was adapted from an earlier project done with Francisco Sant'Anna.

License

Copyright © 2007 Humberto Saraiva Nazareno dos Anjos.

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

lua-leg-0.1.3/doc/lua.gif000066400000000000000000000102101204272643300150530ustar00rootroot00000000000000GIF87arvz}~ oooqqqvvvzzz !!%%""%%)),,55115511::::??BBDDBBEEJJHHLLMMPPTTQQWWYYZZ\\^^``ccggiijjoollqqvvttyy{{~~‰ËŎƐǒȕʙ˙̟ϡϢѥҩөԭ֯ױױصڻݼ,' H*\ȰÇ#JHŋ3jȱǏ CIɓ(S\ɲ˗0cʜIpִQ:(RJI;HGSSFAPȑA!rC+j D#:S Y:A mSNF1Rg.=2 @ ;5A)s.:)v 0`ϟlnp#rdDD#xЙ 643#LTFyPE+]s2Qk( ;gGΗPfh ( oNr,NiHG0^s& *92;!lX_mwDGU7 DX qIRw5EA&9MdMDHM(އhcYC$#!~8@I$~! $pP |@ c11abRI!``a㠁Y2qkD;Pv}6jǍ0}F^ipZDE ph2+ar&C }t.GEbC |,s69 P( y0DpVft%Af.KH?,nb9"|/GAH"} A#w:p;x5$sE-I";[Xwu85!KV}[A]$ܩepG pYC@ڄkn:m VXf2 BYzD3 ,.zs9PBD\ lf4y",Bscdqf(6J袋@\[$z@@B ˦=5ɢE8H Dk8F{R= ~}Ν `g-1,RBHe-Ms (!{QHm ` AcRf!F &D `Dvd!B07 ,^lأRPIh~ @r)`$ hC@AG"yX mB4i Y(D8& F+1Rh KIH+ @EJ I* #RWDBhD ,6o| m#DxBЪV@$Y B yC$B*$} ZE @asPJoQ| Η?BYeJB8JH,iuJ&9a!r(#D|,9|D'yF)_st$ @2^o'ЯCh>@dz,F!QHB6I( E9E;C\oh2B5+$T}mHCه:VGjM0$8!mhApfBɏ@ m:O<6t}p<1@"t$H "BZ-6lgq,:@|P aF #Q!a'Qi];$}^V_xpmѳq8K`m[@(m`q! B /[mrg&^&qt$ 6g)l ];FpHA(]r Ƃb`QAFkq72-KMΧh xw=XvA@@m‡ƺXZO/ ?xă(+D,d9}Ev XԽIe 0]4y}'W!)] B|Fq D ͧ?s!#F3_ӣ738 N>@8lH q[ JƱ7(hE6,sI7)F0Vp`[%W25fc"8t.Aa!D@i_qeG#UT4PFxE vDa~ R t<"(WAl-[uf$P%~w9W@xwHv{9whV65P\ "ufoDd2g>6-c0(Y< 1"1O*p)ЊPj*25 0% (@BanS{ DN/%Jҍ  (3 b1$X]RP9Z*)~N-#r]NhGpl с^@FY4}b/M !0U|Me=dE$>pM ~rJ-&&@.\ 87{3`%0ds+1Hh22PK3 W0uE)*U))+ @50FtEOKHP@}^ jH0}U`10 k咹EcVUK$A_z`TEΑUɕ4A֓<RrfPGBza"gd62(ayImF"qc("gAyP0i@eESҘYO9b4"}"x@'Pupj`22,*&qٕf%E+]B%f0YJ3f?1,/p?;}XV5Q%H1Z4JmpOP7d=O y;!ӑpeq!F PP-eKfw.lzp`- j`o@EPOv*RzUP uS@Ĩ!IO*PEAO` p\ P 1{:LSPU@QzRʪ#4PG`Nڬ;lua-leg-0.1.3/doc/parser.html000066400000000000000000000353071204272643300160030ustar00rootroot00000000000000 parser

parser
Lua 5.1 parser

Version: 0.1.2
Generated: November 26, 2007

Description

Pairing with scanner, this module exports Lua 5.1's syntactic rules as a grammar.

Dependencies

The Grammar

The rules variable implements the official Lua 5.1 grammar. It includes all keyword and symbol rules in scanner, as well as the CHUNK rule, which matches a complete Lua source file.

rules is a table with open references, not yet a LPeg pattern; to create a pattern, it must be given to lpeg.P. This is done to enable users to modify the grammar to suit their particular needs. grammar provides a small API for this purpose.

The code below shows the Lua 5.1 grammar in LPeg, minus spacing issues.

The following convention is used for rule names:

  • TOKENRULE: token rules (which represent terminals) are in upper case when applicable (ex. +, WHILE, NIL, ..., THEN, {, ==).
  • GrammarRule: the main grammar rules (non-terminals): Examples are Chunk, FuncName, BinOp, and TableConstructor.
  • _GrammarRule: subdivisions of the main rules, introduced to ease captures. Examples are _SimpleExp, _PrefixExpParens and _FieldExp.
  • METARULE: grammar rules with a special semantic meaning, to be used for capturing in later modules, like BOF, EOF and EPSILON.

rules = {
   -- See peculiarities below
   IGNORED  = scanner.IGNORED  -- used as spacing, not depicted below
   EPSILON = lpeg.P(true)
   EOF     = scanner.EOF  -- end of file
   BOF     = scanner.BOF  -- beginning of file
   Name    = ID

   -- Default initial rule
   [1]     = CHUNK
   CHUNK   = scanner.BANG^-1 * Block

   Chunk   = (Stat * ';'^-1)^0 * (LastStat * ';'^-1)^-1
   Block   = Chunk

   -- STATEMENTS
   Stat          = Assign + FunctionCall + Do + While + Repeat + If
                 + NumericFor + GenericFor + GlobalFunction + LocalFunction
                 + LocalAssign
   Assign        = VarList * '=' * ExpList
   Do            = 'do' * Block * 'end'
   While         = 'while' * Exp * 'do' * Block * 'end'
   Repeat        = 'repeat' * Block * 'until' * Exp
   If            = 'if' * Exp * 'then' * Block
                     * ('elseif' * Exp * 'then' * Block)^0
                     * (('else' * Block) + EPSILON)
                     * 'end'
   NumericFor    = 'for' * Name * '='
                     * Exp * ',' * Exp * ((',' * Exp) + EPSILON)
                     * 'do' * Block * 'end'
   GenericFor    = 'for' * NameList * 'in' * ExpList * 'do' * Block * 'end'
   GlobalFunction = 'function' * FuncName * FuncBody
   LocalFunction = 'local' * 'function' * Name * FuncBody
   LocalAssign   = 'local' * NameList * ('=' * ExpList)^-1
   LastStat      = 'return' * ExpList^-1
                 + 'break'

   -- LISTS
   VarList  = Var * (',' * Var)^0
   NameList = Name * (',' * Name)^0
   ExpList  = Exp * (',' * Exp)^0

   -- EXPRESSIONS
   Exp          = _SimpleExp * (BinOp * _SimpleExp)^0
   _SimpleExp   = 'nil' + 'false' + 'true' + Number + String + '...' + Function
                + _PrefixExp + TableConstructor + (UnOp * _SimpleExp)
   _PrefixExp   = ( Name                  a Var
                  + _PrefixExpParens      only an expression
                  ) * (
                      _PrefixExpSquare    a Var
                    + _PrefixExpDot       a Var
                    + _PrefixExpArgs      a FunctionCall
                    + _PrefixExpColon     a FunctionCall
                  ) ^ 0

   -- Extra rules for semantic actions:
   _PrefixExpParens = '(' * Exp * ')'
   _PrefixExpSquare = '[' * Exp * ']'
   _PrefixExpDot    = '.' * ID
   _PrefixExpArgs   = Args
   _PrefixExpColon  = ':' * ID * _PrefixExpArgs

   -- These rules use an internal trick to be distingished from _PrefixExp
   Var              = _PrefixExp
   FunctionCall     = _PrefixExp

   -- FUNCTIONS
   Function     = 'function' * FuncBody
   FuncBody     = '(' * (ParList+EPSILON) * ')' * Block * 'end'
   FuncName     = Name * _PrefixExpDot^0 * ((':' * ID)+EPSILON)
   Args         = '(' * (ExpList+EPSILON) * ')'
                + TableConstructor + String
   ParList      = NameList * (',' * '...')^-1
                + '...'

   -- TABLES
   TableConstructor = '{' * (FieldList+EPSILON) * '}'
   FieldList        = Field * (FieldSep * Field)^0 * FieldSep^-1
   FieldSep         = ',' + ';'

   -- Extra rules for semantic actions:
   _FieldSquare     = '[' * Exp * ']' * '=' * Exp
   _FieldID         = ID * '=' * Exp
   _FieldExp        = Exp

   -- OPERATORS
   BinOp    = '+' + '-' + '*' + '/' + '^' + '%' + '..'
            + '<' + '<=' + '>' + '>=' + '==' + '~='
            + 'and' + 'or'
   UnOp     = '-' + 'not' + '#'

   -- ...plus scanner's keywords and symbols
}

The implementation has certain peculiarities that merit clarification:

  • Spacing is matched only between two tokens in a rule, never at the beginning or the end of a rule.
  • EPSILON matches the empty string, which means that it always succeeds without consuming input. Although rule + EPSILON can be changed to rule^-1 without any loss of syntactic power, EPSILON was introduced in the parser due to it's usefulness as a placeholder for captures.
  • BOF and EOF are rules used to mark the bounds of a parsing match, and are useful for semantic actions.
  • Name versus ID: the official Lua grammar doesn't distinguish between them, as their syntax is exactly the same (Lua identifiers). But semantically Name is a variable identifier, and ID is used with different meanings in _FieldID, FuncName, _PrefixExpColon and _PrefixExpDot.
  • In Lua's original extended BNF grammar, Var and FunctionCall are defined using left recursion, which is unavailable in PEGs. In this implementation, the problem was solved by modifying the PEG rules to eliminate the left recursion, and by setting some markers (with some LPeg chicanery) to ensure the proper pattern is being used.


Variables

rules = table 

A table holding the Lua 5.1 grammar. See The Grammar for an extended explanation.


Functions

apply (extraRules, captures) Uses grammar.apply to return a new grammar, with captures and extra rules. rules stays unmodified.
check (input) Checks if input is valid Lua source code.


apply (extraRules, captures)
    Uses grammar.apply to return a new grammar, with captures and extra rules. rules stays unmodified.

    Parameters:

    • extraRules: optional, the new and modified rules. See grammar.apply for the accepted format.
    • captures: optional, the desired captures. See grammar.apply for the accepted format.

    Returns:

    • the extended grammar.


check (input)
    Checks if input is valid Lua source code.

    Parameters:

    • input: a string containing Lua source code.

    Returns:

    • true, if input is valid Lua source code, or false and an error message if the matching fails.

lua-leg-0.1.3/doc/scanner.html000066400000000000000000000326441204272643300161410ustar00rootroot00000000000000 scanner

scanner
Lua 5.1 lexical patterns

Version: 0.1.2
Generated: November 26, 2007

Description

This module exports several Lua lexical patterns, all implemented in LPeg. None of them have captures.

Dependencies

Example

The following example lists all the tokens in a Lua script:

local lpeg = require 'lpeg'
local scanner = require 'leg.scanner'

 -- this pattern captures all tokens, ignores spaces and comments,
 -- and matches with end of file, giving an error otherwise
patt = (lpeg.C(scanner.TOKEN) + scanner.SPACE + scanner.COMMENT)^0
    * (scanner.EOF + scanner.error'invalid character')
patt = lpeg.Ct(patt)

 -- opens the file passed as parameter and tries to match with patt
f = assert(io.open(arg[1]))

 -- a table storing all the tokens
ALL = patt:match(f:read'*a')
f:close()

 -- dumps all tokens on screen
for _, tk in ipairs(ALL) do
   print(tk)
end


Variables

ANY = LPeg pattern 
Matches any token, comment or space.
BANG = LPeg pattern 
Matches UNIX's shebang (e.g. #!/usr/bin/lua).
BOF = LPeg pattern 
Matches the beginning of a file.
COMMENT = LPeg pattern 
Matches any type of comment.
EOF = LPeg pattern 
Matches the end of a file.
IDENTIFIER = LPeg pattern 
Matches any Lua identifier.
IGNORED = LPeg pattern 
Matches everything ignored by the parser.
KEYWORD = LPeg pattern 
A pattern which matches any Lua keyword.
keywords = {} 

A table with Lua keyword-matching patterns, with the keywords in uppercase as keys.

Examples: keywords.WHILE, keywords['ELSEIF'].

NUMBER = LPeg pattern 
Matches any Lua number.
SPACE = LPeg pattern 
Matches any space character.
STRING = LPeg pattern 
Matches any Lua string.
SYMBOL = LPeg pattern 
A pattern which matches any Lua symbol.
symbols = {} 

A table with Lua symbol-matching patterns, with the symbols themselves as keys.

Examples: symbols['{'], symbols['+'].

TOKEN = LPeg pattern 
Matches any Lua identifier, keyword, symbol, number or string.

Functions

comment2text (comment) Strips all prefixing -- and enclosing --[=*[ from comment tokens.
error (msg) Returns a function which throws lexical errors.
string2text (str) Strips all enclosing ', ", and [=*[ from string tokens, and processes escape characters.
text2comment (text) Encloses the text with comment markers.
text2string (text) Transforms a text into a syntactically valid Lua string. Similar to string.format with the '%q' option, but inserting escape numbers and escape codes where applicable.


comment2text (comment)
    Strips all prefixing -- and enclosing --[=*[ from comment tokens.

    Parameters:

    • comment: the comment to strip.

    Returns:

    • the text without comment marking syntax.


error (msg)
    Returns a function which throws lexical errors.

    Parameters:

    • msg: the message to be concatenated to the error message.

    Returns:

    • A function built to be used as a LPeg pattern, which will throw an error when matched.

    Usage example:

    patt = intended_pattern^0 * (EOF + error'character after EOF')
    

    It may also be used as a normal function:

    function (subject, i)
     if bad_condition then
       error'bad condition'(subject, i)
     end
    end
    


string2text (str)
    Strips all enclosing ', ", and [=*[ from string tokens, and processes escape characters.

    Parameters:

    • str: the string to strip.

    Returns:

    • the text without string enclosers.


text2comment (text)
    Encloses the text with comment markers.

    Parameters:

    • text: the text to comment.

    Returns:

    • the text with comment marking syntax.


text2string (text)
    Transforms a text into a syntactically valid Lua string. Similar to string.format with the '%q' option, but inserting escape numbers and escape codes where applicable.

    Parameters

    • text: a string containing the text.

    Returns:

    • a string, similar to string.format with option '%q'.

lua-leg-0.1.3/examples/000077500000000000000000000000001204272643300146625ustar00rootroot00000000000000lua-leg-0.1.3/examples/comment-extraction.lua000077500000000000000000000065371204272643300212230ustar00rootroot00000000000000------------------------------------------------------------------------------- -- -- The comment extraction example given in doc/grammar.html. For those of you -- who haven't read it, the code and comments are available below. -- -- Authors: Humberto Anjos and Francisco Sant'Anna -- -- $Id: comment-extraction.lua,v 1.2 2007/11/19 13:34:47 hanjos Exp $ -- ------------------------------------------------------------------------------- -- some imports to get things started. local lpeg = require 'lpeg' local parser = require 'leg.parser' local scanner = require 'leg.scanner' local grammar = require 'leg.grammar' -- some aliasing local P, V = lpeg.P, lpeg.V -- argument capturing local args = { ... } -- -- Let's consider the problem of documenting a Lua module. In this case, comments -- must be captured before every function declaration when in the outermost scope. -- -- the code to parse subject = args[1] or [=[ -- Calculates the sum a+b. -- An extra line. function sum (a, b) -- code end -- a variable assignment is not a "proper" declaration for an -- exported function f1 = function () -- code end while true do -- this function is not in the outermost scope function aux() end end function something:other(a, ...) -- a global function without comments return a, ... -- won't appear in the result end ]=] -- In the code above we want only to document sum and something:other, as -- f1 isn't properly (by our standards) declared and aux is not in the -- outermost scope (although it is still a global function). -- -- Let's define some patterns to simplify our job: -- spacing rule local S = scanner.SPACE ^ 0 -- matches any Lua statement, no captures Stat = P( parser.apply(V'Stat', nil) ) -- some interesting captures bundled up in a table. Note that the table keys -- match the grammar rules we want to add captures to. Any rules not in the -- `rules` table below will come from parser.rules . captures = { [1] = function (...) -- the initial rule return ''..table.concat{...}..'' end, GlobalFunction = function (name, parlist) -- global function declaration return ''..name..''..(parlist or '')..'' end, FuncName = grammar.C, -- capture the raw text ParList = grammar.C, -- capture the raw text COMMENT = scanner.comment2text, -- extract comment trappings } -- the rules table rules = { [1] = ((V'COMMENT' *S) ^ 0) *S* V'GlobalFunction', -- new initial rule COMMENT = scanner.COMMENT, -- just to add COMMENT's capture to the capture table } -- building the new grammar and adding the captures. This pattern will match -- any global function optionally preceded by comments, and return a string -- in the following format: -- -- commentsnameparameter list commentedFunc = P( grammar.apply(parser.rules, rules, captures) ) -- finally, this pattern matches all commented global functions, Stats -- or any other Lua tokens and packages the results in a table. This is done -- to capture only global function declarations in the global scope. patt = (commentedFunc + Stat + scanner.ANY)^0 / function(...) return table.concat({...}, '\n\n') -- some line breaks for easier reading end -- now match subject print('subject:', '\n'..subject) print('result:', '\n'..patt:match(subject)) lua-leg-0.1.3/examples/local-imports.lua000066400000000000000000000052721204272643300201600ustar00rootroot00000000000000------------------------------------------------------------------------------- -- -- A preprocessor that detects which basic modules and functions are being -- used, and adds local declarations at the top of the code. Somewhat useful -- (at least for me) along a module declaration. -- -- This is a very simplistic preprocessor: it simply searches for identifiers -- with the same name as a basic module or function. This might yield false -- positives, as a variable which happens to have the same name as a basic -- value will be incorrectly counted. Since the code generated here goes before -- the code in the input, the program still runs normally, so the false -- positives don't affect its semantics. -- -- A more complex analysis could be made, but it would be far easier to do it -- with an AST. -- -- Author: Humberto Anjos -- -- $Id: local-imports.lua,v 1.1 2007/12/03 20:46:16 hanjos Exp $ -- ------------------------------------------------------------------------------- -- imported modules local scanner = require 'leg.scanner' local lpeg = require 'lpeg' -- imported functions local P = lpeg.P -- HELPER CODE ------------------------ -- tables using the names of Lua's basic modules and functions as keys, -- for easy searching local basicModules, basicFunctions = {}, {} -- populating the tables for k, v in pairs(_G) do if type(v) == 'table' then basicModules[k] = true elseif type(v) == 'function' then basicFunctions[k] = true end end -- to pretty print the statements local function maxNameLength(list) local max = 0 for _, v in ipairs(list) do if max < #v then max = #v end end return max end local function buildStatements(list, type) local str, max = '-- basic '..type..'\n', maxNameLength(list) table.sort(list) for _, v in ipairs(list) do str = str..'local '..v..string.rep(' ', max - #v)..' = '..v..'\n' end return str end local modules, functions = {}, {} local ID = scanner.IDENTIFIER / function (id) if basicModules[id] and not modules[id] then modules[#modules + 1] = id modules[id] = #modules elseif basicFunctions[id] and not functions[id] then functions[#functions + 1] = id functions[id] = #functions end end local ALL = P( (ID + 1)^0 ) -- TESTING ---------------------------- local args = { ... } subject = args[1] or [=[ local a = _VERSION or math.pi local table = {} -- false positive for i, v in ipairs(table.sort(_G[t])) do if type(v) == 'function' then print(i, v) end end ]=] ALL:match(subject) result = buildStatements(modules, 'modules')..'\n' ..buildStatements(functions, 'functions')..'\n-- code\n' ..subject print('subject:', '\n'..subject) print('result:', '\n'..result)lua-leg-0.1.3/examples/metalua-ast.lua000077500000000000000000000476601204272643300176220ustar00rootroot00000000000000------------------------------------------------------------------------------- -- -- An AST builder for Leg. This AST is from Metalua -- (http://metalua.luaforge.net). -- -- Author: Humberto Anjos (the code below) and Fabien Fleutot (the AST design) -- -- $Id: metalua-ast.lua,v 1.2 2007/12/03 20:46:16 hanjos Exp $ -- ------------------------------------------------------------------------------- -- basic modules local _G = _G local math = math local string = string local table = table -- basic functions local error = error local ipairs = ipairs local pairs = pairs local print = print local require = require local select = select local tonumber = tonumber local type = type local unpack = unpack -- imported modules local parser = require 'leg.parser' local grammar = require 'leg.grammar' local scanner = require 'leg.scanner' local lpeg = require 'lpeg' -- imported functions local P = lpeg.P -- AST BUILDING FUNCTIONS ------------- -- the table holding the node builders builder = {} -- last stats function builder.Break() return { tag = 'Break' } end function builder.Return(...) return { tag = 'Return', ... } end -- statements function builder.Do(block) return { tag = 'Do', block } end function builder.Let(lhslist, exprlist) return { tag = 'Let', lhslist, exprlist } end function builder.While(expr, block) return { tag = 'While', expr, block } end function builder.Repeat(block, expr) return { tag = 'Repeat', block, expr } end function builder.If(...) return { tag = 'If', ... } end function builder.Fornum(var, index, limit, step, block) return { tag = 'Fornum', var, index, limit, step or Number(1), block } end function builder.Forin(varlist, exprlist, block) return { tag = 'Forin', varlist, exprlist, block } end function builder.Local(varlist, exprlist) return { tag = 'Local', varlist, exprlist } end function builder.Localrec(varlist, exprlist) return { tag = 'Localrec', varlist, exprlist } end function builder.Call(func, ...) return { tag = 'Call', func, ... } end function builder.Method(table, string, ...) return { tag = 'Method', table, string, ... } end -- expressions function builder.Nil() return { tag = 'Nil' } end function builder.Dots() return { tag = 'Dots' } end function builder.True() return { tag = 'True' } end function builder.False() return { tag = 'False' } end function builder.Number(number) return { tag = 'Number', number } end function builder.String(string) return { tag = 'String', string } end function builder.Function(parlist, block) return { tag = 'Function', parlist, block } end function builder.Table(...) return { tag = 'Table', ... } end function builder.Key(key, value) return { tag = 'Key', key, value } end function builder.Op(op, value1, value2) return { tag = 'Op', op, value1, value2 } end -- a parenthesized expression function builder.One(expr) return { tag = 'One', expr } end -- variables function builder.Id(identifier) return { tag = 'Id', identifier } end function builder.Index(table, index) return { tag = 'Index', table, index } end -- operators function builder.Add() return { tag = 'Add' } end function builder.Sub() return { tag = 'Sub' } end function builder.Mul() return { tag = 'Mul' } end function builder.Div() return { tag = 'Div' } end function builder.Mod() return { tag = 'Mod' } end function builder.Pow() return { tag = 'Pow' } end function builder.Concat() return { tag = 'Concat' } end function builder.Eq() return { tag = 'Eq' } end function builder.Ne() return { tag = 'Ne' } end function builder.Gt() return { tag = 'Gt' } end function builder.Ge() return { tag = 'Ge' } end function builder.Lt() return { tag = 'Lt' } end function builder.Le() return { tag = 'Le' } end function builder.And() return { tag = 'And' } end function builder.Or() return { tag = 'Or' } end function builder.Not() return { tag = 'Not' } end function builder.Len() return { tag = 'Len' } end -- technically, the operator Sub is also used for the unary operator -, -- but to avoid ambiguities during the construction of the expression tree, -- I preferred to build an Unm node and change it to a Sub when the node -- is safely identified as an unary -. function builder.Unm() return { tag = 'Unm' } end -- OPERATOR PROCESSING CODE ----------- -- OBS.: -- Leg's grammar does not specify operator precedence, so it must be treated -- outside the grammar. This really sucks, and it's on the list of things to -- improve in future versions. -- Operator precedence table. Maps operator tags to a table holding the -- respective precedence, left or right associativity, and arity (unary -- or binary) local ops = { Or = { precedence = 1, left = true, arity = 2 }, And = { precedence = 2, left = true, arity = 2 }, Eq = { precedence = 3, left = true, arity = 2 }, Ne = { precedence = 3, left = true, arity = 2 }, Le = { precedence = 3, left = true, arity = 2 }, Ge = { precedence = 3, left = true, arity = 2 }, Lt = { precedence = 3, left = true, arity = 2 }, Gt = { precedence = 3, left = true, arity = 2 }, Concat = { precedence = 4, right = true, arity = 2 }, Add = { precedence = 5, left = true, arity = 2 }, Sub = { precedence = 5, left = true, arity = 2 }, Mul = { precedence = 6, left = true, arity = 2 }, Div = { precedence = 6, left = true, arity = 2 }, Mod = { precedence = 6, left = true, arity = 2 }, Not = { precedence = 7, arity = 1 }, Len = { precedence = 7, arity = 1 }, Unm = { precedence = 7, arity = 1 }, Pow = { precedence = 8, right = true, arity = 2 } } -- some self-explaining helper functions local function isOperator(node) return node and ops[node.tag] end local function isUnary(node) return isOperator(node) and ops[node.tag].arity == 1 end local function isBinary(node) return isOperator(node) and ops[node.tag].arity == 2 end -- Takes a list of tokens with Lua values and operators and returns it in -- Reverse Polish Notation, using Dijkstra's shunting yard algorithm. The -- actual expression tree will be built in Exp's capture function local function toRPN(list) local queue = {} local stack = {} for _, v in ipairs(list) do if isBinary(v) or isUnary(v) then local vPrec, topPrec if stack[#stack] then vPrec, topPrec = ops[v.tag].precedence, ops[stack[#stack][1].tag].precedence end while stack[#stack] and ((ops[v.tag].right and vPrec < topPrec) or (ops[v.tag].left and vPrec <= topPrec)) do queue[#queue + 1] = table.remove(stack) end stack[#stack + 1] = builder.Op(v) else queue[#queue + 1] = v end end -- dumping the stack for i = #stack, 1, -1 do queue[#queue + 1] = stack[i] end return queue end -- a temporary node local function MethodDecl(Index, Method) return { tag = 'MethodDecl', Index, Method } end -- a temporary node local hole = { tag = 'Hole' } -- a table mapping an operator to its builder function local opToBuilder = { ['or'] = builder.Or, ['and'] = builder.And, ['=='] = builder.Eq, ['~='] = builder.Ne, ['<='] = builder.Le, ['>='] = builder.Ge, ['<'] = builder.Lt, ['>'] = builder.Gt, ['..'] = builder.Concat, ['+'] = builder.Add, ['-'] = builder.Sub, ['*'] = builder.Mul, ['/'] = builder.Div, ['%'] = builder.Mod, ['not'] = builder.Not, ['#'] = builder.Len, ['unm'] = builder.Unm, ['^'] = builder.Pow, } -- CAPTURE TABLE ---------------------- -- the capture table. This table will be piped to parser.rules to build the -- AST. captures = { -- Statements Block = function (...) local Block = { ... } -- if the captured block has no statements, Block will contain { '' }. -- Detect that and return an empty table in that case if #Block == 1 and Block[1] == '' then return {} end return Block end, Assign = builder.Let, Do = builder.Do, While = builder.While, Repeat = builder.Repeat, If = builder.If, NumericFor = builder.Fornum, GenericFor = builder.Forin, GlobalFunction = function (FuncName, FuncBody) if FuncName.tag == 'MethodDecl' then -- it's a method declaration -- a method declaration like 'function b:o() <...> end' is equivalent to -- 'b.o = function (self) <...> end' FuncName.tag = 'Index' -- FuncName should be an Index node then local parlist = FuncBody[1] table.insert(parlist, 1, builder.Id 'self') end return builder.Let( { FuncName }, { FuncBody } ) end, LocalFunction = function (Name, FuncBody) return builder.Localrec( { Name }, { FuncBody }) end, LocalAssign = function (NameList, ExpList) return builder.Local(NameList, ExpList or {}) end, LastStat = function (STAT) if STAT == 'break' then return builder.Break() else if STAT == 'return' then STAT = {} end return builder.Return(unpack(STAT)) end end, -- Expressions -- Takes a list of tokens and operators and builds the appropriate tree node Exp = function (...) local list = { ... } if #list == 1 then return list[1] end local listRPN = toRPN(list) -- putting the list in RPN local stack = {} for _, v in ipairs(listRPN) do if v.tag == 'Op' and isUnary(v[1]) and not v[2] then if v[1].tag == 'Unm' then -- replacing Unm with Sub v[1].tag = 'Sub' end v[2] = table.remove(stack) elseif v.tag == 'Op' and isBinary(v[1]) and not v[2] and not v[3] then v[3] = table.remove(stack) v[2] = table.remove(stack) end stack[#stack + 1] = v end return stack[1] end, _PrefixExp = function (base, ...) for _, suffix in ipairs { ... } do -- filling the holes suffix[1] = base base = suffix end return base end, _PrefixExpParens = function (Exp) return builder.One(Exp) end, _PrefixExpDot = function (ID) -- the hole will be filled in _PrefixExp return builder.Index(hole, builder.String(ID)) end, _PrefixExpSquare = function (Exp) -- the hole will be filled in _PrefixExp return builder.Index(hole, Exp) end, _PrefixExpColon = function (ID, _PrefixExpArgs) -- the hole will be filled in _PrefixExp return builder.Method(hole, builder.String(ID), select(2, unpack(_PrefixExpArgs))) end, _PrefixExpArgs = function (Args) -- the hole will be filled in _PrefixExp return builder.Call(hole, unpack(Args)) end, -- Functions and closures FuncName = function (Name, ...) local base = Name for _, v in ipairs {...} do if type(v) == 'string' then -- it's a method -- using MethodDecl; this will be transformed into an Index node later base = MethodDecl(base, builder.String(v)) elseif v.tag == 'Index' then v[1] = base base = v end end return base end, FuncBody = function (ParList, Block) return builder.Function(ParList or {}, Block) end, Args = function (arg) if (not arg) or arg.tag then -- there's either one or no arguments arg = { arg } end return arg end, -- Lists VarList = grammar.Ct, NameList = grammar.Ct, ExpList = grammar.Ct, ParList = function (NameList, varargs) if NameList.tag == 'Dots' then -- the parameter list is just ... return { NameList } end NameList[#NameList + 1] = varargs return NameList end, -- Table constructors TableConstructor = function (FieldList) FieldList = FieldList or {} return builder.Table(unpack(FieldList)) end, -- fields FieldList = grammar.Ct, _FieldSquare = builder.Key, _FieldExp = grammar.C, _FieldID = function (ID, Exp) return builder.Key(builder.String(ID), Exp) end, -- Operators BinOp = function (op) return opToBuilder[op]() end, UnOp = function (op) if op == '-' then return opToBuilder['unm']() else return opToBuilder[op]() end end, -- Simple expressions NIL = builder.Nil, TRUE = builder.True, FALSE = builder.False, NUMBER = function (num) return builder.Number(tonumber(num)) end, STRING = function (str) return builder.String(scanner.string2text(str)) end, ID = grammar.C, Name = builder.Id, ['...'] = builder.Dots, -- Helper patterns EPSILON = function () return nil end, } -- the matching pattern local patt = P( parser.apply(nil, captures) ) -- Takes a string and checks if it's syntactically valid Lua 5.1 code. If it -- is, the corresponding AST is built and returned; if not, an error is thrown. function build(input) local result, msg = parser.check(input) if result then return patt:match(input) else error(msg) end end -- shamelessly stolen from Metalua: this is its table.tostring function, slightly -- adapted to substitute its dependencies for my own code. local function ast2string(t, ...) local LINE_MAX, PRINT_HASH = math.huge, true for _, x in ipairs {...} do if type(x) == "number" then LINE_MAX = x elseif x=="nohash" then PRINT_HASH = false end end 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. return type(x) == "string" and scanner.IDENTIFIER:match(x) end local function shallowcopy(t) local newt = {} for k, v in pairs(t) do newt[k] = v end return newt end -- Compute the number of chars it would require to display the table -- as 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, tracker) tracker = tracker or { } if x==nil then return #"nil" end if tracker[x] then return #_G.tostring(x) end local len = xlen_cache[x] if len then return len end local f = xlen_type[type(x)] if not f then return #_G.tostring(x) end len = f (x, tracker) 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 #_G.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, tracker) -- Circular references detection tracker = shallowcopy(tracker) tracker [adt] = true local has_tag = 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, tracker) + 2 end -- count surrounding barckets x = x + xlen (v, tracker) + 5 -- count " = " and ", " end end end for i = 1, alen do x = x + xlen (adt[i], tracker) + 2 end -- count ", " 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, indent, tracker) 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 (_G.tostring (adt)) end function x.string() acc (string.format ("%q", adt)) end function x.boolean() acc (adt and "true" or "false") end function x.table() tracker[adt] = true local has_tag = valid_id(adt.tag) local alen = #adt local has_arr = alen>0 local has_hash = false local new_indent 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 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 -- nothing: this an array-part pair, parsed later else -- hash-part pair -- Is it the first time we parse a hash pair? if not has_hash then acc "{ "; indent = current_offset 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, tracker) + #" = , " else expected_len = xlen (k, tracker) + xlen (v, tracker) + #"[] = , " 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, current_offset, tracker); acc "] = " end -- Print the value rec (v, current_offset, tracker) 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 "{ }"; return elseif has_tag and not has_hash and not has_arr then return -- nothing! else -- has_hash or has_arr 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 "{" and return before printing "}" acc (" "); rec (adt[1], new_indent, tracker); return elseif not has_hash then -- Braces required, but not opened by hash-part handler yet acc "{ "; indent = current_offset end -- 2nd pass: array-part if has_arr then rec (adt[1], new_indent, tracker) for i=2, alen do acc ", "; if current_offset + xlen (adt[i], { }) > LINE_MAX then acc_newline() end rec (adt[i], new_indent, tracker) end end acc " }" end end local y = x[type(adt)] if y then y() else acc(_G.tostring(adt)) end end rec(t, 0, { }) return table.concat (acc_list) end -- TESTING ---------------------------- local args = { ... } subject = args[1] or [=[ -- this comment won't be captured local a = 3 + -math.pi function b(ascii, ...) local t = { a = 1, ['b'] = {}, -3, .234 } while _VERSION > 5.1 do if x['a'] then x = false return x, 1, 2, 3, -4 else break end end end local function ascii2() return [[ascii2!]], whatever end ]=] print('subject:', '\n'..subject) print('result:', '\n'..ast2string(build(subject), 80, 'nohash'))lua-leg-0.1.3/examples/switch-macro.lua000077500000000000000000000144041204272643300177730ustar00rootroot00000000000000------------------------------------------------------------------------------- -- -- A preprocessor which transforms a switch construct (called 'match' here) -- into a sequence of if-elseif blocks. There is quite a discussion going on -- (again...) about adding switch statements in Lua -- (http://lua-users.org/lists/lua-l/2007-11/msg00099.html), so I decided to -- use it as a demonstration of Leg's capabilities. -- -- Author: Humberto Anjos -- -- $Id: switch-macro.lua,v 1.2 2007/11/19 13:34:47 hanjos Exp $ -- ------------------------------------------------------------------------------- -- imports local lpeg = require 'lpeg' local parser = require 'leg.parser' -- aliasing local P, V, C, Ct, Cs = lpeg.P, lpeg.V, lpeg.C, lpeg.Ct, lpeg.Cs -- command-line arguments local args = { ... } -- the code to parse subject = args[1] or [=[ match assert(io.open(file, '*a')) -- no cases here, nothing is generated end local args = { ... } -- before match match #args -- before the first case when 0 do print 'No arguments!' -- before the second case when 1 do -- after case checking print('only one argument: ', args[1]) when 2 do print('two arguments: ', args[1], args[2]) local a = tonumber(args[1]) + tonumber(args[2]) io.write(a) io.flush() else -- before else block for _, v in ipairs(args) do print(v) end -- after else end -- after match function eval(node, ...) match node.tag when 'Const' do return node.value when (node.left and node.right) and 'BinOp' do -- this comment won't appear in the generated code local op, left, right = node.op, node.left, node.right -- but this one will return op(left, right) -- and this one too when node.operand and 'UnOp' do local op, operand = node.op, node.operand return op(operand) else match isList(node) -- internal match statement when true do visit(node) when false do error 'Invalid node!' end end end ]=] -- After reading several proposals on the Lua discussion list, I decided to -- implement this one: -- -- match -- when do -- when do -- ... -- when do -- else -- end -- -- where , , ... are Lua expressions. -- -- The construct above will be converted into the following code: -- -- do -- local __temp__ = -- if __temp__ == () then -- elseif __temp__ == () then -- ... -- elseif __temp__ == () then -- else end -- end -- -- Implementation notes: -- -- * Technically, the local variable __temp__ should receive a name provably -- unique in the program. But, for this example, naming it __temp__ and -- restricting its scope will do the trick. -- -- * The local declaration will always be generated, even if there are no -- clauses to match. This is done because the expression in the local -- declaration might have side effects, which affect program semantics even -- if no match is made. -- -- * The default case, if present, must be the last clause. -- -- * If there's only the default case, the local declaration and the default -- case's block will be generated, without an enclosing if statement. -- -- * If there are no cases and no default case, only the local declaration will -- be generated. -- -- * Some comments are captured, some are not. The comments captured are those -- which are in the middle or at the end of a statement, and are -- captured along with the block. The other ones are matched as part of the -- spacing, and consequently not captured. -- -- * This is an obvious one, but: since the result is a series of if-elseif -- blocks, there is no fallthrough. -- -- * A reasonable improvement would be allowing a case clause to have several -- possible matches, generating something like -- if __temp__ == () or __temp__ == () then ... -- -- This is left as an exercise to the reader *shameless cop-out*. -- spacing local S = V'IGNORED' -- parser.rules.IGNORED or scanner.IGNORED could be used -- epsilon rule local EPSILON = V'EPSILON' / function () end -- new matching rule. Notice that the Block rule has no captures. local Match = (P'match' *S* C(V'Exp') *S* Ct((P'when' *S* C(V'Exp') *S* P'do' *S* V'Block')^0) *S* ((P'else' *S* V'Block') + EPSILON) *S* P'end') / function (exp, cases, default) if #cases == 0 then -- no case clauses if default then -- return the local declaration and the block return 'do local __temp__ = ('..exp..') '..default..' end' else -- generate just the local declaration return 'do local __temp__ = ('..exp..') end' end else -- there's at least one clause local str = 'do local __temp__ = ('..exp..') ' -- generating a new if or elseif block for i = 1, #cases - 3, 2 do str = str..'if __temp__ == ('..cases[i]..') then ' ..cases[i + 1]..' else' end -- the last case clause str = str..'if __temp__ == ('..cases[#cases - 1]..') then ' ..cases[#cases] if default then -- generate the else block str = str..' else '..default..' end' else -- no else, just finish it str = str..' end' -- end if-elseif chain end return str..' end' -- end do end end -- creating the LPeg pattern local oldStat, oldBlock = parser.rules.Stat, parser.rules.Block local MATCH = P( parser.apply { -- adding Match to the list of valid Statements Stat = oldStat + Match, -- the Block rule needs to be updated as well, in order to make the -- necessary substitutions to inner Match statements Block = Cs(oldBlock) } ) print('subject:', '\n'..subject) print('result:', '\n'..MATCH:match(subject))lua-leg-0.1.3/examples/syntax-highlighting.lua000077500000000000000000000052021204272643300213600ustar00rootroot00000000000000------------------------------------------------------------------------------- -- -- A syntax highlighter. Highlights some lexical patterns. Do -- lua syntax-highlighting.lua > syntax.html to see the result in a browser. -- -- Author: Humberto Anjos -- -- $Id: syntax-highlighting.lua,v 1.2 2007/11/19 13:34:47 hanjos Exp $ -- ------------------------------------------------------------------------------- -- import the fellas local lpeg = require 'lpeg' local leg = require 'leg' local scanner = leg.scanner local parser = leg.parser -- aliasing... local Cs, V, P = lpeg.Cs, lpeg.V, lpeg.P -- arguments local args = { ... } -- the code to parse subject = args[1] or [==[ local a, b = x'BOOM, baby!' + 2 function hit_it(t) -- and quit it local points = 0 for i, v in ipairs(t) do print "Miller time!" points = points + (math.random() < 0.5) and 3 or 2 end return points end --[[ I need a multi-line comment for good measure. Hum... I want my baby back, baby back, baby back... I want my baby back, baby back, baby back... CHILI! Baby back ribs! I want my baby back ribs... ]] _G.print [=[ Attempting to print out a long, multi-level string to [[ demonstrate the ineffable, zen-like beauty of this code. ]] So many diferent ways to do this, but all is one in the Tao. Wait a minute, what the hell am I talking about? ]=] ]==] -- now the magic happens.... -- the colors... local commentColor = '#808080' -- gray local stringColor = '#008000' -- dark green local numberColor = '#B00000' -- red local keywordColor = '#0000FF' -- blue -- the patterns... local COMMENT = scanner.COMMENT / function (c) return ' '..c..'' end local STRING = scanner.STRING / function (c) return ''..c..'' end local NUMBER = scanner.NUMBER / function (c) return ''..c..'' end local KEYWORD = scanner.KEYWORD / function (c) return ''..c..'' end -- opening tags local BOF = scanner.BOF / function () return '
' end

-- closing tags
local EOF = scanner.EOF / function () return '
' end -- this is here just to keep identifiers like bin2c from being parsed by NUMBER local ID = scanner.IDENTIFIER -- the substitution pattern. BOF and EOF are there to ensure that the -- opening and closing tags are there to make the result a valid HTML page. local patt = Cs( BOF* (COMMENT + STRING + ID + NUMBER + KEYWORD + 1)^0 *EOF ) -- voil! print(patt:match(subject)) lua-leg-0.1.3/examples/vararg-indexing.lua000077500000000000000000000042211204272643300204540ustar00rootroot00000000000000------------------------------------------------------------------------------- -- -- A preprocessor which uses Leg to transform ...[] into -- select(, ...). -- -- Author: Humberto Anjos -- -- $Id: vararg-indexing.lua,v 1.2 2007/11/19 13:34:47 hanjos Exp $ -- ------------------------------------------------------------------------------- -- ye olde imports local lpeg = require 'lpeg' local parser = require 'leg.parser' -- some aliasing to save my poor fingertips local V, P, Cs = lpeg.V, lpeg.P, lpeg.Cs -- argument processing local args = { ... } -- the code to parse subject = args[1] or [=[ local arg1, arg2, arg3 = ...[1], ... [ -2 +x[[whatever, man]]^t[5] ], ... -- Oh my G0dZ, a comment in the middle !!1!one!1! This will disappear [-(-3)] if do_or_die() then -- inside a block return ...[BOOM_baby(...[2], 'boink!')] -- inside an expression end -- ...['inside a comment'] a = " ...['inside a string!'] " ]=] -- spacing rule local S = parser.rules.IGNORED -- scanner.IGNORED or V'IGNORED' could be used -- a pattern which matches any instance of ...[] and returns -- 'select(, ...)'. You need parser.apply because the definition of Exp is -- recursive, and needs roughly half of Lua's grammar to work. One could try to -- work out which rules are actually needed, but just using the whole damn -- thing is so much easier... local oldExp = parser.rules.Exp local VARARG = P( parser.apply ( { -- the rule table -- matching ...[]. We'll use lpeg.Cs for the substitution. VarargIndex = V'...' *S* V'[' *S* V'Exp' *S* V']', -- VarargIndex is now a valid subexpression. Using lpeg.Cs ensures that -- inner VarargIndexes will be substituted as well. VarargIndex must be -- matched before oldExp or the ... will be understood as a normal -- ellipsis. Exp = Cs(V'VarargIndex' + oldExp), }, { -- the capture table VarargIndex = function (exp) return 'select('..exp..', ...)' end }) ) -- a pattern which does the substitution with Cs local ALL = Cs(VARARG) -- printing the results print('subject:', '\n'..subject) print('result:', '\n'..ALL:match(subject)) lua-leg-0.1.3/release000066400000000000000000000013751204272643300144150ustar00rootroot00000000000000$Id: release,v 1.2 2007/11/26 18:41:51 hanjos Exp $ Version: 0.1.3 ============== Updated to work with LPeg 0.10 Version: 0.1.2 ============== API === * grammar.lua received two new utility functions: anyOf and listOf Bugs Fixed ========== * adding scanner's keywords e symbols to parser's rules ended up altering scanner.keywords and scanner.symbols as well; using grammar.complete instead of grammar.apply does the trick. * grammar.apply was inadvertently piping the captures to grammar instead of rules if rules was nil. Documentation ============= * changes in the documentation generator made the new docs slightly different; no big conceptual change. * an improved README file * a COPYRIGHT file with Leg's license * and this release filelua-leg-0.1.3/src/000077500000000000000000000000001204272643300136335ustar00rootroot00000000000000lua-leg-0.1.3/src/grammar.lua000077500000000000000000000261671204272643300160030ustar00rootroot00000000000000--[=[ <% project.title = "grammar" project.description = "LPeg grammar manipulation" project.version = "0.1.2" project.date = _G.os.date'%B %d, %Y' project.modules = { 'grammar', 'parser', 'scanner' } %> # Description This module defines a handful of operations which can be applied to [http://www.inf.puc-rio.br/~roberto/lpeg.html LPeg] patterns and grammars in general. # Dependencies * [http://www.inf.puc-rio.br/~roberto/lpeg.html LPeg]. # Operations ## Piping Pattern matching dissociates the notion of *matching* from the notion of *capturing*: matching checks if a given string follows a certain pattern, and capturing generates values according to the match made. This division allows interesting possibilities: * different problems can be solved by applying different captures to the same grammar; * captures may be defined separately; * captures may be done on top of other captures. Accounting for the first and second bullets, the grammar given in [parser.html parser] has no captures, enabling the user to reuse it to solve any problems that require a Lua grammar. One good example is documentation generation, described in a little more detail [#section_Example below]. The third bullet depicts a more interesting idea: a capture might take the result of another capture as input, doing a further transformation of the original data. This capture chaining, with the latter ones using the former's output as its input, is very similar to [http://en.wikipedia.org/wiki/Pipeline_%28Unix%29 Unix pipelines], so this mechanism was named **piping**. ## Completing With piping, several levels of captures can be chained together up to the most appropriate for the task at hand. Yet some levels might require extra rules, and modifications to existing ones, to ensure proper matching. To avoid manual copying, the new grammar should redefine only the necessary rules, copying the rest from the older grammar. This action is dubbed **completing**. ## Applying Once a new rule set is created and [#section_Completing completed], and all captures are correctly [#section_Piping piped], all that's left is to put them together, a process called **applying**. The result is a grammar ready for [http://www.inf.puc-rio.br/~roberto/lpeg.html#lpeg lpeg.P] consumption, whose pattern will return the intended result when a match is made. ## Example Let's consider the problem of documenting a Lua module. In this case, comments must be captured before every function declaration when in the outermost scope: `` -- -- the code to parse subject = %[%[ -- -- Calculates the sum a+b. -- -- An extra line. function sum (a, b) -- -- code end -- -- f1: assume a variable assignment is not a proper declaration for an -- -- exported function f1 = function () -- -- code end while true do -- -- this function is not in the outermost scope function aux() end end function something:other(a, ...) -- -- a global function without comments end %]%] `` In the code above only `sum` and `something:other` should be documented, as `f1` isn't properly (by our standards) declared and `aux` is not in the outermost scope. By combining [http://www.inf.puc-rio.br/~roberto/lpeg.html LPeg] and the modules [scanner.html scanner], [parser.html parser] and [grammar.html grammar], this specific problem can be solved as follows: `` -- -- change only the initial rule and make no captures patt = grammar.apply(parser.rules, scanner.COMMENT^-1 %* lpeg.V'GlobalFunction', nil) -- -- transform the new grammar into a LPeg pattern patt = lpeg.P(patt) -- -- making a pattern that matches any Lua statement, also without captures Stat = lpeg.P( grammar.apply(parser.rules, lpeg.V'Stat', nil) ) -- -- a pattern which matches function declarations and skips statements in -- -- inner scopes or undesired tokens patt = (patt + Stat + scanner.ANY)^0 -- -- matching a string patt:match(subject) `` These are the relevant rules in [parser.html#section_The_Grammar the grammar]: `` GlobalFunction = 'function' %* FuncName %* FuncBody FuncName = ID %* ('.' %* ID)^0 %* (':' %* ID)^-1 FuncBody = '(' %* (ParList + EPSILON) %* ')' %* Block %* 'end' ParList = NameList %* (',' %* '...')^-1 NameList = ID %* (',' %* ID)^0 ID = scanner.ID EPSILON = lpeg.P(true) `` It may seem that `ParList + EPSILON` could be substituted for `ParList^-1` (optionally match `ParList`), but then no captures would be made for empty parameter lists, and `GlobalFunction` would get all strings matched by `FuncBody`. The `EPSILON` rule acts in this manner as a placeholder in the argument list, avoiding any argument list processing in the capture function. Since no captures are being made, [http://www.inf.puc-rio.br/~roberto/lpeg.html#basic lpeg.match] doesn't return anything interesting. Here are some possible captures: `` -- -- some interesting captures bundled up in a table. Note that the table keys -- -- match the grammar rules we want to add captures to. Whatever rules aren't in -- -- the rules table below will come from parser.rules . captures = { %[1%] = function (...) -- the initial rule return '<function>'..table.concat{...}..'</function>' end, GlobalFunction = function (name, parlist) return '<name>'..name..'</name><parlist>'..(parlist or '')..'</parlist>' end, FuncName = grammar.C, -- capture the raw text ParList = grammar.C, -- capture the raw text COMMENT = scanner.comment2text, -- remove the comment trappings } -- -- spacing rule local S = scanner.SPACE ^ 0 -- -- rules table rules = { %[1%] = ((lpeg.V'COMMENT' %*S) ^ 0) %*S%* lpeg.V'GlobalFunction', COMMENT = scanner.COMMENT, } -- -- building the new grammar and adding the captures patt = lpeg.P( grammar.apply(parser.rules, rules, captures) ) -- -- a pattern that matches a sequence of patts and concatenates the results patt = (patt + Stat + scanner.ANY)^0 / function(...) return table.concat({...}, '\n\n') -- some line breaks for easier reading end -- -- finally, matching a string print(patt:match(subject)) `` `FuncBody` needs no captures, as `Block` and all its non-terminals have none; it just needs to pass along any captures made by `ParList`. `NameList` and `ID` also have no captures, and the whole subject string is passed further. The printed result is:
<function>Calculates the sum a+b. An extra line.<name>sum</name><parlist>a, b</parlist></function>

<function><name>something:other</name><parlist>a, ...</parlist></function>
--]=] -- $Id: grammar.lua,v 1.3 2007/11/26 18:41:51 hanjos Exp $ -- basic functions local assert = assert local pairs = pairs local type = type -- imported modules local lpeg = require 'lpeg' -- imported functions local P, V = lpeg.P, lpeg.V -- module declaration module 'leg.grammar' --[[ Returns a pattern which matches any of the patterns received. **Example:** `` local g, s, m = require 'leg.grammar', require 'leg.scanner', require 'lpeg' -- -- match numbers or operators, capture the numbers print( (g.anyOf { '+', '-', '%*', '/', m.C(s.NUMBER) }):match '34.5@23 %* 56 / 45 - 45' ) -- --> prints 34.5 `` **Parameters:** * `list`: a list of zero or more LPeg patterns or values which can be fed to [http://www.inf.puc-rio.br/~roberto/lpeg.html#lpeg lpeg.P]. **Returns:** * a pattern which matches any of the patterns received. --]] function anyOf(list) local patt = P(false) for i = 1, #list, 1 do patt = P(list[i]) + patt end return patt end --[=[ Returns a pattern which matches a list of `patt`s, separated by `sep`. **Example:** matching comma-separated values: `` local g, m = require 'leg.grammar', require 'lpeg' -- -- separator local sep = m.P',' + m.P'\n' -- -- element: anything but sep, capture it local elem = m.C((1 - sep)^0) -- -- pattern local patt = g.listOf(elem, sep) -- -- matching print( patt:match %[%[a, b, 'christmas eve' d, evening; mate! f%]%]) -- --> prints out "a b 'christmas eve' d evening; mate! f" `` **Parameters:** * `patt`: a LPeg pattern. * `sep`: a LPeg pattern. **Returns:** * the following pattern: ``patt %* (sep %* patt)^0`` --]=] function listOf(patt, sep) patt, sep = P(patt), P(sep) return patt * (sep * patt)^0 end --[[ A capture function, made so that `patt / C` is equivalent to `m.C(patt)`. It's intended to be used in capture tables, such as those required by [#function_pipe pipe] and [#function_apply apply]. --]] function C(...) return ... end --[[ A capture function, made so that `patt / Ct` is equivalent to `m.Ct(patt)`. It's intended to be used in capture tables, such as those required by [#function_pipe pipe] and [#function_apply apply]. --]] function Ct(...) return { ... } end --[[ Creates a shallow copy of `grammar`. **Parameters:** * `grammar`: a regular table. **Returns:** * a newly created table, with `grammar`'s keys and values. --]] function copy(grammar) local newt = {} for k, v in pairs(grammar) do newt[k] = v end return newt end --[[ [#section_Completing Completes] `dest` with `orig`. **Parameters:** * `dest`: the new grammar. Must be a table. * `orig`: the original grammar. Must be a table. **Returns:** * `dest`, with new rules inherited from `orig`. --]] function complete (dest, orig) for rule, patt in pairs(orig) do if not dest[rule] then dest[rule] = patt end end return dest end --[[ [#section_Piping Pipes] the captures in `orig` to the ones in `dest`. `dest` and `orig` should be tables, with each key storing a capture function. Each capture in `dest` will be altered to use the results for the matching one in `orig` as input, using function composition. Should `orig` possess keys not in `dest`, `dest` will copy them. **Parameters:** * `dest`: a capture table. * `orig`: a capture table. **Returns:** * `dest`, suitably modified. --]] function pipe (dest, orig) for k, vorig in pairs(orig) do local vdest = dest[k] if vdest then dest[k] = function(...) return vdest(vorig(...)) end else dest[k] = vorig end end return dest end --[[ [#section_Completing Completes] `rules` with `grammar` and then [#Applying applies] `captures`. `rules` can either be: * a single pattern, which is taken to be the new initial rule, * a possibly incomplete LPeg grammar, as per [#function_complete complete], or * `nil`, which means no new rules are added. `captures` can either be: * a capture table, as per [#function_pipe pipe], or * `nil`, which means no captures are applied. **Parameters:** * `grammar`: the old grammar. It stays unmodified. * `rules`: optional, the new rules. * `captures`: optional, the final capture table. **Returns:** * `rules`, suitably augmented by `grammar` and `captures`. --]] function apply (grammar, rules, captures) if rules == nil then rules = {} elseif type(rules) ~= 'table' then rules = { rules } end complete(rules, grammar) if type(grammar[1]) == 'string' then rules[1] = lpeg.V(grammar[1]) end if captures ~= nil then assert(type(captures) == 'table', 'captures must be a table') for rule, cap in pairs(captures) do rules[rule] = rules[rule] / cap end end return rules end lua-leg-0.1.3/src/init.lua000077500000000000000000000043311204272643300153050ustar00rootroot00000000000000--[[ <% project.title = "Leg" project.description = "LPeg-powered Lua 5.1 grammar" project.version = "0.1.2" project.date = _G.os.date'%B %d, %Y' project.modules = { 'grammar', 'parser', 'scanner' } %> # Overview Leg is a Lua library which offers a complete Lua 5.1 grammar, along with some functions to use and modify it. Some examples of projects which could benefit from Leg are a syntax highlighter, a Luadoc-style document generator, and a macro preprocessor. Leg uses [http://www.inf.puc-rio.br/~roberto/lpeg.html LPeg] for pattern matching, and returns [http://www.inf.puc-rio.br/~roberto/lpeg.html LPeg] patterns for user manipulation. Leg is available under the same [#section_License license] as Lua 5.1. # Dependencies * [http://www.inf.puc-rio.br/~roberto/lpeg.html LPeg] # Download Leg can be downloaded from its [http://luaforge.net/projects/leg/ LuaForge page]. # Credits This project is maintained by Humberto Anjos, and was adapted from an earlier project done with Francisco Sant'Anna. # License Copyright © 2007 Humberto Saraiva Nazareno dos Anjos. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --]] -- $Id: init.lua,v 1.2 2007/11/26 18:41:51 hanjos Exp $ require 'leg.grammar' require 'leg.scanner' require 'leg.parser' module 'leg'lua-leg-0.1.3/src/parser.lua000077500000000000000000000360771204272643300156520ustar00rootroot00000000000000--[[ <% project.title = "parser" project.description = "Lua 5.1 parser" project.version = "0.1.2" project.date = _G.os.date'%B %d, %Y' project.modules = { 'grammar', 'parser', 'scanner' } %> # Description Pairing with [scanner.html scanner], this module exports Lua 5.1's syntactic rules as a grammar. # Dependencies * [http://www.inf.puc-rio.br/~roberto/lpeg.html LPeg]; * [grammar.html grammar]; and * [scanner.html scanner]. # The Grammar The [#variable_rules rules] variable implements the official [http://www.lua.org/manual/5.1/manual.html#8 Lua 5.1 grammar]. It includes all keyword and symbol rules in [scanner.html scanner], as well as the `CHUNK` rule, which matches a complete Lua source file. [#variable_rules rules] is a table with [http://www.inf.puc-rio.br/~roberto/lpeg.html#grammar open references], not yet a LPeg pattern; to create a pattern, it must be given to `[http://www.inf.puc-rio.br/~roberto/lpeg.html#lpeg lpeg.P]`. This is done to enable users to modify the grammar to suit their particular needs. [grammar.html grammar] provides a small API for this purpose. The code below shows the Lua 5.1 grammar in LPeg, minus spacing issues. The following convention is used for rule names: * **TOKENRULE**: token rules (which represent terminals) are in upper case when applicable (ex. `+, WHILE, NIL, ..., THEN, {, ==`). * **GrammarRule**: the main grammar rules (non-terminals): Examples are `Chunk`, `FuncName`, `BinOp`, and `TableConstructor`. * **_GrammarRule**: subdivisions of the main rules, introduced to ease captures. Examples are `_SimpleExp`, `_PrefixExpParens` and `_FieldExp`. * **METARULE**: grammar rules with a special semantic meaning, to be used for capturing in later modules, like `BOF`, `EOF` and `EPSILON`. `` rules = { -- -- See peculiarities below IGNORED = scanner.IGNORED -- used as spacing, not depicted below EPSILON = lpeg.P(true) EOF = scanner.EOF -- end of file BOF = scanner.BOF -- beginning of file Name = ID -- -- Default initial rule %[1%] = CHUNK CHUNK = scanner.BANG^-1 %* Block Chunk = (Stat %* ';'^-1)^0 %* (LastStat %* ';'^-1)^-1 Block = Chunk -- -- STATEMENTS Stat = Assign + FunctionCall + Do + While + Repeat + If + NumericFor + GenericFor + GlobalFunction + LocalFunction + LocalAssign Assign = VarList %* '=' %* ExpList Do = 'do' %* Block %* 'end' While = 'while' %* Exp %* 'do' %* Block %* 'end' Repeat = 'repeat' %* Block %* 'until' %* Exp If = 'if' %* Exp %* 'then' %* Block %* ('elseif' %* Exp %* 'then' %* Block)^0 %* (('else' %* Block) + EPSILON) %* 'end' NumericFor = 'for' %* Name %* '=' %* Exp %* ',' %* Exp %* ((',' %* Exp) + EPSILON) %* 'do' %* Block %* 'end' GenericFor = 'for' %* NameList %* 'in' %* ExpList %* 'do' %* Block %* 'end' GlobalFunction = 'function' %* FuncName %* FuncBody LocalFunction = 'local' %* 'function' %* Name %* FuncBody LocalAssign = 'local' %* NameList %* ('=' %* ExpList)^-1 LastStat = 'return' %* ExpList^-1 + 'break' -- -- LISTS VarList = Var %* (',' %* Var)^0 NameList = Name %* (',' %* Name)^0 ExpList = Exp %* (',' %* Exp)^0 -- -- EXPRESSIONS Exp = _SimpleExp %* (BinOp %* _SimpleExp)^0 _SimpleExp = 'nil' + 'false' + 'true' + Number + String + '...' + Function + _PrefixExp + TableConstructor + (UnOp %* _SimpleExp) _PrefixExp = ( Name a Var + _PrefixExpParens only an expression ) %* ( _PrefixExpSquare a Var + _PrefixExpDot a Var + _PrefixExpArgs a FunctionCall + _PrefixExpColon a FunctionCall ) ^ 0 -- -- Extra rules for semantic actions: _PrefixExpParens = '(' %* Exp %* ')' _PrefixExpSquare = '[' %* Exp %* ']' _PrefixExpDot = '.' %* ID _PrefixExpArgs = Args _PrefixExpColon = ':' %* ID %* _PrefixExpArgs -- -- These rules use an internal trick to be distingished from _PrefixExp Var = _PrefixExp FunctionCall = _PrefixExp -- -- FUNCTIONS Function = 'function' %* FuncBody FuncBody = '(' %* (ParList+EPSILON) %* ')' %* Block %* 'end' FuncName = Name %* _PrefixExpDot^0 %* ((':' %* ID)+EPSILON) Args = '(' %* (ExpList+EPSILON) %* ')' + TableConstructor + String ParList = NameList %* (',' %* '...')^-1 + '...' -- -- TABLES TableConstructor = '{' %* (FieldList+EPSILON) %* '}' FieldList = Field %* (FieldSep %* Field)^0 %* FieldSep^-1 FieldSep = ',' + ';' -- -- Extra rules for semantic actions: _FieldSquare = '[' %* Exp %* ']' %* '=' %* Exp _FieldID = ID %* '=' %* Exp _FieldExp = Exp -- -- OPERATORS BinOp = '+' + '-' + '%*' + '/' + '^' + '%' + '..' + '<' + '<=' + '>' + '>=' + '==' + '~=' + 'and' + 'or' UnOp = '-' + 'not' + '#' -- -- ...plus scanner's keywords and symbols } `` The implementation has certain peculiarities that merit clarification: * Spacing is matched only between two tokens in a rule, never at the beginning or the end of a rule. * `EPSILON` matches the empty string, which means that it always succeeds without consuming input. Although `rule + EPSILON` can be changed to `rule^-1` without any loss of syntactic power, `EPSILON` was introduced in the parser due to it's usefulness as a placeholder for captures. * `BOF` and `EOF` are rules used to mark the bounds of a parsing match, and are useful for semantic actions. * `Name` versus `ID`: the official Lua grammar doesn't distinguish between them, as their syntax is exactly the same (Lua identifiers). But semantically `Name` is a variable identifier, and `ID` is used with different meanings in `_FieldID`, `FuncName`, `_PrefixExpColon` and `_PrefixExpDot`. * In Lua's [http://www.lua.org/manual/5.1/manual.html#8 original extended BNF grammar], `Var` and `FunctionCall` are defined using left recursion, which is unavailable in PEGs. In this implementation, the problem was solved by modifying the PEG rules to eliminate the left recursion, and by setting some markers (with some LPeg chicanery) to ensure the proper pattern is being used. --]] -- $Id: parser.lua,v 1.3 2007/11/26 18:41:51 hanjos Exp $ -- basic modules local _G = _G local table = table local string = string -- basic functions local error = error local require = require -- imported modules local m = require 'lpeg' local scanner = require 'leg.scanner' local grammar = require 'leg.grammar' -- module declaration module 'leg.parser' -- Searches for the last substring in s which matches pattern local function rfind(s, pattern, init, finish) init = init or #s finish = finish or 1 for i = init, finish, -1 do local lfind, rfind = string.find(s, pattern, i) if lfind and rfind then return lfind, rfind end end return nil end -- Counts the number of lines (separated by *'\n'*) in `subject`. -- Viliam Kubis (01.03.2011) - replaced use of obsolete and deprecated lpeg.Ca with new lpeg.Cf local function lines (subject) local inc = function (acc,arg) return acc + 1 end local L = m.Cf( m.Cc(1) * (m.P'\n' + m.P(1)) ^0, inc ) return L:match(subject) end -- a little LPeg trick to enable a limited form of left recursion local prefix -- sets a prefix used to distinguish between Var and FunctionCall local setPrefix = function (p) return function (_,i) prefix = p return i end end -- matches Var and FunctionCall from _PrefixExp local matchPrefix = function (p) return function (_, i) return (prefix == p) and i end end -- throws an error if the grammar rule `rule` doesn't match -- `desc` is there for a slightly better error message local function CHECK(rule, desc) patt, desc = m.V(rule), desc or 'chunk' return patt + m.P(function (s, i) local line = lines(s:sub(1, i)) local vicinity = s:sub(i-5, i+5):gsub("\n", "") error('Malformed '..desc..' in line '..line..', near "'..vicinity..'": a "'..rule:lower()..'" is missing!', 0) end) end -- this will be used a lot below local S, listOf, anyOf = m.V'IGNORED', grammar.listOf, grammar.anyOf --[[ A table holding the Lua 5.1 grammar. See [#section_The_Grammar The Grammar] for an extended explanation. --]] -- <% exp = 'table' %> rules = { IGNORED = scanner.IGNORED -- seen as S below , EPSILON = m.P(true) , EOF = scanner.EOF , BOF = scanner.BOF , NUMBER = scanner.NUMBER , ID = scanner.IDENTIFIER , STRING = scanner.STRING , Name = m.V'ID' -- CHUNKS , [1] = m.V'CHUNK' , CHUNK = scanner.BANG^-1 * m.V'Block' , Chunk = (S* m.V'Stat' *S* m.V';'^-1)^0 *S* (m.V'LastStat' *S* m.V';'^-1)^-1 , Block = m.V'Chunk' -- STATEMENTS , Stat = m.V'Assign' + m.V'FunctionCall' + m.V'Do' + m.V'While' + m.V'Repeat' + m.V'If' + m.V'NumericFor' + m.V'GenericFor' + m.V'GlobalFunction' + m.V'LocalFunction' + m.V'LocalAssign' , Assign = m.V'VarList' *S* m.V'=' *S* m.V'ExpList' , Do = m.V'DO' *S* m.V'Block' *S* CHECK('END', 'do block') , While = m.V'WHILE' *S* m.V'Exp' *S* CHECK('DO', 'while loop') *S* m.V'Block' *S* CHECK('END', 'while loop') , Repeat = m.V'REPEAT' *S* m.V'Block' *S* CHECK('UNTIL', 'repeat loop') *S* m.V'Exp' , If = m.V'IF' *S* m.V'Exp' *S* CHECK('THEN', 'then block') *S* m.V'Block' * (S* m.V'ELSEIF' *S* m.V'Exp' *S* CHECK('THEN', 'elseif block') *S* m.V'Block')^0 * ((S* m.V'ELSE' * m.V'Block') + m.V'EPSILON') * S* CHECK('END', 'if statement') , NumericFor = m.V'FOR' *S* m.V'Name' *S* m.V'=' *S* m.V'Exp' *S* m.V',' *S* m.V'Exp' *S* ((m.V',' *S* m.V'Exp') + m.V'EPSILON') *S* CHECK('DO', 'numeric for loop') *S* m.V'Block' *S* CHECK('END', 'numeric for loop') , GenericFor = m.V'FOR' *S* m.V'NameList' *S* m.V'IN' *S* m.V'ExpList' *S* CHECK('DO', 'generic for loop') *S* m.V'Block' *S* CHECK('END', 'generic for loop') , GlobalFunction = m.V'FUNCTION' *S* m.V'FuncName' *S* m.V'FuncBody' , LocalFunction = m.V'LOCAL' *S* m.V'FUNCTION' *S* m.V'Name' *S* m.V'FuncBody' , LocalAssign = m.V'LOCAL' *S* m.V'NameList' * (S* m.V'=' *S* m.V'ExpList')^-1 , LastStat = m.V'RETURN' * (S* m.V'ExpList')^-1 + m.V'BREAK' -- LISTS --, VarList = m.V'Var' * (S* m.V',' *S* m.V'Var')^0 --, NameList = m.V'Name' * (S* m.V',' *S* m.V'Name')^0 --, ExpList = m.V'Exp' * (S* m.V',' *S* m.V'Exp')^0 , VarList = listOf(m.V'Var' , S* m.V',' *S) , NameList = listOf(m.V'Name', S* m.V',' *S) , ExpList = listOf(m.V'Exp' , S* m.V',' *S) -- EXPRESSIONS , Exp = m.V'_SimpleExp' * (S* m.V'BinOp' *S* m.V'_SimpleExp')^0 , _SimpleExp = m.V'NIL' + m.V'FALSE' + m.V'TRUE' + m.V'NUMBER' + m.V'STRING' + m.V'...' + m.V'Function' + m.V'_PrefixExp' + m.V'TableConstructor' + (m.V'UnOp' *S* m.V'_SimpleExp') , _PrefixExp = ( m.V'Name' * setPrefix'Var' -- Var + m.V'_PrefixExpParens' * setPrefix(nil)) -- removes last prefix * (S* ( m.V'_PrefixExpSquare' * setPrefix'Var' -- Var + m.V'_PrefixExpDot' * setPrefix'Var' -- Var + m.V'_PrefixExpArgs' * setPrefix'Call' -- FunctionCall + m.V'_PrefixExpColon' * setPrefix'Call' -- FunctionCall )) ^ 0 , _PrefixExpParens = m.V'(' *S* m.V'Exp' *S* CHECK(')', 'parenthesized expression') , _PrefixExpSquare = m.V'[' *S* m.V'Exp' *S* CHECK(']', 'index field') , _PrefixExpDot = m.V'.' *S* m.V'ID' , _PrefixExpArgs = m.V'Args' , _PrefixExpColon = m.V':' *S* m.V'ID' *S* m.V'_PrefixExpArgs' -- solving the left recursion problem , Var = m.V'_PrefixExp' * matchPrefix'Var' , FunctionCall = m.V'_PrefixExp' * matchPrefix'Call' -- FUNCTIONS , Function = m.V'FUNCTION' *S* m.V'FuncBody' , FuncBody = m.V'(' *S* (m.V'ParList'+m.V'EPSILON') *S* CHECK(')', 'parameter list') *S* m.V'Block' *S* CHECK('END', 'function body') , FuncName = m.V'Name' * (S* m.V'_PrefixExpDot')^0 * ((S* m.V':' *S* m.V'ID') + m.V'EPSILON') , Args = m.V'(' *S* (m.V'ExpList'+m.V'EPSILON') *S* CHECK(')', 'argument list') + m.V'TableConstructor' + m.V'STRING' , ParList = m.V'NameList' * (S* m.V',' *S* m.V'...')^-1 + m.V'...' -- TABLES , TableConstructor = m.V'{' *S* (m.V'FieldList'+m.V'EPSILON') *S* CHECK('}', 'table constructor') , FieldList = m.V'Field' * (S* m.V'FieldSep' *S* m.V'Field')^0 * (S* m.V'FieldSep')^-1 , Field = m.V'_FieldSquare' + m.V'_FieldID' + m.V'_FieldExp' , _FieldSquare = m.V'[' *S* m.V'Exp' *S* CHECK(']', 'index field') *S* CHECK('=', 'field assignment') *S* m.V'Exp' , _FieldID = m.V'ID' *S* m.V'=' *S* m.V'Exp' , _FieldExp = m.V'Exp' , FieldSep = m.V',' + m.V';' -- OPERATORS , BinOp = m.V'+' + m.V'-' + m.V'*' + m.V'/' + m.V'^' + m.V'%' + m.V'..' + m.V'<' + m.V'<=' + m.V'>' + m.V'>=' + m.V'==' + m.V'~=' + m.V'AND' + m.V'OR' , UnOp = m.V'-' + m.V'NOT' + m.V'#' } -- puts all the keywords and symbols to the grammar grammar.complete(rules, scanner.keywords) grammar.complete(rules, scanner.symbols) --[[ Checks if `input` is valid Lua source code. **Parameters:** * `input`: a string containing Lua source code. **Returns:** * `true`, if `input` is valid Lua source code, or `false` and an error message if the matching fails. --]] function check(input) local builder = m.P(rules) local result = builder:match(input) if result ~= #input + 1 then -- failure, build the error message local init, _ = rfind(input, '\n*', result - 1) local _, finish = string.find(input, '\n*', result + 1) init = init or 0 finish = finish or #input local line = lines(input:sub(1, result)) local vicinity = input:sub(init + 1, finish) return false, 'Syntax error at line '..line..', near "'..vicinity..'"' end return true end --[[ Uses [grammar.html#function_apply grammar.apply] to return a new grammar, with `captures` and extra rules. [#variable_rules rules] stays unmodified. **Parameters:** * `extraRules`: optional, the new and modified rules. See [grammar.html#function_apply grammar.apply] for the accepted format. * `captures`: optional, the desired captures. See [grammar.html#function_apply grammar.apply] for the accepted format. **Returns:** * the extended grammar. --]] function apply(extraRules, captures) return grammar.apply(rules, extraRules, captures) end lua-leg-0.1.3/src/scanner.lua000077500000000000000000000266771204272643300160140ustar00rootroot00000000000000--[=[ <% project.title = "scanner" project.description = "Lua 5.1 lexical patterns" project.version = "0.1.2" project.date = _G.os.date "%B %d, %Y" project.modules = { 'grammar', 'parser', 'scanner' } %> # Description This module exports several Lua lexical patterns, all implemented in [http://www.inf.puc-rio.br/~roberto/lpeg.html LPeg]. None of them have captures. # Dependencies * [http://www.inf.puc-rio.br/~roberto/lpeg.html LPeg] # Example The following example lists all the tokens in a Lua script: `` local lpeg = require 'lpeg' local scanner = require 'leg.scanner' -- -- this pattern captures all tokens, ignores spaces and comments, -- -- and matches with end of file, giving an error otherwise patt = (lpeg.C(scanner.TOKEN) + scanner.SPACE + scanner.COMMENT)^0 %* (scanner.EOF + scanner.error'invalid character') patt = lpeg.Ct(patt) -- -- opens the file passed as parameter and tries to match with patt f = assert(io.open(arg%[1%])) -- -- a table storing all the tokens ALL = patt:match(f:read'%*a') f:close() -- -- dumps all tokens on screen for _, tk in ipairs(ALL) do print(tk) end `` --]=] -- $Id: scanner.lua,v 1.2 2007/11/26 18:41:51 hanjos Exp $ -- basic modules local _G = _G local string = string -- basic functions local assert = assert local pairs = pairs local print = print local require = require local tonumber = tonumber local type = type -- imported modules local m = require 'lpeg' -- module declaration module 'leg.scanner' ------------------------------ PATTERNS --------------------------------------- -- digit pattern local N = m.R'09' -- alphanumeric pattern local AZ = m.R('__','az','AZ','\127\255') -- llex.c uses isalpha() -- punctuation pattern. Used only in text2string local PUNCTUATION = m.S'!@$&|?~`' ------------------------------ FUNCTIONS -------------------------------------- -- Counts the number of lines (separated by '\n') in subject. -- Viliam Kubis (01.03.2011) - replaced use of obsolete and deprecated lpeg.Ca with new lpeg.Cf local function lines (subject) local inc = function (acc,arg) return acc + 1 end local L = m.Cf( m.Cc(1) * (m.P'\n' + m.P(1)) ^0, inc ) return L:match(subject) end --[[ Returns a function which throws lexical errors. **Parameters:** * `msg`: the message to be concatenated to the error message. **Returns:** * A function built to be used as a [http://www.inf.puc-rio.br/~roberto/lpeg.html#lpeg LPeg pattern], which will throw an error when matched. Usage example: `` patt = intended_pattern^0 %* (EOF + error'character after EOF') `` It may also be used as a normal function: `` function (subject, i) if bad_condition then error'bad condition'(subject, i) end end `` --]] function error (msg) return function (subject, i) local line = lines(string.sub(subject,1,i)) _G.error('Lexical error in line '..line..', near "' ..(subject:sub(i-10,i)):gsub('\n','EOL')..'": '..msg, 0) end end --[[ Strips all prefixing `--` and enclosing `--%[=%*%[` from comment tokens. **Parameters:** * `comment`: the comment to strip. **Returns:** * the text without comment marking syntax. --]] function comment2text (comment) -- TEMP: LPeg could be used here local ret, i, brackets = comment:find('^%-%-%[(=*)%[', i) if ret then comment = comment:gsub('^%-%-%['..brackets..'%[', '') -- removes "--[===[" comment = comment:gsub('%]'..brackets..'%]$', '') -- removes "]===]" comment = '\n' .. comment comment = comment:gsub('\n\n', '\n%-%-\n') -- adjust empty lines comment = comment:gsub('\n%s*%-%-+ ?', '\n' ) -- removes "--+ " prefix from lines comment = comment:gsub('^\n\n?', '') -- removes empty prefix lines comment = comment:gsub('\n$', '') -- removes empty sufix lines else comment = comment:gsub('^%-%-+%s*', '') end return comment end --[[ Encloses the text with comment markers. **Parameters:** * `text`: the text to comment. **Returns:** * the text with comment marking syntax. --]] function text2comment (text) -- finds a pattern anywhere in the subject, ripped from LPeg's home page local function anywhere(patt) return m.P { m.P(patt) + 1 * m.V(1) } end -- searching for the largest [(=)*[ in the text local max = -1 local updateMax = function (c) if max < #c then max = #c end end local openPatt = m.P'[' * m.C((m.P'=')^0) * m.P'[' / updateMax local closePatt = m.P']' * m.C((m.P'=')^0) * m.P']' / updateMax anywhere(openPatt):match(text) anywhere(closePatt):match(text) -- enclosing text with --[(=)^(max+1)[ and --](=)^(max + 1)] local equals = string.rep('=', max + 1) return '--['..equals..'[\n'..text..'--]'..equals..']' end -- used for escape processing in string2text local escapeTable = { ['\\n'] = '\n', ['\\t'] = '\t', ['\\r'] = '\r', ['\\v'] = '\v', ['\\a'] = '\a', ['\\b'] = '\b', ['\\f'] = '\f', ['\\"'] = '"', ["\\'"] = "'", ['\\\\'] = '\\', } -- used for escape processing in text2string local reverseEscapeTable = {} for k, v in pairs(escapeTable) do reverseEscapeTable[v] = k reverseEscapeTable[string.byte(v)] = k end --[=[ Strips all enclosing `'`, `"`, and `%[=%*%[` from string tokens, and processes escape characters. **Parameters:** * `str`: the string to strip. **Returns:** * the text without string enclosers. --]=] function string2text(str) local escapeNum = m.C(N^-3) / tonumber local escapePatt = ( (m.P'\\' * m.S[[ntrvabf'"\\]]) / escapeTable + (m.P'\\' * escapeNum) / string.char ) local openDQuote, openQuote = m.P'"' / '', m.P"'" / '' local closeDQuote, closeQuote = openDQuote, openQuote local start, l = "[" * m.P"="^0 * "[", nil local longstring = #(m.P'[' * m.S'[=') * m.P(function (s, i) l = start:match(s, i) if not l then return nil end local p = m.P("]"..string.rep("=", l - i - 2).."]") p = (1 - p)^0 * p return p:match(s, l) end) local patt = m.Cs( (openDQuote * ((escapePatt + 1) - closeDQuote)^0 * closeDQuote) + (openQuote * ((escapePatt + 1) - closeQuote)^0 * closeQuote) + longstring / function (c) return string.sub(c, l, -l) end ) return patt:match(str) end --[[ Transforms a text into a syntactically valid Lua string. Similar to `string.format` with the `'%%q'` option, but inserting escape numbers and escape codes where applicable. **Parameters** * `text`: a string containing the text. **Returns:** * a string, similar to string.format with option `'%%q'`. --]] function text2string(text) local function reverseEscape(char) local c = reverseEscapeTable[char] if c then return c elseif (AZ + N + SPACE + SYMBOL + PUNCTUATION):match(char) then return char else return '\\'..string.byte(char) end end local escapePatt = m.P(1) / reverseEscape local patt = m.Cs(escapePatt^0) return '"'..patt:match(text)..'"' end ------------------------------ TOKENS ----------------------------------------- --[[ A table with Lua keyword-matching patterns, with the keywords in uppercase as keys. Examples: `keywords.WHILE`, `keywords%['ELSEIF'%]`. --]] keywords = {} --[[ A table with Lua symbol-matching patterns, with the symbols themselves as keys. Examples: `symbols%['{'%]`, `symbols%['+'%]`. --]] symbols = {} -- Transforms strings into literal patterns of the same name. -- If "minus" is passed, each pattern is concatenated to it. local apply = function (dst, src, minus) local ret = m.P(false) for _, v in _G.ipairs(src) do local UP = string.upper(v) dst[UP] = m.P(v) if minus then dst[UP] = dst[UP] * minus end ret = dst[UP] + ret end return ret end -- A pattern which matches any Lua keyword. -- <% exp = 'LPeg pattern' %> KEYWORD = apply (keywords, { 'and', 'break', 'do', 'else', 'elseif', 'end', 'false', 'for', 'function', 'if', 'in', 'local', 'nil', 'not', 'or', 'repeat', 'return', 'then', 'true', 'until', 'while', }, -(N + AZ) ) -- A pattern which matches any Lua symbol. -- <% exp = 'LPeg pattern' %> SYMBOL = apply (symbols, { '+', '-', '*', '/', '%', '^', '#', '==', '~=', '<=', '>=', '<', '>', '=', '(', ')', '{', '}', '[', ']', ';', ':', ',', '.', '..', '...', }) -- special cases (needs lookahead) symbols['-'] = symbols['-'] - '--' symbols['<'] = symbols['<'] - symbols['<='] symbols['>'] = symbols['>'] - symbols['>='] symbols['='] = symbols['='] - symbols['=='] symbols['['] = symbols['['] - '[' * m.S'[=' symbols['.'] = symbols['.'] - (symbols['..'] + N) symbols['..'] = symbols['..'] - symbols['...'] SYMBOL = m.P(false) for _, v in _G.pairs(symbols) do SYMBOL = SYMBOL + v end -- Matches any Lua identifier. -- <% exp = 'LPeg pattern' %> IDENTIFIER = AZ * (AZ+N)^0 - KEYWORD -- tries to implement the same read_numeral() as in llex.c local number = function (subject, i) -- [.\d]+ .. ( [eE] .. [+-]? )? .. isalnum()* local patt = (m.P'.' + N)^1 * (m.S'eE' * m.S'+-'^-1)^-1 * (N+AZ)^0 patt = patt / function(num) if not _G.tonumber(num) then error('Malformed number: '.._G.tostring(num))(subject,i) end end return m.match(patt, subject, i) end -- Matches any Lua number. -- <% exp = 'LPeg pattern' %> NUMBER = #(N + (m.P'.' * N)) * number -- LONG BRACKETS local long_brackets = #(m.P'[' * m.P'='^0 * m.P'[') * function (subject, i1) local level = _G.assert( subject:match('^%[(=*)%[', i1) ) local _, i2 = subject:find(']'..level..']', i1, true) -- true = plain "find substring" return (i2 and (i2+1)) or error('unfinished long brackets')(subject, i1) end -- Matches any Lua string. -- <% exp = 'LPeg pattern' %> STRING = nil do -- TEMP ddd limites: local ddd = m.P'\\' * (N^3 + N^2 + N^1) -- OPEN and ( (\?) or ( anything not CLOSE or \n )^0 ) and CLOSE local Str1 = m.P'"' * ( (m.P'\\' * 1) + (1 - (m.S'"\n\r\f')) )^0 * (m.P'"' + error'unfinished string') local Str2 = m.P"'" * ( (m.P'\\' * 1) + (1 - (m.S"'\n\r\f")) )^0 * (m.P"'" + error'unfinished string') local Str3 = long_brackets STRING = Str1 + Str2 + Str3 end local multi = m.P'--' * long_brackets local single = m.P'--' * (1 - m.P'\n')^0 -- Matches any type of comment. -- <% exp = 'LPeg pattern' %> COMMENT = multi + single -- multi must be the first ( --[ is a one line comment ) ------------------------------------------------------------------------------- ------------------------------ USEFUL PATTERNS -------------------------------- ------------------------------------------------------------------------------- -- Matches any space character. -- <% exp = 'LPeg pattern' %> SPACE = m.S'\n \t\r\f' -- Matches everything ignored by the parser. -- <% exp = 'LPeg pattern' %> IGNORED = (SPACE + COMMENT)^0 -- Matches any Lua [#variable_IDENTIFIER identifier], [#variable_KEYWORD keyword], [#variable_SYMBOL symbol], [#variable_NUMBER number] or [#variable_STRING string]. -- <% exp = 'LPeg pattern' %> TOKEN = IDENTIFIER + KEYWORD + SYMBOL + NUMBER + STRING -- Matches any [#variable_TOKEN token], [#variable_COMMENT comment] or [#variable_SPACE space]. -- <% exp = 'LPeg pattern' %> ANY = TOKEN + COMMENT + SPACE -- TEMP: + error'invalid character' -- Matches the beginning of a file. -- <% exp = 'LPeg pattern' %> BOF = m.P(function(s,i) return (i==1) and i end) -- Matches the end of a file. -- <% exp = 'LPeg pattern' %> EOF = m.P(-1) -- Matches UNIX's shebang (e.g. `#!/usr/bin/lua`). -- <% exp = 'LPeg pattern' %> BANG = BOF * m.P'#!' * (m.P(1)-'\n')^0 * '\n' lua-leg-0.1.3/tests/000077500000000000000000000000001204272643300142065ustar00rootroot00000000000000lua-leg-0.1.3/tests/test.lua000077500000000000000000000003121204272643300156670ustar00rootroot00000000000000print '==================== SCANNER ====================' dofile 'test_scanner.lua' print () print '==================== PARSER =====================' dofile 'test_parser.lua' print () print 'All done!'lua-leg-0.1.3/tests/test_parser.lua000077500000000000000000000074541204272643300172610ustar00rootroot00000000000000------------------------------------------------------------------------------- -- Parser test suite for Leg -- -- Authors: Humberto Anjos and Francisco Sant'Anna -- Copyright (c) 2007 Leg -- -- $Id: test_parser.lua,v 1.2 2007/11/22 21:15:24 hanjos Exp $ -- ------------------------------------------------------------------------------- local lpeg = require 'lpeg' local parser = require 'leg.parser' local function TEST (rule, tests) io.write(string.format("%-26s", 'Testing '..rule..': ')) io.flush() local G = lpeg.P( parser.apply(lpeg.V(rule)) ) for i, subject in ipairs(tests) do io.write(i..'... ') io.flush() if string.sub(subject, 1, 1) == '!' then subject = string.sub(subject, 2) local G = G * function(subject) print() error('"'..subject..'": should not match') end G:match(subject) else local G = G + function(subject) print() error('"'..subject..'": should match') end G:match(subject) end end print'OK!' end TEST('UnOp', { '-', 'not', '#', '!--', '!anot', '!nota' }) TEST('BinOp', { '+', '-', '*', '/', '^', '%', '..', '<', '<=', '>', '>=', '==', '~=', 'and', 'or' }) TEST('FieldSep', { ',', ';' }) TEST('Field', { '[a] = f(1)', 'a.name().name', 'name = 1' }) TEST('FieldList', { '[a] = f(1), a.name().name, name = 1,' }) TEST('TableConstructor', { "{ a, b, c, c=c, [1]={}, }", "{ ['oi']=nil, 'as', true, false, [{}]={}}" }) TEST('ParList', { 'a, d, ...', '...', '..., a' }) TEST('FuncBody', { '(a, ...) a = {1, 2, 3} + {} - ac end', '(o) return a end' }) TEST('Function', { 'function () return { a = "a" + 10 } end' }) TEST('Args', { "(a+1, {}, 'oi' )", '{a,c,s}', "'aaa'", '( function() a=1 end )' }) TEST('FunctionCall', { 'a()', 'a.name(2)', 'a.name:method()', 'print( function() a=1 end )' }) TEST('_PrefixExp', { 'a', '(a.name)', '((aaa))' }) TEST('_PrefixExp', { 'a', 'a.name', 'a.name:method()', '(a+1):x()', '(a[1+{}]:method())()', '(a[1+{}]:method())().abs', 'print( function() print() end )' }) TEST('_SimpleExp', { 'nil', 'false', 'true', '1e-10', '"adkaljsdaldk"', '...', 'function() return a end', '(a[1+{}]:method())().abs', '{}' }) TEST('Exp', { '1 + 1', '2*5-(10-f().name):a()+13', '-1', '-1*2*4^0', 'function() print() end', '1 * {} + -9 ^ 1-1', [[(a[1+{}]:method())().abs]] }) TEST('ExpList', { 'a, b, c', 'a+1, f:name(1+2), -a', }) TEST('NameList', { 'a, b, c' }) TEST('Var', { '!(a)', '!a()', 'a[a]', 'a.name', 'a.name[1]', '(a+1).name', '(a[1+{}]:method())()[f()]', '(a[1+{}]:method())().abs', }) TEST('VarList', { '!(a), b(), c:c()', 'a, b, b[1], (b())[1]' }) TEST('FuncName', { 'f.name.aa.a.a.a', 'f.name.name.name:aa', }) TEST('LastStat', { 'return', 'return f(1):a()', }) TEST('Assign', { 'a, b, c = a+1, f:name(1+2), -a', 'a=a()-1', '!subject _G.assert(io.open(input))' }) TEST('FunctionCall', { 'a()', '(a+1)(b+ c)(2^0)' }) TEST('Do', { 'do a=1 b=f(a(),b()) break end' }) TEST('While', { 'while true do return 1 end' }) TEST('Repeat', { 'repeat a=1 until false' }) TEST('If', { 'if a == 1 then a=1 end', 'if 1 then return a elseif 2 then return b else return c end', 'if g() then repeat a=1 until false end' }) TEST('NumericFor', { 'for a=f(), g()+1/2, -100e-10 do a=1 end' , 'for a=1, 10 do if a then a = 1 else break end end' }) TEST('GenericFor', { 'for a, b, c in ipairs(a) do print(a, b, c) end' }) TEST('GlobalFunction', { 'function a (a, b, ...) for k=1, 10 do end return true end', "function tofile (t, name, filename, opts) local fs = _G.require 'fs' return fs.value(t, name, filename, opts) end" }) TEST('LocalFunction', { 'local function a () end' }) TEST('LocalAssign', { 'local a, b, as', 'local a, c = 1, 2, 3, f()', 'local a' }) TEST('Block', { 'b= a()' }) TEST('Chunk', { 'a=a', '', 'a, b, c=1', 'a=1; b=5; if a then return b end; break;' }) lua-leg-0.1.3/tests/test_scanner.lua000077500000000000000000000124301204272643300174040ustar00rootroot00000000000000------------------------------------------------------------------------------- -- Scanner test suite for Leg -- -- Authors: Humberto Anjos -- Copyright (c) 2007 Leg -- -- $Id: test_scanner.lua,v 1.2 2007/11/22 21:15:24 hanjos Exp $ -- ------------------------------------------------------------------------------- local scanner = require 'leg.scanner' print 'Testing keyword matching...' for KEY, patt in pairs(scanner.keywords) do local key = string.lower(KEY) -- The key alone assert(patt:match(key)) -- The key in uppercase assert(not patt:match(KEY)) -- The key twice assert(not patt:match(key..key)) -- The key at the end of something assert(not patt:match('yadda_'..key)) -- The key at the beginning of something assert(not patt:match(key..'yaddayadda')) end print '...OK!' print 'Testing symbol matching...' for symbol, patt in pairs(scanner.symbols) do -- The symbol alone assert(patt:match(symbol)) -- The symbol at the end of something assert(not patt:match('yadda '..symbol)) -- The symbol at the beginning of something assert(patt:match(symbol..' yadda') == #symbol + 1) end print '...OK!' print 'Testing IDENTIFIER...' local ID = scanner.IDENTIFIER -- a keyword assert(not ID:match'and') -- a keyword in the name assert(ID:match'andale' == 7) -- an operator assert(ID:match'make-name' == 5) -- spaces assert(ID:match'one two three' == 4) -- invalid characters assert(not ID:match'!!name$') -- accent marks assert(ID:match'ao' == 6) -- weird, but should match assert(ID:match'_123' == 5) -- starting with a digit assert(not ID:match'1abc') -- digits at the end assert(ID:match'abc1234' == 8) -- all underscores assert(ID:match'___' == 4) -- weird characters assert(ID:match'\127\128\129' == 5) print '...OK!' print 'Testing NUMBER...' local NUM = scanner.NUMBER -- natural number assert(NUM:match'123') -- natural number assert(NUM:match'101') -- natural number assert(NUM:match'001234567890') -- decimal point assert(NUM:match'.123') -- decimal point assert(NUM:match'1.') -- decimal point assert(NUM:match'12.343') -- hexadecimal notation assert(NUM:match'0x7a2fF') -- hexadecimal notation (yes, this matches) assert(NUM:match'0x') -- hexadecimal notation assert(not NUM:match'!ff') -- scientific notation assert(NUM:match'1e-23') -- scientific notation assert(NUM:match'9.045e+3') -- scientific notation assert(NUM:match'.00678e2') -- scientific notation (works, but blows up in flames) --assert(not NUM:match'1.23e4e5') -- negative number (this is the unary operator - applied to 0.1) assert(not NUM:match'-.1') -- malformed number (works, but blows up in flames) --assert(NUM:match'123and') print '...OK!' print 'Testing STRING...' local STR = scanner.STRING -- single quotes assert(STR:match"'What a pity...'") -- single quotes with escape characters assert(STR:match"'What \065 \112\105\116\121...'") -- unclosed single quotes (works, but blows up in flames) --assert(not STR:match"'what?") -- single quotes with line breaks inside (works, but blows up in flames) --assert(not STR:match"'what\n?'") -- single quotes with valid line breaks inside assert(STR:match"'what\\\n?'") -- single quotes, escaped assert(STR:match"'What a \\'pity\\'...'") -- single quotes, with double quotes inside assert(STR:match"'What a \"pity\"...'") -- double quotes assert(STR:match'"What a pity..."') -- double quotes with escape characters assert(STR:match'"What \065 \112\105\116\121..."') -- unclosed double quotes (works, but blows up in flames) --assert(not STR:match'"what?') -- double quotes with line breaks inside (works, but blows up in flames) --assert(not STR:match'"what\n?"') -- double quotes with valid line breaks inside assert(STR:match'"what\\\n?"') -- double quotes, escaped assert(STR:match'"What a \\"pity\\"..."') -- double quotes, with single quotes inside assert(STR:match'"What a \'pity\'..."') -- long strings assert(STR:match"[[If not for you...]]") -- multi-line long strings assert(STR:match"[[something\n\or\nanother]]") -- embedded long strings assert(STR:match"[=[fantasy for sale [[that's entertainment!]]]=]") -- long strings with quotes inside assert(STR:match"[[Package it like a \"rebel\" or a 'hero']]") -- unclosed long strings (works, but blows up in flames) --assert(not STR:match"[=[[[Reality]] withdraws") print '...OK!' print 'Testing COMMENT...' local COM = scanner.COMMENT -- single line comment assert(COM:match'-- single line comment\nsome code here' == 23) -- single line comment, no line break assert(COM:match'-- she can manipulate reactions') -- multi-line comment assert(COM:match'--[[superconductor!\nThat\'s entertainment!]]') -- multi-line comment, alternate closing assert(COM:match'--[[superconductor!\nThat\'s entertainment!\n--]]') -- unclosed comment (works, but blows up in flames) --assert(not COM:match'--[[') -- embedded multi-line comment assert(COM:match'--[=[That belongs to --[[the clan]]]=]') -- embedded multi-line comment assert(COM:match[===[--[[ multi-line --[==[ asdaks \n -- --[[ deve pegar comment multi-line --print'oi' ]] ]===]) print '...OK!'