├── docs ├── CNAME ├── .gitignore ├── assets │ └── img │ │ ├── favicon.ico │ │ ├── nelua-logo.png │ │ ├── nelua-logo-64px.png │ │ └── nelua-logo.svg ├── _layouts │ ├── default.html │ └── docs.html ├── docsearch_config.json ├── 404.html ├── pages │ ├── documentation.md │ ├── tutorial.md │ └── installing.md ├── _includes │ ├── logo.svg │ ├── navbar.html │ ├── head.html │ └── footer.html ├── Gemfile ├── Gemfile.lock └── _config.yml ├── .github ├── CODEOWNERS ├── FUNDING.yml ├── ISSUE_TEMPLATE │ ├── feature_request.md │ └── bug_report.md ├── pull_request_template.md └── workflows │ ├── package.yml │ └── test.yml ├── tests ├── myclib │ ├── myclib.h │ ├── init.nelua │ └── myclib.c ├── libmylib_static.nelua ├── myclib_test.nelua ├── mylib_static_test.nelua ├── require_itself.nelua ├── require_test_metadep.lua ├── require_test.nelua ├── require_test_dep.nelua ├── all_test.nelua ├── mylib_test.nelua ├── builtins_test.nelua ├── libmylib.nelua ├── traits_test.nelua ├── threads_test.nelua ├── gc_test.nelua ├── stringbuilder_test.nelua ├── hash_test.nelua ├── libc_test.nelua ├── errorhandling_test.nelua ├── os_test.nelua └── defer_test.nelua ├── examples ├── helloworld.nelua ├── dsl2.lisp ├── dsl2.nelua ├── matmul.nelua ├── fibonacci.nelua ├── brainfuck.nelua └── gameoflife.nelua ├── .luacov ├── nelua.bat ├── lualib ├── nelua.lua └── nelua │ ├── utils │ ├── defer.lua │ ├── metamagic.lua │ ├── class.lua │ ├── memoize.lua │ ├── iterators.lua │ ├── sstream.lua │ ├── platform.lua │ ├── tracker.lua │ ├── nanotimer.lua │ ├── traits.lua │ ├── shaper.lua │ ├── errorer.lua │ ├── stringer.lua │ └── tabler.lua │ ├── luabuiltins.lua │ ├── luadefs.lua │ ├── luacompiler.lua │ ├── analyzercontext.lua │ └── attr.lua ├── lib ├── C │ ├── init.nelua │ ├── arg.nelua │ ├── errno.nelua │ ├── stdarg.nelua │ ├── signal.nelua │ ├── ctype.nelua │ ├── locale.nelua │ ├── time.nelua │ ├── string.nelua │ └── stdlib.nelua ├── table.nelua ├── arg.nelua ├── allocators │ ├── default.nelua │ ├── aligned.nelua │ └── general.nelua ├── detail │ └── xoshiro256.nelua └── traits.nelua ├── .luacheckrc ├── .gitattributes ├── .gitignore ├── src ├── luainit.c ├── README.md ├── lpeglabel │ ├── lplprint.h │ ├── lplcode.h │ ├── lplcap.h │ ├── lplvm.h │ ├── lpltree.h │ └── lpltypes.h ├── lua │ ├── lundump.h │ ├── lprefix.h │ ├── lzio.c │ ├── lapi.h │ ├── lopnames.h │ ├── lzio.h │ ├── lualib.h │ ├── lstring.h │ ├── lfunc.h │ ├── linit.c │ ├── ljumptab.h │ ├── ldebug.h │ ├── ltable.h │ ├── lctype.h │ ├── lctype.c │ ├── llex.h │ ├── ltm.h │ ├── ldo.h │ └── lmem.h └── onelua.c ├── spec ├── init.lua ├── pegger_spec.lua ├── aster_spec.lua ├── utils_spec.lua ├── stdlib_spec.lua ├── tools │ └── covreporter.lua └── except_spec.lua ├── nelua ├── Dockerfile ├── LICENSE └── CONTRIBUTING.md /docs/CNAME: -------------------------------------------------------------------------------- 1 | nelua.io -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @edubart 2 | -------------------------------------------------------------------------------- /tests/myclib/myclib.h: -------------------------------------------------------------------------------- 1 | void myprint(void); 2 | -------------------------------------------------------------------------------- /examples/helloworld.nelua: -------------------------------------------------------------------------------- 1 | print 'hello world' 2 | -------------------------------------------------------------------------------- /tests/libmylib_static.nelua: -------------------------------------------------------------------------------- 1 | require 'tests.libmylib' 2 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: edubart 2 | patreon: edubart 3 | -------------------------------------------------------------------------------- /tests/myclib_test.nelua: -------------------------------------------------------------------------------- 1 | require 'tests.myclib' 2 | 3 | myprint() 4 | -------------------------------------------------------------------------------- /tests/mylib_static_test.nelua: -------------------------------------------------------------------------------- 1 | ## STATIC=true 2 | require 'tests.mylib_test' 3 | -------------------------------------------------------------------------------- /docs/.gitignore: -------------------------------------------------------------------------------- 1 | _site 2 | .sass-cache 3 | .jekyll-cache 4 | .jekyll-metadata 5 | vendor 6 | .env -------------------------------------------------------------------------------- /.luacov: -------------------------------------------------------------------------------- 1 | exclude = { 2 | 'neluacfg', 3 | 'thirdparty', 4 | 'ppcode', 5 | 'pragma', 6 | } 7 | -------------------------------------------------------------------------------- /nelua.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | SETLOCAL 3 | SET EXE=%~dp0\nelua-lua.exe 4 | "%EXE%" -lnelua nelua.lua %* 5 | -------------------------------------------------------------------------------- /tests/require_itself.nelua: -------------------------------------------------------------------------------- 1 | -- this will trigger a compile error 2 | require 'tests.require_itself' 3 | -------------------------------------------------------------------------------- /docs/assets/img/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/edubart/nelua-lang/HEAD/docs/assets/img/favicon.ico -------------------------------------------------------------------------------- /docs/assets/img/nelua-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/edubart/nelua-lang/HEAD/docs/assets/img/nelua-logo.png -------------------------------------------------------------------------------- /lualib/nelua.lua: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env lua 2 | 3 | -- Run the Nelua compiler. 4 | os.exit(require'nelua.runner'.run(arg)) 5 | -------------------------------------------------------------------------------- /docs/assets/img/nelua-logo-64px.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/edubart/nelua-lang/HEAD/docs/assets/img/nelua-logo-64px.png -------------------------------------------------------------------------------- /tests/myclib/init.nelua: -------------------------------------------------------------------------------- 1 | ## cfile 'myclib.c' 2 | ## cinclude 'myclib.h' 3 | global function myprint() end 4 | -------------------------------------------------------------------------------- /tests/myclib/myclib.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "myclib.h" 3 | 4 | void myprint(void) { 5 | printf("hello from C\n"); 6 | } 7 | -------------------------------------------------------------------------------- /lib/C/init.nelua: -------------------------------------------------------------------------------- 1 | -- Library used only to define the C namespace. 2 | 3 | -- Namespace used to import C functions. 4 | global C: type = @record{} 5 | 6 | return C 7 | -------------------------------------------------------------------------------- /.luacheckrc: -------------------------------------------------------------------------------- 1 | std='max' 2 | include_files={'lualib', 'spec'} 3 | exclude_files={'lualib/nelua/thirdparty/*','lualib\\nelua\\thirdparty\\*'} 4 | globals = { 5 | io = {"stdout", "stderr" } 6 | } 7 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto eol=lf 2 | *.nelua text eol=lf linguist-language=lua 3 | *.h text eol=lf linguist-language=c 4 | *.c text eol=lf linguist-language=c 5 | docs/* linguist-documentation=true 6 | -------------------------------------------------------------------------------- /tests/require_test_metadep.lua: -------------------------------------------------------------------------------- 1 | local metadep = {} 2 | function metadep.inject_num_assign(name, num) 3 | inject_statement(aster.Assign{{aster.Id{name}}, {aster.Number{num}}}) 4 | end 5 | return metadep 6 | -------------------------------------------------------------------------------- /docs/_layouts/default.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | {% include head.html %} 5 | 6 | 7 | 8 | {% include navbar.html %} 9 | 10 | {{content}} 11 | 12 | {% include footer.html %} 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /lualib/nelua/utils/defer.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Defer module 3 | 4 | ]] 5 | -- Execute function at end of the scope, even when an error is raised. 6 | local function defer(f) 7 | return setmetatable({}, {__close = f}) 8 | end 9 | 10 | return defer 11 | -------------------------------------------------------------------------------- /lib/C/arg.nelua: -------------------------------------------------------------------------------- 1 | -- Library importing C's main `argc` and `argv`. 2 | 3 | require 'C' 4 | 5 | -- Import argc and argv from C 'nelua_main' 6 | 7 | global C.argc: cint 8 | global C.argv: *[0]cstring 9 | 10 | return C 11 | -------------------------------------------------------------------------------- /lualib/nelua/luabuiltins.lua: -------------------------------------------------------------------------------- 1 | local luabuiltins = {} 2 | 3 | local operators = {} 4 | luabuiltins.operators = operators 5 | 6 | function operators.idiv(_, _, emitter, lnode, rnode) 7 | emitter:add('math.floor(', lnode, ' / ', rnode, ')') 8 | end 9 | 10 | local builtins = {} 11 | luabuiltins.builtins = builtins 12 | 13 | function builtins.bit() 14 | end 15 | 16 | return luabuiltins 17 | -------------------------------------------------------------------------------- /docs/docsearch_config.json: -------------------------------------------------------------------------------- 1 | { 2 | "index_name": "docsearch", 3 | "start_urls": ["https://nelua.io/"], 4 | "sitemap_urls": ["https://nelua.io/sitemap.xml"], 5 | "selectors": { 6 | "lvl0": "main h1", 7 | "lvl1": "main h2", 8 | "lvl2": "main h3", 9 | "lvl3": "main h4", 10 | "lvl4": "main h5", 11 | "lvl5": "main h6", 12 | "text": "main p, main li" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /examples/dsl2.lisp: -------------------------------------------------------------------------------- 1 | ; This file is used by our DSL, but it's not really Lisp. 2 | ; The 'lisp' extension is used just to enable syntax highlighting. 3 | (print "Hello from DSL!") 4 | (fn fib (n) 5 | (let a 0) 6 | (let b 1) 7 | (let i 0) 8 | (while (< i n) 9 | (let tmp a) 10 | (= a b) 11 | (= b (+ a tmp)) 12 | (= i (+ i 1)) 13 | ) 14 | a 15 | ) 16 | (let n (fib 10)) 17 | (print (.. "Fib 10 is " n)) 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | nelua_cache 2 | nelua-lua 3 | nelua-luac 4 | neluacfg.lua 5 | .neluacfg.lua 6 | .neluacfg.*.lua 7 | *.out 8 | *.tmp 9 | *.log 10 | *.gcov 11 | *.gcda 12 | *.exe 13 | *.so 14 | *.dylib 15 | *.o 16 | *.a 17 | *.bmir 18 | *.mir 19 | *.dll 20 | *.data 21 | *.old 22 | *.zst 23 | *.obj 24 | *.luabc 25 | .vscode 26 | tempCodeRunner.* 27 | /play 28 | /pkg 29 | /mylib_test 30 | /pgo 31 | preview.md 32 | test_file.tmp 33 | require_tmp.nelua 34 | *-bindings.nelua -------------------------------------------------------------------------------- /tests/require_test.nelua: -------------------------------------------------------------------------------- 1 | require '.require_test_dep' 2 | require 'tests.require_test_dep' 3 | 4 | assert(a == 1) 5 | assert(b == 2) 6 | ## def_c() 7 | assert(c == 3) 8 | ## def_d() 9 | assert(d == 4) 10 | 11 | ## def_lc() 12 | assert(lc == 3) 13 | ## def_ld() 14 | ## assert(ld == nil) 15 | 16 | ## local metadep = require '.require_test_metadep' 17 | ## metadep = require 'tests.require_test_metadep' 18 | local a = 0 19 | ## metadep.inject_num_assign('a', 5) 20 | assert(a == 5) 21 | 22 | print 'require OK!' 23 | -------------------------------------------------------------------------------- /examples/dsl2.nelua: -------------------------------------------------------------------------------- 1 | --[[ 2 | DSL Example Part 2 3 | 4 | This is evolution of DSL example part 1, with the following additions: 5 | - Use the `aster` compiler module, making easier to parse/process AST nodes. 6 | - Make able to load DSL with `.lisp` file extension. 7 | - Move the DSL logic to a Lua file. 8 | ]] 9 | 10 | 11 | ## require './dsl2.lua' 12 | 13 | require 'string' 14 | 15 | print 'Hello from Nelua!' 16 | 17 | -- Execute our DSL 18 | require './dsl2.lisp' 19 | 20 | print 'Hello from Nelua again!' 21 | -------------------------------------------------------------------------------- /tests/require_test_dep.nelua: -------------------------------------------------------------------------------- 1 | -- globals 2 | global a = 1 3 | 4 | ## if true then 5 | global b = 2 6 | ## end 7 | 8 | ## function def_c() 9 | global c = 3 10 | ## end 11 | 12 | ## def_d = hygienize(function() 13 | global d = 4 14 | ## end) 15 | 16 | -- locals 17 | local la = 1 18 | assert(la == 1) 19 | 20 | ## if true then 21 | local lb = 2 22 | assert(lb == 2) 23 | ## end 24 | 25 | ## function def_lc() 26 | local lc = 3 27 | ## end 28 | 29 | ## def_ld = hygienize(function() 30 | local ld = 4 31 | assert(ld == 4) 32 | ## end) 33 | -------------------------------------------------------------------------------- /docs/404.html: -------------------------------------------------------------------------------- 1 | --- 2 | permalink: /404.html 3 | layout: default 4 | --- 5 | 6 | 19 | 20 |
21 |

404

22 | 23 |

Page not found :(

24 |

The requested page could not be found.

25 |
26 | -------------------------------------------------------------------------------- /lib/table.nelua: -------------------------------------------------------------------------------- 1 | -- Tables are not implement yet. 2 | 3 | ## static_error 'tables are not implement yet' 4 | 5 | -- table 6 | -- table.concat(list, sep, i, j) 7 | -- table.insert(list, pos, value) 8 | -- table.remove(list, pos) 9 | -- table.move(a1, f, e, t, a2) 10 | -- table.sort(list, comp) 11 | -- table.pack(...) 12 | -- table.unpack(list, i, j) 13 | 14 | -- rawequal(v1, v2) 15 | -- rawget(table, index) 16 | -- rawlen(v) 17 | -- rawset(table, index, value) 18 | -- getmetatable(object) 19 | -- setmetatable(table, metatable) 20 | 21 | -- _G 22 | -------------------------------------------------------------------------------- /src/luainit.c: -------------------------------------------------------------------------------- 1 | #include "luainit.h" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | /* 8 | ** Runs a builtin initialization script that adjusts 'package.path', 9 | ** so we can find Nelua lua files 10 | */ 11 | LUALIB_API void lua_luainit(lua_State *L) { 12 | int res = luaL_loadbufferx(L, (char*)src_luainit_lua, src_luainit_lua_len, "@luainit.lua", "t"); 13 | if (res == LUA_OK) { 14 | lua_call(L, 0, 0); 15 | } else { 16 | lua_writestringerror("%s", lua_tostring(L, -1)); 17 | lua_pop(L, 1); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /lib/C/errno.nelua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Library that imports symbols from the `` header according to C11 specifications. 3 | 4 | For a complete documentation about the functions, 5 | see [C errno documentation](https://www.cplusplus.com/reference/cerrno/). 6 | ]] 7 | 8 | require 'C' 9 | 10 | -- Global variables 11 | 12 | global C.errno: cint '> 13 | 14 | -- Constants 15 | 16 | global C.EDOM: cint '> 17 | global C.EILSEQ: cint '> 18 | global C.ERANGE: cint '> 19 | 20 | return C 21 | -------------------------------------------------------------------------------- /spec/init.lua: -------------------------------------------------------------------------------- 1 | local lester = require 'nelua.thirdparty.lester' 2 | 3 | lester.seconds = require 'nelua.utils.nanotimer'.nanotime 4 | 5 | -- read environment configs 6 | require 'nelua.configer' 7 | 8 | require 'spec.except_spec' 9 | require 'spec.bn_spec' 10 | require 'spec.pegger_spec' 11 | require 'spec.utils_spec' 12 | require 'spec.aster_spec' 13 | require 'spec.syntaxdefs_spec' 14 | require 'spec.analyzer_spec' 15 | require 'spec.luagenerator_spec' 16 | require 'spec.cgenerator_spec' 17 | require 'spec.preprocessor_spec' 18 | require 'spec.stdlib_spec' 19 | require 'spec.runner_spec' 20 | 21 | lester.report() 22 | lester.exit() 23 | -------------------------------------------------------------------------------- /nelua: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # implement realpath case it's not present (Mac OS X for example) 4 | if ! [ -x "$(command -v realpath)" ]; then 5 | realpath() { 6 | OURPWD=$PWD 7 | cd "$(dirname "$1")" 8 | LINK=$(readlink "$(basename "$1")") 9 | while [ "$LINK" ]; do 10 | cd "$(dirname "$LINK")" 11 | LINK=$(readlink "$(basename "$1")") 12 | done 13 | REALPATH="$PWD/$(basename "$1")" 14 | cd "$OURPWD" 15 | echo "$REALPATH" 16 | } 17 | fi 18 | 19 | # detect the current directory for this script 20 | SCRIPT=$(realpath "$0") 21 | SCRIPT_DIR=$(dirname "$SCRIPT") 22 | NELUA_LUA="$SCRIPT_DIR/nelua-lua" 23 | 24 | # execute nelua compiler 25 | exec "$NELUA_LUA" -lnelua nelua.lua "$@" 26 | -------------------------------------------------------------------------------- /lib/C/stdarg.nelua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Library that imports symbols from the `` header according to C11 specifications. 3 | 4 | For a complete documentation about the functions, 5 | see [C stdarg documentation](https://www.cplusplus.com/reference/cstdarg/). 6 | ]] 7 | 8 | require 'C' 9 | 10 | function C.va_start(ap: cvalist, paramN: auto): void '> end 11 | function C.va_end(ap: cvalist): void '> end 12 | function C.va_arg(ap: *cvalist, T: type): auto 13 | local va: T 14 | ## cinclude '' 15 | ## cemit(function(emitter) emitter:add_indent_ln(va, ' = va_arg(*', ap, ', ', T.value, ');') end) 16 | return va 17 | end 18 | 19 | return C 20 | -------------------------------------------------------------------------------- /tests/all_test.nelua: -------------------------------------------------------------------------------- 1 | require 'tests.builtins_test' 2 | require 'tests.io_test' 3 | require 'tests.libc_test' 4 | require 'tests.math_test' 5 | require 'tests.memory_test' 6 | require 'tests.allocators_test' 7 | require 'tests.require_test' 8 | require 'tests.sequence_test' 9 | require 'tests.span_test' 10 | require 'tests.string_test' 11 | require 'tests.utf8_test' 12 | require 'tests.pattern_matching_test' 13 | require 'tests.pack_test' 14 | require 'tests.traits_test' 15 | require 'tests.vector_test' 16 | require 'tests.list_test' 17 | require 'tests.hash_test' 18 | require 'tests.hashmap_test' 19 | require 'tests.defer_test' 20 | require 'tests.coroutine_test' 21 | 22 | -- must be the last test because it calls os.exit() 23 | require 'tests.os_test' 24 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # ArchLinux alternative 2 | FROM archlinux/archlinux 3 | RUN pacman -Syu --noconfirm 4 | RUN pacman -S --noconfirm --needed base-devel git gcc clang 5 | RUN pacman -S --noconfirm lua luarocks sdl2 6 | 7 | # for using the terminal 8 | RUN pacman -S --noconfirm vim bash-completion 9 | 10 | # add docker user with sudo permission 11 | RUN pacman -S --noconfirm sudo 12 | RUN useradd -m -G wheel -s /bin/bash docker 13 | RUN echo '%wheel ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers 14 | USER docker 15 | 16 | # luacov 17 | RUN sudo luarocks install cluacov 18 | # luacheck 19 | RUN sudo luarocks install luacheck 20 | 21 | # nelua global config (to force testing it) 22 | RUN mkdir -p /home/docker/.config/nelua 23 | RUN echo "return {}" >> /home/docker/.config/nelua/neluacfg.lua 24 | 25 | WORKDIR /mnt 26 | -------------------------------------------------------------------------------- /src/README.md: -------------------------------------------------------------------------------- 1 | # Nelua's Lua Interpreter 2 | 3 | This is the Lua 5.4.6 interpreter used by Nelua, with the following changes: 4 | 5 | * Uses rpmalloc as the default memory allocator (usually much faster than the system's default memory allocator). 6 | * Libraries "hasher", "lpeglabel", "sys" and "lfs" are bundled (they are required by Nelua compiler). 7 | * Use a distribution friendly LUA_ROOT in luaconf.h 8 | * Use -fno-crossjumping -fno-gcse in lua VM for a faster instruction execution. 9 | * C compilation flags are tuned to squeeze more performance from the Lua interpreter. 10 | * Execute `luainit.lua` code on startup, used to adjust `package.path` 11 | to make Nelua compiler files available with `require`. 12 | 13 | It is recommended to use this interpreter to have the same behavior 14 | and because it can be ~30% faster than the standard Lua. 15 | -------------------------------------------------------------------------------- /tests/mylib_test.nelua: -------------------------------------------------------------------------------- 1 | ##[[ 2 | -- Add the current folder to the linker's library search path. 3 | cflags('-L "'..config.output_dir..'"') 4 | if STATIC then 5 | if ccinfo.is_msc then 6 | linklib 'libmylib_static' 7 | else 8 | linklib 'mylib_static' 9 | end 10 | else 11 | if ccinfo.is_msc then 12 | linklib 'libmylib' 13 | else 14 | linklib 'mylib' 15 | end 16 | if ccinfo.is_posix then 17 | -- We expect that 'mylib' library is available in the current path. 18 | cflags "-Wl,-rpath,'$ORIGIN'" 19 | end 20 | end 21 | ]] 22 | 23 | -- Import mylib functions. 24 | local function mylib_sum(x: integer, y: integer): integer end 25 | local function mylib_init() end 26 | local function mylib_terminate() end 27 | 28 | -- Run example. 29 | mylib_init() 30 | local a = mylib_sum(1, 2) 31 | print('the sum is:') 32 | print(a) 33 | assert(a == 3) 34 | mylib_terminate() 35 | -------------------------------------------------------------------------------- /src/lpeglabel/lplprint.h: -------------------------------------------------------------------------------- 1 | /* 2 | ** $Id: lplprint.h $ 3 | */ 4 | 5 | 6 | #if !defined(lplprint_h) 7 | #define lplprint_h 8 | 9 | 10 | #include "lpltree.h" 11 | #include "lplvm.h" 12 | 13 | 14 | #if defined(LPEG_DEBUG) 15 | 16 | LUAI_FUNC void printpatt (Instruction *p, int n); 17 | LUAI_FUNC void printtree (TTree *tree, int ident); 18 | LUAI_FUNC void printktable (lua_State *L, int idx); 19 | LUAI_FUNC void printcharset (const byte *st); 20 | LUAI_FUNC void printcaplist (Capture *cap, Capture *limit); 21 | LUAI_FUNC void printinst (const Instruction *op, const Instruction *p); 22 | 23 | #else 24 | 25 | #define printktable(L,idx) \ 26 | luaL_error(L, "function only implemented in debug mode") 27 | #define printtree(tree,i) \ 28 | luaL_error(L, "function only implemented in debug mode") 29 | #define printpatt(p,n) \ 30 | luaL_error(L, "function only implemented in debug mode") 31 | 32 | #endif 33 | 34 | 35 | #endif 36 | 37 | -------------------------------------------------------------------------------- /docs/pages/documentation.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: docs 3 | title: Documentation 4 | permalink: /docs/ 5 | --- 6 | 7 | ### [Installing](/installing/) 8 | 9 | Instructions for downloading and installing the Nelua compiler. 10 | 11 | ### [Tutorial](/tutorial/) 12 | 13 | Tutorial for running your first programs using the Nelua compiler. 14 | Read this page after installing the Nelua compiler. 15 | 16 | ### [Overview](/overview/) 17 | 18 | Overview of Nelua's features, 19 | the **most useful page** for learning about the syntax and features. 20 | 21 | ### [Libraries](/libraries/) 22 | 23 | List of built-in libraries in Nelua and their contents. 24 | 25 | ### [C libraries](/clibraries/) 26 | 27 | List of built-in C libraries bindings in Nelua and their contents. 28 | 29 | ### [FAQ](/faq/) 30 | 31 | Answers to frequently asked questions about Nelua. 32 | Look on this page for answers to general questions you may have, or try the search bar. 33 | -------------------------------------------------------------------------------- /src/lpeglabel/lplcode.h: -------------------------------------------------------------------------------- 1 | /* 2 | ** $Id: lplcode.h $ 3 | */ 4 | 5 | #if !defined(lplcode_h) 6 | #define lplcode_h 7 | 8 | #include "lua.h" 9 | 10 | #include "lpltypes.h" 11 | #include "lpltree.h" 12 | #include "lplvm.h" 13 | 14 | LUAI_FUNC int tocharset (TTree *tree, Charset *cs); 15 | LUAI_FUNC int checkaux (TTree *tree, int pred); 16 | LUAI_FUNC int fixedlen (TTree *tree); 17 | LUAI_FUNC int hascaptures (TTree *tree); 18 | LUAI_FUNC int lp_gc (lua_State *L); 19 | LUAI_FUNC Instruction *compile (lua_State *L, Pattern *p); 20 | LUAI_FUNC void realloccode (lua_State *L, Pattern *p, int nsize); 21 | LUAI_FUNC int sizei (const Instruction *i); 22 | 23 | 24 | #define PEnullable 0 25 | #define PEnofail 1 26 | 27 | /* 28 | ** nofail(t) implies that 't' cannot fail with any input 29 | */ 30 | #define nofail(t) checkaux(t, PEnofail) 31 | 32 | /* 33 | ** (not nullable(t)) implies 't' cannot match without consuming 34 | ** something 35 | */ 36 | #define nullable(t) checkaux(t, PEnullable) 37 | 38 | 39 | 40 | #endif 41 | -------------------------------------------------------------------------------- /src/lua/lundump.h: -------------------------------------------------------------------------------- 1 | /* 2 | ** $Id: lundump.h $ 3 | ** load precompiled Lua chunks 4 | ** See Copyright Notice in lua.h 5 | */ 6 | 7 | #ifndef lundump_h 8 | #define lundump_h 9 | 10 | #include "llimits.h" 11 | #include "lobject.h" 12 | #include "lzio.h" 13 | 14 | 15 | /* data to catch conversion errors */ 16 | #define LUAC_DATA "\x19\x93\r\n\x1a\n" 17 | 18 | #define LUAC_INT 0x5678 19 | #define LUAC_NUM cast_num(370.5) 20 | 21 | /* 22 | ** Encode major-minor version in one byte, one nibble for each 23 | */ 24 | #define MYINT(s) (s[0]-'0') /* assume one-digit numerals */ 25 | #define LUAC_VERSION (MYINT(LUA_VERSION_MAJOR)*16+MYINT(LUA_VERSION_MINOR)) 26 | 27 | #define LUAC_FORMAT 0 /* this is the official format */ 28 | 29 | /* load one chunk; from lundump.c */ 30 | LUAI_FUNC LClosure* luaU_undump (lua_State* L, ZIO* Z, const char* name); 31 | 32 | /* dump one chunk; from ldump.c */ 33 | LUAI_FUNC int luaU_dump (lua_State* L, const Proto* f, lua_Writer w, 34 | void* data, int strip); 35 | 36 | #endif 37 | -------------------------------------------------------------------------------- /src/lua/lprefix.h: -------------------------------------------------------------------------------- 1 | /* 2 | ** $Id: lprefix.h $ 3 | ** Definitions for Lua code that must come before any other header file 4 | ** See Copyright Notice in lua.h 5 | */ 6 | 7 | #ifndef lprefix_h 8 | #define lprefix_h 9 | 10 | 11 | /* 12 | ** Allows POSIX/XSI stuff 13 | */ 14 | #if !defined(LUA_USE_C89) /* { */ 15 | 16 | #if !defined(_XOPEN_SOURCE) 17 | #define _XOPEN_SOURCE 600 18 | #elif _XOPEN_SOURCE == 0 19 | #undef _XOPEN_SOURCE /* use -D_XOPEN_SOURCE=0 to undefine it */ 20 | #endif 21 | 22 | /* 23 | ** Allows manipulation of large files in gcc and some other compilers 24 | */ 25 | #if !defined(LUA_32BITS) && !defined(_FILE_OFFSET_BITS) 26 | #define _LARGEFILE_SOURCE 1 27 | #define _FILE_OFFSET_BITS 64 28 | #endif 29 | 30 | #endif /* } */ 31 | 32 | 33 | /* 34 | ** Windows stuff 35 | */ 36 | #if defined(_WIN32) /* { */ 37 | 38 | #if !defined(_CRT_SECURE_NO_WARNINGS) 39 | #define _CRT_SECURE_NO_WARNINGS /* avoid warnings about ISO C functions */ 40 | #endif 41 | 42 | #endif /* } */ 43 | 44 | #endif 45 | 46 | -------------------------------------------------------------------------------- /tests/builtins_test.nelua: -------------------------------------------------------------------------------- 1 | require 'iterators' 2 | 3 | do -- print 4 | print('hello', 'world') 5 | end 6 | 7 | do -- warn 8 | warn('hi') 9 | end 10 | 11 | do -- check 12 | check(true) 13 | check(true, 'ok') 14 | end 15 | 16 | do -- assert 17 | assert(true) 18 | assert(true, 'test') 19 | assert(true, (@cstring)('test')) 20 | assert(assert(1) == 1) 21 | assert(assert(0) == 0) 22 | end 23 | 24 | do -- likely/unlikely 25 | assert(likely(true) == true) 26 | assert(likely(false) == false) 27 | assert(unlikely(true) == true) 28 | assert(unlikely(false) == false) 29 | end 30 | 31 | do -- nilptr 32 | assert(not nilptr) 33 | end 34 | 35 | do -- _VERSION 36 | assert(_VERSION == #[require 'nelua.version'.NELUA_VERSION]#) 37 | end 38 | 39 | do -- select 40 | assert(select('#') == 0) 41 | assert(select('#', 10) == 1) 42 | assert(select('#', 10, 20) == 2) 43 | 44 | assert(select(1, 10) == 10) 45 | assert(select(1, 10, 20) == 10) 46 | assert(select(2, 10, 20) == 20) 47 | assert(select(-1, 10, 20) == 20) 48 | assert(select(-1, 10, 20) == 20) 49 | end 50 | 51 | print 'builtins OK!' 52 | -------------------------------------------------------------------------------- /docs/_includes/logo.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/assets/img/nelua-logo.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /examples/matmul.nelua: -------------------------------------------------------------------------------- 1 | require 'sequence' 2 | 3 | local Matrix = @sequence(sequence(number)) 4 | 5 | local function matrix_transpose(a: Matrix, n: integer): Matrix 6 | local x: Matrix = {} 7 | for i=1,n do 8 | x[i] = {} 9 | for j=1,n do 10 | x[i][j] = a[j][i] 11 | end 12 | end 13 | return x 14 | end 15 | 16 | local function matrix_multiply(a: Matrix, b: Matrix, n: integer): Matrix 17 | local x: Matrix = {} 18 | local c = matrix_transpose(b, n) 19 | for i=1,n do 20 | x[i] = {} 21 | for j=1,n do 22 | local sum = 0.0 23 | for k=1,n do 24 | sum = sum + a[i][k] * c[j][k] 25 | end 26 | x[i][j] = sum 27 | end 28 | end 29 | return x 30 | end 31 | 32 | local function matrix_generate(n: integer): Matrix 33 | local a: Matrix, tmp = {}, 1.0 / n / n 34 | for i=1,n do 35 | a[i] = {} 36 | for j=1,n do 37 | a[i][j] = tmp * (i - j - 2) * (i + j - 2) 38 | end 39 | end 40 | return a 41 | end 42 | 43 | local n = 200 44 | local a = matrix_generate(n) 45 | local b = matrix_generate(n) 46 | local res = matrix_multiply(a, b, n) 47 | print(res[n//2+1][n//2+1]) 48 | -------------------------------------------------------------------------------- /examples/fibonacci.nelua: -------------------------------------------------------------------------------- 1 | require 'math' 2 | 3 | do -- Recursive solution. 4 | local function fibonacci(n: integer): integer 5 | if n <= 2 then 6 | return 1 7 | end 8 | return fibonacci(n - 1) + fibonacci(n - 2) 9 | end 10 | print(fibonacci(10)) 11 | end 12 | 13 | do -- Tail recursive solution. 14 | local function fibonacci(n: integer, current: integer, next: integer): integer 15 | if n == 0 then 16 | return current 17 | end 18 | return fibonacci(n - 1, next, current + next) 19 | end 20 | print(fibonacci(10, 0, 1)) 21 | end 22 | 23 | do -- Iterative solution. 24 | local function fibonacci(n: integer): integer 25 | local first, second = 0, 1 26 | for i=1,n do 27 | first, second = second, first 28 | second = second + first 29 | end 30 | return first 31 | end 32 | print(fibonacci(10)) 33 | end 34 | 35 | do -- Analytic solution. 36 | local function fibonacci(n: integer): integer 37 | local p = (1.0 + math.sqrt(5.0)) / 2.0 38 | local q = 1.0 / p 39 | return (@integer)(math.floor((p^n + q^n) / math.sqrt(5.0))) 40 | end 41 | print(fibonacci(10)) 42 | end 43 | -------------------------------------------------------------------------------- /lib/C/signal.nelua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Library that imports symbols from the `` header according to C11 specifications. 3 | 4 | For a complete documentation about the functions, 5 | see [C signal documentation](https://www.cplusplus.com/reference/csignal/). 6 | ]] 7 | 8 | require 'C' 9 | 10 | function C.signal(sig: cint, handler: function(cint)): function(cint): void '> end 11 | function C.raise(sig: cint): cint '> end 12 | 13 | -- Constants 14 | 15 | global C.SIG_DFL: function(cint): void '> 16 | global C.SIG_IGN: function(cint): void '> 17 | global C.SIG_ERR: function(cint): void '> 18 | 19 | global C.SIGTERM: cint '> 20 | global C.SIGSEGV: cint '> 21 | global C.SIGINT: cint '> 22 | global C.SIGILL: cint '> 23 | global C.SIGABRT: cint '> 24 | global C.SIGFPE: cint '> 25 | 26 | return C 27 | -------------------------------------------------------------------------------- /tests/libmylib.nelua: -------------------------------------------------------------------------------- 1 | -- Disable the garbage collector, 2 | -- because libraries should not and cannot use the garbage collector, 3 | -- this means that you are responsible to manually manage the memory. 4 | ## pragmas.nogc = true 5 | -- Prefix used when declaring C symbols for this file. 6 | -- With this an exported function named 'sum' will be exported as 'mylib_sum' 7 | ## pragmas.unitname = 'mylib' 8 | 9 | print 'mylib - in top scope' 10 | 11 | local function sum(x: integer, y: integer): integer 12 | print 'mylib - sum' 13 | return x + y 14 | end 15 | 16 | -- Initialize the library. 17 | -- This function is marked as entry point, initialization code should be done here. 18 | local function init() 19 | -- Import and call nelua_main on initialization. 20 | -- This function run all code in top scope and initializes global variables. 21 | local function nelua_main(argc: cint, argv: *cstring): cint end 22 | print 'mylib - init' 23 | nelua_main(0, nilptr) 24 | end 25 | 26 | -- Terminate the library. 27 | local function terminate() 28 | print 'mylib - terminate' 29 | end 30 | -------------------------------------------------------------------------------- /tests/traits_test.nelua: -------------------------------------------------------------------------------- 1 | require 'traits' 2 | 3 | do -- type 4 | assert(type(1) == 'number') 5 | assert(type(false) == 'boolean') 6 | assert(type(true) == 'boolean') 7 | assert(type(nilptr) == 'pointer') 8 | assert(type(nil) == 'nil') 9 | assert(type('a') == 'string') 10 | local function f() end 11 | local R = @record{x:integer} 12 | local r: R 13 | assert(type(r) == 'record') 14 | assert(r.x == 0) 15 | assert(type(f) == 'function') 16 | assert(type(R) == 'type') 17 | assert(type(r) == 'record') 18 | assert(type(&r) == 'pointer') 19 | 20 | local x = 1 21 | assert(type(x) == 'number') 22 | end 23 | 24 | do -- traits.typeidof 25 | assert(traits.typeidof(@niltype) == 0) 26 | assert(traits.typeidof(@string) == traits.typeidof('a')) 27 | assert(traits.typeidof(@integer) == traits.typeidof(1)) 28 | assert(traits.typeidof(@integer) == traits.typeidof(1)) 29 | end 30 | 31 | do -- traits.typeinfoof 32 | local niltype_typeinfo = traits.typeinfoof(@niltype) 33 | assert(niltype_typeinfo.id == 0) 34 | assert(niltype_typeinfo.name == 'niltype') 35 | assert(niltype_typeinfo.codename == 'nlniltype') 36 | end 37 | 38 | print 'traits OK!' 39 | -------------------------------------------------------------------------------- /lib/arg.nelua: -------------------------------------------------------------------------------- 1 | --[[ 2 | The arguments library provides the global sequence `arg`, 3 | which is filled with command line arguments on initialization. 4 | ]] 5 | 6 | require 'sequence' 7 | require 'allocators.general' 8 | 9 | --[[ 10 | Sequence of command line arguments. 11 | 12 | The value at index `0` is usually filled with the program executable name. 13 | The values starting from index `1` up to `#arg` contains each command line argument. 14 | ]] 15 | global arg: sequence(string, GeneralAllocator) 16 | 17 | -- We only want to initialize 'arg' if it is really used somewhere else. 18 | ## after_analyze(hygienize(function() 19 | ## if symbols.arg:is_used() then 20 | -- Import argc and argv from C 'nelua_main' 21 | local nelua_argc: cint 22 | local nelua_argv: *[0]cstring 23 | 24 | -- setup args 25 | if nelua_argc > 0 then 26 | local narg: usize = (@usize)(nelua_argc-1) 27 | arg:reserve(narg) 28 | for i:usize=0,narg do 29 | arg[i] = nelua_argv[i] 30 | end 31 | end 32 | ## end 33 | ## end)) 34 | 35 | -- NOTE: The memory of `arg` is never freed, but it's fine, it's not a leak for global variables. 36 | 37 | return arg 38 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019-2023 Eduardo Bart (https://github.com/edubart) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /lualib/nelua/utils/metamagic.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Metamagic module 3 | 4 | The metamagic module have a few utilities for working with metatables. 5 | ]] 6 | 7 | local metamagic = {} 8 | 9 | -- Set the `__index` metamethod for a table, creating a metatable if necessary. 10 | function metamagic.setmetaindex(t, __index, overwrite) 11 | local mt = getmetatable(t) 12 | if mt then 13 | assert(overwrite or mt.__index == __index, 'cannot overwrite metatable') 14 | mt.__index = __index 15 | elseif __index then 16 | setmetatable(t, { __index = __index}) 17 | end 18 | return t 19 | end 20 | 21 | -- Set the `__call` metamethod for a table, always creating a new metatable. 22 | function metamagic.setmetacall(t, f) 23 | local mt = getmetatable(t) 24 | local callfunc = function(_, ...) 25 | return f(...) 26 | end 27 | assert(not mt, 'cannot overwrite metatable') 28 | setmetatable(t, { __call = callfunc}) 29 | return t 30 | end 31 | 32 | -- Check if a value has metamethod `method` in its metatable. 33 | function metamagic.hasmetamethod(t, method) 34 | local mt = getmetatable(t) 35 | return (mt and mt[method]) and true or false 36 | end 37 | 38 | return metamagic 39 | -------------------------------------------------------------------------------- /spec/pegger_spec.lua: -------------------------------------------------------------------------------- 1 | local lester = require 'nelua.thirdparty.lester' 2 | local describe, it = lester.describe, lester.it 3 | 4 | local expect = require 'spec.tools.expect' 5 | local pegger = require 'nelua.utils.pegger' 6 | 7 | describe("pegger", function() 8 | 9 | it("c double quoting", function() 10 | local q = pegger.double_quote_c_string 11 | expect.equal(q [[hello world]], [["hello world"]]) 12 | expect.equal(q [["]], [["\""]]) 13 | expect.equal(q [[']], [["'"]]) 14 | expect.equal(q [[\]], [["\\"]]) 15 | expect.equal(q "\t", [["\t"]]) 16 | expect.equal(q "\a", [["\a"]]) 17 | expect.equal(q "\b", [["\b"]]) 18 | expect.equal(q "\f", [["\f"]]) 19 | expect.equal(q "\n", [["\n"]]) 20 | expect.equal(q "\r", [["\r"]]) 21 | expect.equal(q "\v", [["\v"]]) 22 | expect.equal(q "\001", [["\001"]]) 23 | expect.equal(q "\255", [["\377"]]) 24 | 25 | -- trigraphs 26 | expect.equal(q "??=", [["?\?="]]) 27 | expect.equal(q "??/", [["?\?/"]]) 28 | expect.equal(q "??'", [["?\?'"]]) 29 | expect.equal(q "??(", [["?\?("]]) 30 | expect.equal(q "??)", [["?\?)"]]) 31 | expect.equal(q "??!", [["?\?!"]]) 32 | expect.equal(q "??<", [["?\?<"]]) 33 | expect.equal(q "??>", [["?\?>"]]) 34 | expect.equal(q "??-", [["?\?-"]]) 35 | end) 36 | 37 | end) 38 | -------------------------------------------------------------------------------- /lualib/nelua/luadefs.lua: -------------------------------------------------------------------------------- 1 | local luabuiltins = require 'nelua.luabuiltins' 2 | local luadefs = {} 3 | 4 | luadefs.unary_ops = { 5 | ['not'] = 'not ', 6 | ['unm'] = '-', 7 | ['bnot'] = '~', 8 | ['len'] = '#', 9 | } 10 | 11 | luadefs.binary_ops = { 12 | ['or'] = 'or', 13 | ['and'] = 'and', 14 | ['ne'] = '~=', 15 | ['eq'] = '==', 16 | ['le'] = '<=', 17 | ['ge'] = '>=', 18 | ['lt'] = '<', 19 | ['gt'] = '>', 20 | ['bor'] = '|', 21 | ['bxor'] = '~', 22 | ['band'] = '&', 23 | ['shl'] = '<<', 24 | ['shr'] = '>>', 25 | ['add'] = '+', 26 | ['sub'] = '-', 27 | ['mul'] = '*', 28 | ['div'] = '/', 29 | ['idiv'] = '//', 30 | ['mod'] = '%', 31 | ['pow'] = '^', 32 | ['concat'] = '..', 33 | } 34 | 35 | luadefs.lua51_unary_ops = { 36 | ['bnot'] = { func = 'bit.bnot', builtin = 'bit'}, 37 | } 38 | 39 | luadefs.lua51_binary_ops = { 40 | ['idiv'] = { func = luabuiltins.operators.idiv }, 41 | ['pow'] = { func = 'math.pow' }, 42 | ['bor'] = { func = 'bit.bor', builtin = 'bit' }, 43 | ['band'] = { func = 'bit.band', builtin = 'bit' }, 44 | ['bxor'] = { func = 'bit.bxor', builtin = 'bit' }, 45 | ['shl'] = { func = 'bit.lshift', builtin = 'bit' }, 46 | ['shr'] = { func = 'bit.rshift', builtin = 'bit' } 47 | } 48 | 49 | return luadefs 50 | -------------------------------------------------------------------------------- /lib/allocators/default.nelua: -------------------------------------------------------------------------------- 1 | --[[ 2 | The default allocator library provides the default allocator used by the standard libraries. 3 | 4 | The default allocator is `GCAllocator` when GC is enabled, 5 | otherwise `GeneralAllocator` (when using the pragma `nogc`). 6 | 7 | When disabling the GC the user is responsible for manually deallocating 8 | unused memory from the default allocator. 9 | ]] 10 | 11 | ## if not pragmas.nogc then -- GC enabled 12 | require 'allocators.gc' 13 | ## context.rootscope.symbols.default_allocator = gc_allocator 14 | ## context.rootscope.symbols.DefaultAllocator = GCAllocator 15 | ## else -- GC disabled 16 | require 'allocators.general' 17 | ## context.rootscope.symbols.default_allocator = general_allocator 18 | ## context.rootscope.symbols.DefaultAllocator = GeneralAllocator 19 | ## end 20 | 21 | --[[ 22 | Shorthand for `default_allocator:new`. 23 | For details see also `Allocator:new`. 24 | ]] 25 | global function new(what: auto, size: facultative(usize), flags: facultative(usize)): auto 26 | return default_allocator:new(what, size, flags) 27 | end 28 | 29 | --[[ 30 | Shorthand for `default_allocator:delete`. 31 | For details see also `Allocator:delete`. 32 | ]] 33 | global function delete(v: auto): void 34 | default_allocator:delete(v) 35 | end 36 | -------------------------------------------------------------------------------- /docs/Gemfile: -------------------------------------------------------------------------------- 1 | source "https://rubygems.org" 2 | # Hello! This is where you manage which Jekyll version is used to run. 3 | # When you want to use a different version, change it below, save the 4 | # file and run `bundle install`. Run Jekyll with `bundle exec`, like so: 5 | # 6 | # bundle exec jekyll serve 7 | # 8 | # This will help ensure the proper Jekyll version is running. 9 | # Happy Jekylling! 10 | gem "jekyll", "~> 4.1.1" 11 | # This is the default theme for new Jekyll sites. You may change this to anything you like. 12 | gem "minima", "~> 2.5" 13 | # If you want to use GitHub Pages, remove the "gem "jekyll"" above and 14 | # uncomment the line below. To upgrade, run `bundle update github-pages`. 15 | # gem "github-pages", group: :jekyll_plugins 16 | # If you have any plugins, put them here! 17 | group :jekyll_plugins do 18 | gem "jekyll-feed" 19 | gem "jekyll-sitemap" 20 | end 21 | 22 | # Windows and JRuby does not include zoneinfo files, so bundle the tzinfo-data gem 23 | # and associated library. 24 | platforms :mingw, :x64_mingw, :mswin, :jruby do 25 | gem "tzinfo" 26 | gem "tzinfo-data" 27 | end 28 | 29 | # Performance-booster for watching directories on Windows 30 | gem "wdm", "~> 0.1.1", :platforms => [:mingw, :x64_mingw, :mswin] 31 | 32 | 33 | gem "webrick", "~> 1.7" 34 | -------------------------------------------------------------------------------- /docs/_includes/navbar.html: -------------------------------------------------------------------------------- 1 | 29 | -------------------------------------------------------------------------------- /lib/C/ctype.nelua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Library importing symbols from `` header according to C11 spec. 3 | 4 | For a complete documentation about the functions, 5 | see [C ctype documentation](https://www.cplusplus.com/reference/cctype/). 6 | ]] 7 | 8 | require 'C' 9 | 10 | -- Character classification functions 11 | 12 | function C.isalnum(x: cint): cint '> end 13 | function C.isalpha(x: cint): cint '> end 14 | function C.islower(x: cint): cint '> end 15 | function C.isupper(x: cint): cint '> end 16 | function C.isdigit(x: cint): cint '> end 17 | function C.isxdigit(x: cint): cint '> end 18 | function C.iscntrl(x: cint): cint '> end 19 | function C.isgraph(x: cint): cint '> end 20 | function C.isspace(x: cint): cint '> end 21 | function C.isblank(x: cint): cint '> end 22 | function C.isprint(x: cint): cint '> end 23 | function C.ispunct(x: cint): cint '> end 24 | 25 | -- Character conversion functions 26 | 27 | function C.tolower(c: cint): cint '> end 28 | function C.toupper(c: cint): cint '> end 29 | 30 | return C 31 | -------------------------------------------------------------------------------- /lualib/nelua/utils/class.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Class module 3 | 4 | The class module is used to create object oriented classes using Lua metatables. 5 | Used in many parts of the compiler. 6 | ]] 7 | 8 | local metamagic = require 'nelua.utils.metamagic' 9 | 10 | local class = {} 11 | 12 | -- Helper called to initialize a new object instance of `klass`. 13 | local function createobject(klass, ...) 14 | local object = setmetatable({}, klass) 15 | object:_init(...) 16 | return object 17 | end 18 | 19 | -- Create a new class derived from `base`. 20 | function class.new(base) 21 | local klass = {} 22 | local create = createobject 23 | if base then 24 | for k, v in pairs(base) do 25 | if k:find("^__") == 1 then 26 | klass[k] = v 27 | end 28 | end 29 | create = getmetatable(base).__call 30 | end 31 | klass.__index = klass 32 | setmetatable(klass, { __index = base, __call = create }) 33 | return klass 34 | end 35 | 36 | -- Check if `val` is an instance of `klass`. 37 | function class.is(val, klass) 38 | local mt = getmetatable(val) 39 | while mt do 40 | local mtindex = rawget(mt, '__index') 41 | if rawequal(mtindex, klass) then return true end 42 | mt = getmetatable(mtindex) 43 | end 44 | return false 45 | end 46 | 47 | -- Allow calling `class()` to create a new class. 48 | metamagic.setmetacall(class, class.new) 49 | 50 | return class 51 | -------------------------------------------------------------------------------- /lualib/nelua/utils/memoize.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Memoize function 3 | 4 | The memoize function is used to cache a function, 5 | to avoid repeated function evaluations thus increase efficiency in some compiler parts. 6 | ]] 7 | 8 | local shallow_compare_nomt = require 'nelua.utils.tabler'.shallow_compare_nomt 9 | 10 | --[[ 11 | Wraps a function `f` into a memoized function. 12 | A memoized function is evaluated only once for different arguments, 13 | second evaluations returns a cached result. 14 | ]] 15 | local function memoize(f) 16 | local cache = {} 17 | return function(...) 18 | -- search in the cache 19 | local n = select('#', ...) 20 | for params,res in pairs(cache) do 21 | if n == params.n then 22 | local match = true 23 | for i=1,n do 24 | local pv = params[i] 25 | local av = select(i, ...) 26 | if pv ~= av and not shallow_compare_nomt(pv, av) then 27 | match = false 28 | break 29 | end 30 | end 31 | if match then 32 | -- found an evaluation with the same arguments, return the results 33 | return table.unpack(res, 1, res.n) 34 | end 35 | end 36 | end 37 | local params = table.pack(...) 38 | local res = table.pack(f(...)) 39 | cache[params] = res 40 | return table.unpack(res, 1, res.n) 41 | end 42 | end 43 | 44 | return memoize 45 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Request a feature 4 | title: feature 5 | labels: enhancement 6 | assignees: '' 7 | --- 8 | 9 | **WARNING: Please, read this note carefully before submitting a feature request:** 10 | 11 | It is important to realize that **this is NOT A SUPPORT FORUM**, this is for ISSUES ONLY. 12 | 13 | If you need support, have questions, ideas to discuss you should use 14 | the [Nelua Discussions](https://github.com/edubart/nelua-lang/discussions) forum or 15 | the [Nelua Discord server](https://discord.gg/7aaGeG7). 16 | 17 | Please, before submitting a new issue, verify the following: 18 | - You have tested it on latest Nelua version from master branch. 19 | - You have checked that there is no similar feature already implemented or requested. 20 | 21 | --- 22 | 23 | ### Feature motivation 24 | 25 | A clear and concise description of what problem this feature solves. 26 | 27 | ### Feature description 28 | 29 | A clear and concise description of the feature your want. 30 | 31 | ### Code example 32 | 33 | Provide minimal code example for the feature. 34 | Please, format the code properly and try to keep it as simple as possible, just focusing on the feature. 35 | 36 | ```lua 37 | -- Place the Nelua code here. 38 | ``` 39 | 40 | ### Describe alternatives you've considered 41 | 42 | A clear and concise description of any alternative solutions you've considered. 43 | -------------------------------------------------------------------------------- /lualib/nelua/utils/iterators.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Iterators module 3 | 4 | The iterators module contains some custom iterators to do 'for in' loops. 5 | ]] 6 | 7 | local iterators = {} 8 | 9 | -- Helper for the `izip2` iterator. 10 | local function izip2_next(ts, i) 11 | i = i + 1 12 | local v1, v2 = ts[1][i], ts[2][i] 13 | if v1 ~= nil or v2 ~= nil then 14 | return i, v1, v2 15 | end 16 | end 17 | 18 | --[[ 19 | Iterate multiples values for multiple arrays, returning the iterator index and values 20 | Stops only when all values in the arrays are nil. 21 | ]] 22 | function iterators.izip2(t1, t2) 23 | return izip2_next, {t1,t2}, 0 24 | end 25 | 26 | -- Helper for the `spairs` iterator. 27 | local function spairs_next(t, k) 28 | local v 29 | repeat 30 | k, v = next(t, k) 31 | if k == nil then return nil end 32 | until type(k) == 'string' 33 | return k, v 34 | end 35 | 36 | -- Like `pairs` but only iterate on string keys. 37 | function iterators.spairs(t) 38 | return spairs_next, t, nil 39 | end 40 | 41 | -- Like `pairs` but only iterate on string keys with ordering. 42 | function iterators.ospairs(t) 43 | local okeys = {} 44 | for k,_ in next,t do 45 | if type(k) == 'string' then 46 | okeys[#okeys + 1] = k 47 | end 48 | end 49 | table.sort(okeys) 50 | local i = 1 51 | return function() 52 | local k = okeys[i] 53 | local v = t[k] 54 | i = i + 1 55 | if v ~= nil then 56 | return k, v 57 | end 58 | end 59 | end 60 | 61 | return iterators 62 | -------------------------------------------------------------------------------- /lualib/nelua/utils/sstream.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Sstream class 3 | 4 | The sstream (stands for string stream) is a utility to compose big strings, 5 | by doing many small string concatenations. 6 | It's used by the compiler to generate large strings. 7 | ]] 8 | 9 | local class = require 'nelua.utils.class' 10 | local metamagic = require 'nelua.utils.metamagic' 11 | local sstream = class() 12 | local hasmetamethod = metamagic.hasmetamethod 13 | local concat = table.concat 14 | 15 | -- Initialize a sstream. 16 | function sstream._init() 17 | -- nothing to do 18 | end 19 | 20 | -- Concatenate many arguments, converting them to a string as needed. 21 | function sstream:add(v) 22 | self[#self+1] = tostring(v) 23 | end 24 | 25 | -- Concatenate many arguments, converting them to a string as needed. 26 | function sstream:addmany(...) 27 | for i=1,select('#', ...) do 28 | local v = select(i, ...) 29 | if type(v) ~= 'table' or hasmetamethod(v, '__tostring') then 30 | self[#self+1] = tostring(v) 31 | else 32 | self:addlist(v) 33 | end 34 | end 35 | end 36 | 37 | -- Concatenate a list of values using using a separator. 38 | function sstream:addlist(list, sep) 39 | sep = sep or self.sep or ', ' 40 | for i=1,#list do 41 | if i > 1 then 42 | self[#self+1] = sep 43 | end 44 | self:add(list[i]) 45 | end 46 | end 47 | 48 | -- Build into single string, called when composing the stream. 49 | function sstream:tostring() 50 | return concat(self) 51 | end 52 | 53 | sstream.__tostring = sstream.tostring 54 | 55 | return sstream 56 | -------------------------------------------------------------------------------- /tests/threads_test.nelua: -------------------------------------------------------------------------------- 1 | ## pragmas.nogc = true 2 | 3 | require 'C.threads' 4 | 5 | ## if ccinfo.has_c11_atomics then 6 | require 'C.stdatomic' 7 | local atomic_counter: integer = 0 8 | ## end 9 | local counter: integer = 0 10 | local mutex: C.mtx_t 11 | 12 | local function thread_safe_print(...: varargs) 13 | assert(C.mtx_lock(&mutex) == C.thrd_success) 14 | print(...) 15 | assert(C.mtx_unlock(&mutex) == C.thrd_success) 16 | end 17 | 18 | local function add_thread(arg: pointer): cint 19 | local tid: isize = (@isize)(arg) 20 | thread_safe_print('started thread', tid) 21 | for i=1,1000 do 22 | assert(C.mtx_lock(&mutex) == C.thrd_success) 23 | counter = counter + 1 24 | assert(C.mtx_unlock(&mutex) == C.thrd_success) 25 | ## if ccinfo.has_c11_atomics then 26 | C.atomic_fetch_add(&atomic_counter, 1) 27 | ## end 28 | end 29 | return tid 30 | end 31 | 32 | -- Initialize mutex. 33 | assert(C.mtx_init(&mutex, C.mtx_plain) == C.thrd_success) 34 | 35 | -- Create threads. 36 | local thrds: [10]C.thrd_t 37 | for i:isize=0,<10 do 38 | assert(C.thrd_create(&thrds[i], add_thread, (@pointer)(i)) == C.thrd_success) 39 | end 40 | 41 | -- Wait threads. 42 | for i=0,<10 do 43 | local res: cint 44 | assert(C.thrd_join(&thrds[i], &res) == C.thrd_success) 45 | assert(res == i) 46 | thread_safe_print('joined thread', i) 47 | end 48 | 49 | -- Destroy mutex. 50 | C.mtx_destroy(&mutex) 51 | 52 | print('counter', counter) 53 | assert(counter == 10000) 54 | ## if ccinfo.has_c11_atomics then 55 | assert(atomic_counter == 10000) 56 | ## end 57 | -------------------------------------------------------------------------------- /docs/_includes/head.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | {% if page.title and page.title != site.title %}{{ page.title }} - {{ site.title }}{% else %}{{ site.title }}{% endif %} 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | **WARNING: Please, read this note carefully before submitting a new pull request:** 2 | 3 | Nelua is open source, 4 | but not very open to contributions in the form of pull requests, 5 | if you would like something fixed or implemented in the core language 6 | try first submitting a bug report or opening a discussion instead of doing a PR. 7 | The authors prefer it this way, so that the ideal solution is always provided, 8 | without unwanted consequences on the project, thus keeping the quality of the software. 9 | 10 | If you insist doing a PR, typically for a small bug fix, then follow these guidelines: 11 | 12 | - Make sure the PR description clearly describes the problem and solution. Include the relevant issue number if applicable. 13 | - Don't send big pull requests (lots of changes), they are difficult to review. It's better to send small pull requests, one at a time. 14 | - Use different pull requests for different issues, each pull request should only address one issue. 15 | - When fixing a bug or adding a feature add tests related to your changes to assure the changes will always work as intended in the future and also to cover new lines added. 16 | - Verify that changes don't break the tests, you can check this with `make test`. 17 | - Follow the same coding style rules as the code base. 18 | - Pull requests just doing style changes are not welcome, a PR must address a real issue. 19 | 20 | ### Motivation 21 | 22 | Describe why you or others may need this PR. 23 | 24 | ### Code example 25 | 26 | Provide minimal code example that this PR may allow. 27 | -------------------------------------------------------------------------------- /examples/brainfuck.nelua: -------------------------------------------------------------------------------- 1 | require 'C.stdio' 2 | 3 | local data: [1024]int32 4 | local ptr: int32 = 0 5 | 6 | ##[=[ 7 | local source = [[ 8 | ++++++++[>++++[>++>+++>+++>+<<<<-]>+>+>->>+[<]<-]>>.>---.+++++++ 9 | ..+++.>>.<-.<.+++.------.--------.>>+.>++. 10 | ]] 11 | -- add a stack to keep track of the beginning of each loop 12 | local jumpstack = {} 13 | for i=1,#source do 14 | local c = source:sub(i,i) 15 | if c == ">" then 16 | ]=] ptr = ptr + 1 ##[=[ 17 | elseif c == "<" then 18 | ]=] ptr = ptr - 1 ##[=[ 19 | elseif c == "+" then 20 | ]=] data[ptr] = data[ptr] + 1 ##[=[ 21 | elseif c == "-" then 22 | ]=] data[ptr] = data[ptr] - 1 ##[=[ 23 | elseif c == "." then 24 | ]=] C.putchar(data[ptr]) ##[=[ 25 | elseif c == "," then 26 | ]=] data[ptr] = C.getchar() ##[=[ 27 | elseif c == "[" then 28 | --generate labels to represent the beginning and ending of the loop 29 | local target = { before = 'b' .. i, after = 'a' .. i } 30 | table.insert(jumpstack, target) 31 | ]=] 32 | --label for beginning of the loop 33 | ::#|target.before|#:: 34 | if data[ptr] == 0 then 35 | goto #|target.after|# --exit the loop 36 | end 37 | ##[=[ 38 | elseif c == "]" then 39 | --retrieve the labels that match this loop 40 | local target = table.remove(jumpstack) 41 | ]=] 42 | goto #|target.before|# --loop back edge 43 | ::#|target.after|#:: --label for end of the loop 44 | ##[=[ 45 | elseif c:match("%s") then 46 | -- skip spaces 47 | else 48 | error("unknown character " .. c) 49 | end 50 | end 51 | ]=] 52 | -------------------------------------------------------------------------------- /src/lua/lzio.c: -------------------------------------------------------------------------------- 1 | /* 2 | ** $Id: lzio.c $ 3 | ** Buffered streams 4 | ** See Copyright Notice in lua.h 5 | */ 6 | 7 | #define lzio_c 8 | #define LUA_CORE 9 | 10 | #include "lprefix.h" 11 | 12 | 13 | #include 14 | 15 | #include "lua.h" 16 | 17 | #include "llimits.h" 18 | #include "lmem.h" 19 | #include "lstate.h" 20 | #include "lzio.h" 21 | 22 | 23 | int luaZ_fill (ZIO *z) { 24 | size_t size; 25 | lua_State *L = z->L; 26 | const char *buff; 27 | lua_unlock(L); 28 | buff = z->reader(L, z->data, &size); 29 | lua_lock(L); 30 | if (buff == NULL || size == 0) 31 | return EOZ; 32 | z->n = size - 1; /* discount char being returned */ 33 | z->p = buff; 34 | return cast_uchar(*(z->p++)); 35 | } 36 | 37 | 38 | void luaZ_init (lua_State *L, ZIO *z, lua_Reader reader, void *data) { 39 | z->L = L; 40 | z->reader = reader; 41 | z->data = data; 42 | z->n = 0; 43 | z->p = NULL; 44 | } 45 | 46 | 47 | /* --------------------------------------------------------------- read --- */ 48 | size_t luaZ_read (ZIO *z, void *b, size_t n) { 49 | while (n) { 50 | size_t m; 51 | if (z->n == 0) { /* no bytes in buffer? */ 52 | if (luaZ_fill(z) == EOZ) /* try to read more */ 53 | return n; /* no more input; return number of missing bytes */ 54 | else { 55 | z->n++; /* luaZ_fill consumed first byte; put it back */ 56 | z->p--; 57 | } 58 | } 59 | m = (n <= z->n) ? n : z->n; /* min. between n and z->n */ 60 | memcpy(b, z->p, m); 61 | z->n -= m; 62 | z->p += m; 63 | b = (char *)b + m; 64 | n -= m; 65 | } 66 | return 0; 67 | } 68 | 69 | -------------------------------------------------------------------------------- /src/lua/lapi.h: -------------------------------------------------------------------------------- 1 | /* 2 | ** $Id: lapi.h $ 3 | ** Auxiliary functions from Lua API 4 | ** See Copyright Notice in lua.h 5 | */ 6 | 7 | #ifndef lapi_h 8 | #define lapi_h 9 | 10 | 11 | #include "llimits.h" 12 | #include "lstate.h" 13 | 14 | 15 | /* Increments 'L->top.p', checking for stack overflows */ 16 | #define api_incr_top(L) {L->top.p++; \ 17 | api_check(L, L->top.p <= L->ci->top.p, \ 18 | "stack overflow");} 19 | 20 | 21 | /* 22 | ** If a call returns too many multiple returns, the callee may not have 23 | ** stack space to accommodate all results. In this case, this macro 24 | ** increases its stack space ('L->ci->top.p'). 25 | */ 26 | #define adjustresults(L,nres) \ 27 | { if ((nres) <= LUA_MULTRET && L->ci->top.p < L->top.p) \ 28 | L->ci->top.p = L->top.p; } 29 | 30 | 31 | /* Ensure the stack has at least 'n' elements */ 32 | #define api_checknelems(L,n) \ 33 | api_check(L, (n) < (L->top.p - L->ci->func.p), \ 34 | "not enough elements in the stack") 35 | 36 | 37 | /* 38 | ** To reduce the overhead of returning from C functions, the presence of 39 | ** to-be-closed variables in these functions is coded in the CallInfo's 40 | ** field 'nresults', in a way that functions with no to-be-closed variables 41 | ** with zero, one, or "all" wanted results have no overhead. Functions 42 | ** with other number of wanted results, as well as functions with 43 | ** variables to be closed, have an extra check. 44 | */ 45 | 46 | #define hastocloseCfunc(n) ((n) < LUA_MULTRET) 47 | 48 | /* Map [-1, inf) (range of 'nresults') into (-inf, -2] */ 49 | #define codeNresults(n) (-(n) - 3) 50 | #define decodeNresults(n) (-(n) - 3) 51 | 52 | #endif 53 | -------------------------------------------------------------------------------- /tests/gc_test.nelua: -------------------------------------------------------------------------------- 1 | require 'allocators.gc' 2 | require 'memory' 3 | 4 | local function alloc_test() 5 | for i=1,128 do 6 | local p = gc_allocator:alloc(1024) 7 | assert(p ~= nilptr) 8 | p = nilptr 9 | end 10 | end 11 | 12 | local function clear_stack() 13 | local buffer: [1024]byte 14 | end 15 | 16 | do -- gc 17 | assert(gc:isrunning()) 18 | gc:stop() 19 | assert(not gc:isrunning()) 20 | gc:restart() 21 | assert(gc:isrunning()) 22 | alloc_test() 23 | clear_stack() 24 | gc:collect() 25 | gc:collect() 26 | assert(gc:count() < 16) 27 | end 28 | 29 | do -- collectgarbage 30 | assert(collectgarbage("isrunning")) 31 | collectgarbage("stop") 32 | assert(not collectgarbage("isrunning")) 33 | collectgarbage("restart") 34 | assert(collectgarbage("isrunning")) 35 | alloc_test() 36 | clear_stack() 37 | collectgarbage() 38 | collectgarbage("collect") 39 | assert(collectgarbage("count") < 16) 40 | end 41 | 42 | local gc_count = 0 43 | do -- finalizers 44 | local function finalizer_test(count: boolean) 45 | local Foo = @record{count: boolean} 46 | 47 | function Foo:__gc() 48 | if self.count then 49 | gc_count = gc_count + 1 50 | end 51 | end 52 | 53 | local foo: *Foo = gc_allocator:new(Foo{count=count}) 54 | local foos: span(Foo) = gc_allocator:new(Foo{count=count}, 2) 55 | foos = (@span(Foo)){} 56 | foo = nilptr 57 | end 58 | finalizer_test(true) 59 | finalizer_test(false) 60 | alloc_test() 61 | clear_stack() 62 | collectgarbage() 63 | collectgarbage() 64 | assert(gc_count == 3) 65 | end 66 | -------------------------------------------------------------------------------- /.github/workflows/package.yml: -------------------------------------------------------------------------------- 1 | name: "package" 2 | 3 | on: 4 | push: 5 | branches: 6 | - "master" 7 | 8 | jobs: 9 | package: 10 | strategy: 11 | matrix: 12 | os: [ubuntu-latest, macos-latest, windows-latest] 13 | include: 14 | - os: ubuntu-latest 15 | cc: musl-gcc 16 | cflags: -O2 -flto -fno-plt 17 | pkgext: .tar.xz 18 | - os: macos-latest 19 | cc: clang 20 | cflags: -O2 -flto -fno-plt 21 | pkgext: .tar.xz 22 | - os: windows-latest 23 | cc: gcc 24 | cflags: -O2 25 | pkgext: .zip 26 | runs-on: ${{matrix.os}} 27 | steps: 28 | - uses: actions/checkout@master 29 | with: 30 | fetch-depth: 0 31 | - name: Install musl 32 | if: ${{ matrix.os == 'ubuntu-latest' }} 33 | run: sudo apt-get update && sudo apt-get install musl-tools 34 | - name: Package 35 | shell: bash 36 | run: make package CC=${{matrix.cc}} CFLAGS="${{matrix.cflags}}" 37 | - name: Adjust variables 38 | shell: bash 39 | id: pkgvars 40 | run: | 41 | pkgfile=`ls pkg/*${{matrix.pkgext}}` 42 | echo "::set-output name=pkgfile::$pkgfile"; 43 | - name: Upload pre release 44 | uses: ncipollo/release-action@v1 45 | with: 46 | name: Latest Nelua in development 47 | tag: latest 48 | artifacts: ${{steps.pkgvars.outputs.pkgfile}} 49 | token: ${{secrets.GITHUB_TOKEN}} 50 | prerelease: true 51 | allowUpdates: true 52 | body: 53 | Precompiled Nelua binary package rebuilt at every new commit in master branch. 54 | -------------------------------------------------------------------------------- /tests/stringbuilder_test.nelua: -------------------------------------------------------------------------------- 1 | require 'stringbuilder' 2 | require 'allocators.arena' 3 | 4 | do 5 | local sb: stringbuilder 6 | local ok: boolean, n: usize 7 | ok, n = sb:write('hello world') assert(ok == true and n == 11) 8 | ok = sb:writebyte('\n'_byte) assert(ok == true) 9 | ok, n = sb:writef('%d\n', 10) assert(ok == true and n == 3) 10 | ok, n = sb:writef('%.2f\n', 3.14) assert(ok == true and n == 5) 11 | ok, n = sb:writef('%s %d', 'hi', 1) assert(ok == true and n == 4) 12 | local s = sb:prepare(1) 13 | s[0] = '\n'_byte 14 | sb:commit(1) 15 | local s: string = tostring(sb) 16 | assert(s == "hello world\n10\n3.14\nhi 1\n") 17 | assert(sb:view() == s) 18 | s:destroy() 19 | sb:destroy() 20 | end 21 | 22 | do -- write 23 | local sb: stringbuilder 24 | local ok: boolean, n: usize = sb:write('a', 'b', 2, 2.0, true) 25 | assert(ok == true and n == 10) 26 | assert(sb:view() == 'ab22.0true') 27 | assert(tostringview(sb) == 'ab22.0true') 28 | sb:destroy() 29 | end 30 | 31 | do -- resize 32 | local sb: stringbuilder 33 | assert(sb:write('abcd') == true) 34 | assert(sb:resize(8) == true) 35 | assert(#sb == 8 and sb:view() == 'abcd\0\0\0\0') 36 | assert(sb:resize(2) == true) 37 | assert(#sb == 2 and sb:view() == 'ab') 38 | sb:destroy() 39 | end 40 | 41 | do -- using a custom allocator 42 | local allocator: ArenaAllocator(256, 1) 43 | local sb = (@stringbuilder(*ArenaAllocator(256, 1))).make(&allocator) 44 | sb:write('hello') 45 | local s: string = tostring(sb) 46 | assert(s == "hello") 47 | s:destroy() 48 | -- sb does not need to be destroyed because we are using an arena allocator 49 | end 50 | 51 | print 'stringbuilder OK!' -------------------------------------------------------------------------------- /lualib/nelua/utils/platform.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Platform module 3 | 4 | Platform module defines platform specific values. 5 | ]] 6 | 7 | local platform = {} 8 | 9 | -- Extension of dynamic libraries (eg: .dll, .so, .dylib) 10 | local dynlibext = package.cpath:match("%p[\\|/]?%p(%a+)") 11 | 12 | --[[ 13 | The separator for directories on the platform. 14 | Usually '/' on Linux and '\' on Windows. 15 | ]] 16 | platform.dir_separator = _G.package.config:sub(1,1) 17 | 18 | --[[ 19 | The separator for the PATH environment variable on the platform. 20 | Usually ':' on Linux and ';' on Windows. 21 | ]] 22 | platform.path_separator = platform.is_windows and ';' or ':' 23 | 24 | -- Separator of LUA_PATH variable. 25 | platform.luapath_separator = package.config:match('.[\r\n]+(.)') 26 | 27 | -- Whether we are running on Windows 28 | platform.is_windows = platform.dir_separator == '\\' 29 | 30 | -- Whether we are running on Unix. 31 | platform.is_unix = not platform.is_windows 32 | 33 | -- Whether we are running on Linux. 34 | platform.is_linux = platform.is_unix and dynlibext == 'so' 35 | 36 | -- Whether we are running on MacOS. 37 | platform.is_macos = platform.is_unix and dynlibext == 'dylib' 38 | 39 | -- Whether we are running on CYGWIN shell. 40 | platform.is_cygwin = not not tostring(os.getenv('ORIGINAL_PATH')):find(':/cygdrive') 41 | 42 | -- MSYS2 variables. 43 | platform.msystem = os.getenv('MSYSTEM') 44 | platform.msystem_chost = os.getenv('MSYSTEM_CHOST') 45 | 46 | -- Whether we are running on MSYS shell. 47 | platform.is_msys = platform.msystem ~= nil 48 | 49 | -- Host CPU word size in bits, usually 32 or 64 50 | platform.cpu_bits = string.packsize('T') * 8 51 | 52 | return platform 53 | -------------------------------------------------------------------------------- /lib/C/locale.nelua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Library that imports symbols from the `` header according to C11 specifications. 3 | 4 | For a complete documentation about the functions, 5 | see [C locale documentation](https://www.cplusplus.com/reference/clocale/). 6 | ]] 7 | 8 | require 'C' 9 | 10 | global C.lconv: type ',ctypedef> = @record{ 11 | decimal_point: cstring, 12 | thousands_sep: cstring, 13 | grouping: cstring, 14 | int_curr_symbol: cstring, 15 | currency_symbol: cstring, 16 | mon_decimal_point: cstring, 17 | mon_thousands_sep: cstring, 18 | mon_grouping: cstring, 19 | positive_sign: cstring, 20 | negative_sign: cstring, 21 | int_frac_digits: cchar, 22 | frac_digits: cchar, 23 | p_cs_precedes: cchar, 24 | p_sep_by_space: cchar, 25 | n_cs_precedes: cchar, 26 | n_sep_by_space: cchar, 27 | p_sign_posn: cchar, 28 | n_sign_posn: cchar, 29 | int_p_cs_precedes: cchar, 30 | int_p_sep_by_space: cchar, 31 | int_n_cs_precedes: cchar, 32 | int_n_sep_by_space: cchar, 33 | int_p_sign_posn: cchar, 34 | int_n_sign_posn: cchar 35 | } 36 | 37 | function C.setlocale(category: cint, locale: cstring): cstring '> end 38 | function C.localeconv(): *C.lconv '> end 39 | 40 | -- Constants 41 | 42 | global C.LC_ALL: cint '> 43 | global C.LC_COLLATE: cint '> 44 | global C.LC_CTYPE: cint '> 45 | global C.LC_MONETARY: cint '> 46 | global C.LC_NUMERIC: cint '> 47 | global C.LC_TIME: cint '> 48 | 49 | return C 50 | -------------------------------------------------------------------------------- /src/lua/lopnames.h: -------------------------------------------------------------------------------- 1 | /* 2 | ** $Id: lopnames.h $ 3 | ** Opcode names 4 | ** See Copyright Notice in lua.h 5 | */ 6 | 7 | #if !defined(lopnames_h) 8 | #define lopnames_h 9 | 10 | #include 11 | 12 | 13 | /* ORDER OP */ 14 | 15 | static const char *const opnames[] = { 16 | "MOVE", 17 | "LOADI", 18 | "LOADF", 19 | "LOADK", 20 | "LOADKX", 21 | "LOADFALSE", 22 | "LFALSESKIP", 23 | "LOADTRUE", 24 | "LOADNIL", 25 | "GETUPVAL", 26 | "SETUPVAL", 27 | "GETTABUP", 28 | "GETTABLE", 29 | "GETI", 30 | "GETFIELD", 31 | "SETTABUP", 32 | "SETTABLE", 33 | "SETI", 34 | "SETFIELD", 35 | "NEWTABLE", 36 | "SELF", 37 | "ADDI", 38 | "ADDK", 39 | "SUBK", 40 | "MULK", 41 | "MODK", 42 | "POWK", 43 | "DIVK", 44 | "IDIVK", 45 | "BANDK", 46 | "BORK", 47 | "BXORK", 48 | "SHRI", 49 | "SHLI", 50 | "ADD", 51 | "SUB", 52 | "MUL", 53 | "MOD", 54 | "POW", 55 | "DIV", 56 | "IDIV", 57 | "BAND", 58 | "BOR", 59 | "BXOR", 60 | "SHL", 61 | "SHR", 62 | "MMBIN", 63 | "MMBINI", 64 | "MMBINK", 65 | "UNM", 66 | "BNOT", 67 | "NOT", 68 | "LEN", 69 | "CONCAT", 70 | "CLOSE", 71 | "TBC", 72 | "JMP", 73 | "EQ", 74 | "LT", 75 | "LE", 76 | "EQK", 77 | "EQI", 78 | "LTI", 79 | "LEI", 80 | "GTI", 81 | "GEI", 82 | "TEST", 83 | "TESTSET", 84 | "CALL", 85 | "TAILCALL", 86 | "RETURN", 87 | "RETURN0", 88 | "RETURN1", 89 | "FORLOOP", 90 | "FORPREP", 91 | "TFORPREP", 92 | "TFORCALL", 93 | "TFORLOOP", 94 | "SETLIST", 95 | "CLOSURE", 96 | "VARARG", 97 | "VARARGPREP", 98 | "EXTRAARG", 99 | NULL 100 | }; 101 | 102 | #endif 103 | 104 | -------------------------------------------------------------------------------- /src/lua/lzio.h: -------------------------------------------------------------------------------- 1 | /* 2 | ** $Id: lzio.h $ 3 | ** Buffered streams 4 | ** See Copyright Notice in lua.h 5 | */ 6 | 7 | 8 | #ifndef lzio_h 9 | #define lzio_h 10 | 11 | #include "lua.h" 12 | 13 | #include "lmem.h" 14 | 15 | 16 | #define EOZ (-1) /* end of stream */ 17 | 18 | typedef struct Zio ZIO; 19 | 20 | #define zgetc(z) (((z)->n--)>0 ? cast_uchar(*(z)->p++) : luaZ_fill(z)) 21 | 22 | 23 | typedef struct Mbuffer { 24 | char *buffer; 25 | size_t n; 26 | size_t buffsize; 27 | } Mbuffer; 28 | 29 | #define luaZ_initbuffer(L, buff) ((buff)->buffer = NULL, (buff)->buffsize = 0) 30 | 31 | #define luaZ_buffer(buff) ((buff)->buffer) 32 | #define luaZ_sizebuffer(buff) ((buff)->buffsize) 33 | #define luaZ_bufflen(buff) ((buff)->n) 34 | 35 | #define luaZ_buffremove(buff,i) ((buff)->n -= (i)) 36 | #define luaZ_resetbuffer(buff) ((buff)->n = 0) 37 | 38 | 39 | #define luaZ_resizebuffer(L, buff, size) \ 40 | ((buff)->buffer = luaM_reallocvchar(L, (buff)->buffer, \ 41 | (buff)->buffsize, size), \ 42 | (buff)->buffsize = size) 43 | 44 | #define luaZ_freebuffer(L, buff) luaZ_resizebuffer(L, buff, 0) 45 | 46 | 47 | LUAI_FUNC void luaZ_init (lua_State *L, ZIO *z, lua_Reader reader, 48 | void *data); 49 | LUAI_FUNC size_t luaZ_read (ZIO* z, void *b, size_t n); /* read next n bytes */ 50 | 51 | 52 | 53 | /* --------- Private Part ------------------ */ 54 | 55 | struct Zio { 56 | size_t n; /* bytes still unread */ 57 | const char *p; /* current position in buffer */ 58 | lua_Reader reader; /* reader function */ 59 | void *data; /* additional data */ 60 | lua_State *L; /* Lua state (for reader) */ 61 | }; 62 | 63 | 64 | LUAI_FUNC int luaZ_fill (ZIO *z); 65 | 66 | #endif 67 | -------------------------------------------------------------------------------- /tests/hash_test.nelua: -------------------------------------------------------------------------------- 1 | require 'hash' 2 | 3 | do -- hash primitive types 4 | assert(hash.hash(0) == 0) 5 | assert(hash.hash(1) == 1) 6 | assert(hash.hash(0.0) == 0) 7 | assert(hash.hash(1.0) ~= 1) 8 | assert(hash.hash(true) == 1) 9 | assert(hash.hash(false) == 0) 10 | assert(hash.hash(nilptr) == 0) 11 | assert(hash.hash(nil) == 0) 12 | end 13 | 14 | do -- hash strings 15 | assert(hash.hash('') == (@usize)(0x9e3779b9)) 16 | assert(hash.hash('test') ~= 0) 17 | end 18 | 19 | do -- hash spans 20 | local s: span(byte) 21 | assert(hash.hash(s) == (@usize)(0x9e3779b9)) 22 | s.data = 'test'_cstring s.size = 4 23 | assert(hash.hash(s) ~= 0) 24 | end 25 | 26 | do -- hash records 27 | local vec2 = @record{x: integer, y: integer} 28 | local a: vec2 = {0,0} 29 | local b: vec2 = {1,0} 30 | assert(hash.hash(a) ~= 0 and hash.hash(b) ~= 0) 31 | assert(hash.hash(a) ~= hash.hash(b)) 32 | end 33 | 34 | do -- hash unions 35 | local U = @union{i: int64, f: float32} 36 | local a: U = {i=1} 37 | local b: U = {f=1} 38 | assert(hash.hash(a) ~= hash.hash(b)) 39 | end 40 | 41 | do -- hash arrays 42 | local a: [4]integer = {1,2,3,4} 43 | local b: [4]integer = {5,6,7,8} 44 | assert(hash.hash(a) ~= 0 and hash.hash(b) ~= 0) 45 | assert(hash.hash(a) ~= hash.hash(b)) 46 | end 47 | 48 | do -- hash records with custom hash function 49 | local vec3 = @record{x: integer, y: integer, z: integer} 50 | function vec3:__hash(): usize 51 | return (@usize)(self.x + self.y * 0xff + self.z * 0xffff) 52 | end 53 | 54 | local v: vec3 55 | assert(hash.hash(v) == 0) 56 | v = {1,1,1} 57 | assert(hash.hash(v) == 1 + 0xff + 0xffff) 58 | 59 | local pv: *vec3 60 | assert(hash.hash(pv) == 0) 61 | end 62 | 63 | print 'hash OK!' -------------------------------------------------------------------------------- /src/lua/lualib.h: -------------------------------------------------------------------------------- 1 | /* 2 | ** $Id: lualib.h $ 3 | ** Lua standard libraries 4 | ** See Copyright Notice in lua.h 5 | */ 6 | 7 | 8 | #ifndef lualib_h 9 | #define lualib_h 10 | 11 | #include "lua.h" 12 | 13 | 14 | /* version suffix for environment variable names */ 15 | #define LUA_VERSUFFIX "_" LUA_VERSION_MAJOR "_" LUA_VERSION_MINOR 16 | 17 | 18 | LUAMOD_API int (luaopen_base) (lua_State *L); 19 | 20 | #define LUA_COLIBNAME "coroutine" 21 | LUAMOD_API int (luaopen_coroutine) (lua_State *L); 22 | 23 | #define LUA_TABLIBNAME "table" 24 | LUAMOD_API int (luaopen_table) (lua_State *L); 25 | 26 | #define LUA_IOLIBNAME "io" 27 | LUAMOD_API int (luaopen_io) (lua_State *L); 28 | 29 | #define LUA_OSLIBNAME "os" 30 | LUAMOD_API int (luaopen_os) (lua_State *L); 31 | 32 | #define LUA_STRLIBNAME "string" 33 | LUAMOD_API int (luaopen_string) (lua_State *L); 34 | 35 | #define LUA_UTF8LIBNAME "utf8" 36 | LUAMOD_API int (luaopen_utf8) (lua_State *L); 37 | 38 | #define LUA_MATHLIBNAME "math" 39 | LUAMOD_API int (luaopen_math) (lua_State *L); 40 | 41 | #define LUA_DBLIBNAME "debug" 42 | LUAMOD_API int (luaopen_debug) (lua_State *L); 43 | 44 | #define LUA_LOADLIBNAME "package" 45 | LUAMOD_API int (luaopen_package) (lua_State *L); 46 | 47 | 48 | /* additional libs used by nelua */ 49 | #define LUA_HASHERLIBNAME "hasher" 50 | LUAMOD_API int (luaopen_hasher) (lua_State *L); 51 | 52 | #define LUA_LFSLIBNAME "lfs" 53 | LUAMOD_API int (luaopen_lfs) (lua_State *L); 54 | 55 | #define LUA_SYSLIBNAME "sys" 56 | LUAMOD_API int (luaopen_sys) (lua_State *L); 57 | 58 | #define LUA_LPEGLABELLIBNAME "lpeglabel" 59 | LUAMOD_API int (luaopen_lpeglabel) (lua_State *L); 60 | 61 | /* open all previous libraries */ 62 | LUALIB_API void (luaL_openlibs) (lua_State *L); 63 | 64 | 65 | #endif 66 | -------------------------------------------------------------------------------- /tests/libc_test.nelua: -------------------------------------------------------------------------------- 1 | require 'C.arg' 2 | require 'C.ctype' 3 | require 'C.errno' 4 | require 'C.locale' 5 | require 'C.math' 6 | require 'C.signal' 7 | require 'C.stdarg' 8 | -- require 'C.stdatomic' 9 | require 'C.stdio' 10 | require 'C.stdlib' 11 | require 'C.string' 12 | -- require 'C.threads' 13 | require 'C.time' 14 | 15 | do -- math 16 | assert(C.fabs(-1.0) == 1.0) 17 | assert(C.fabsf(-2.0_f32) == 2.0_f32) 18 | 19 | ## if ccinfo.is_gcc or ccinfo.is_clang then 20 | assert(C.isnan(C.NAN) ~= 0) 21 | assert(C.isinf(C.INFINITY) ~= 0) 22 | assert(C.NAN ~= C.NAN) 23 | ## end 24 | end 25 | 26 | do -- va_list 27 | local function fill_numbers(numbers: *[0]float64, n: integer, ...: cvarargs) 28 | local vl: cvalist 29 | C.va_start(vl, n) 30 | for i=0,tt == LUA_VSHRSTR && (s)->extra > 0) 36 | 37 | 38 | /* 39 | ** equality for short strings, which are always internalized 40 | */ 41 | #define eqshrstr(a,b) check_exp((a)->tt == LUA_VSHRSTR, (a) == (b)) 42 | 43 | 44 | LUAI_FUNC unsigned int luaS_hash (const char *str, size_t l, unsigned int seed); 45 | LUAI_FUNC unsigned int luaS_hashlongstr (TString *ts); 46 | LUAI_FUNC int luaS_eqlngstr (TString *a, TString *b); 47 | LUAI_FUNC void luaS_resize (lua_State *L, int newsize); 48 | LUAI_FUNC void luaS_clearcache (global_State *g); 49 | LUAI_FUNC void luaS_init (lua_State *L); 50 | LUAI_FUNC void luaS_remove (lua_State *L, TString *ts); 51 | LUAI_FUNC Udata *luaS_newudata (lua_State *L, size_t s, int nuvalue); 52 | LUAI_FUNC TString *luaS_newlstr (lua_State *L, const char *str, size_t l); 53 | LUAI_FUNC TString *luaS_new (lua_State *L, const char *str); 54 | LUAI_FUNC TString *luaS_createlngstrobj (lua_State *L, size_t l); 55 | 56 | 57 | #endif 58 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Report a bug 4 | title: bug 5 | labels: bug 6 | assignees: '' 7 | --- 8 | 9 | **WARNING: Please, read this note carefully before submitting a new bug:** 10 | 11 | It is important to realize that **this is NOT A SUPPORT FORUM**, this is for reproducible BUGS ONLY. 12 | 13 | If you need support, have questions, ideas to discuss you should use 14 | the [Nelua Discussions](https://github.com/edubart/nelua-lang/discussions) forum or 15 | the [Nelua Discord server](https://discord.gg/7aaGeG7). 16 | 17 | Please, before submitting a new issue, verify the following: 18 | - You have tested it on latest Nelua version from master branch. 19 | - You have checked that there is no similar issue already reported. 20 | - You have checked that your code has no errors or misuse of Nelua. 21 | 22 | --- 23 | 24 | ### Bug description 25 | 26 | A clear and concise description of the bug you are experiencing. 27 | Tell what you were trying to do and what happened instead. 28 | 29 | ### Code example 30 | 31 | Provide minimal reproduction code to test the bug. 32 | Please, format the code properly and try to keep it as simple as possible, just focusing on the experienced bug. 33 | 34 | ```lua 35 | -- Place the Nelua code here. 36 | ``` 37 | 38 | ### Expected behavior 39 | 40 | A clear and concise description of what you expected to happen. 41 | 42 | ### Workaround 43 | 44 | If you have already found a way to workaround the issue, 45 | then please share how you did it, 46 | this may help others with the same issue. 47 | 48 | ### Environment 49 | 50 | Provide relevant information about your environment: 51 | - Nelua version (you can check with `nelua -v`). 52 | - Platform (e.g. Windows/MSYS2, Ubuntu Linux, MacOS) 53 | - Architecture (e.g. x86_64, arm64) 54 | - C compiler if your bug has C compiler errors. (e.g. GCC 10.2, Clang 11) 55 | -------------------------------------------------------------------------------- /spec/aster_spec.lua: -------------------------------------------------------------------------------- 1 | local lester = require 'nelua.thirdparty.lester' 2 | local aster = require 'nelua.aster' 3 | local expect = require 'spec.tools.expect' 4 | local Attr = require 'nelua.attr' 5 | local describe, it = lester.describe, lester.it 6 | 7 | local n = aster 8 | 9 | describe("aster", function() 10 | 11 | it("create a valid ASTNode", function() 12 | local node = n.Number{'10'} 13 | assert(node) 14 | expect.equal(node.tag, 'Number') 15 | expect.equal({table.unpack(node)}, {'10'}) 16 | end) 17 | 18 | 19 | it("error on invalid ASTNode", function() 20 | expect.fail(function() n.Invalid{} end) 21 | expect.fail(function() n.Block{1} end) 22 | expect.fail(function() n.Block{{1}} end) 23 | expect.fail(function() n.Block{{'a'}} end, 24 | [[invalid shape while creating AST node "Block"]]) 25 | expect.fail(function() aster:create('Invalid') end) 26 | end) 27 | 28 | it("clone different ASTNode", function() 29 | local node = 30 | n.Block{attr=Attr{someattr = true}, 31 | n.Return{ 32 | n.Nil{attr=Attr{someattr = true}, 33 | }}} 34 | local cloned = node:clone() 35 | assert(cloned.attr.someattr == nil) 36 | assert(#cloned.attr == 0) 37 | assert(cloned ~= node) 38 | assert(cloned[1] ~= node[1]) 39 | assert(cloned[1][1] ~= node[1][1]) 40 | expect.equal(tostring(cloned), [[Block { 41 | Return { 42 | Nil { 43 | } 44 | } 45 | }]]) 46 | end) 47 | 48 | it("pretty", function() 49 | local node = 50 | n.Block{attr=Attr{someattr = true}, 51 | n.Return{ 52 | n.Nil{attr=Attr{someattr = true}, 53 | }}} 54 | expect.equal(aster.pretty(node), [[ 55 | Block 56 | | Return 57 | | | Nil]]) 58 | end) 59 | 60 | it("clone", function() 61 | expect.equal(aster.pretty(aster.clone(n.Id{'x'})), aster.pretty(n.Id{'x'})) 62 | expect.equal(aster.pretty(aster.clone{n.Id{'x'},n.Number{1}}), aster.pretty{n.Id{'x'},n.Number{1}}) 63 | end) 64 | 65 | end) 66 | -------------------------------------------------------------------------------- /spec/utils_spec.lua: -------------------------------------------------------------------------------- 1 | local lester = require 'nelua.thirdparty.lester' 2 | local describe, it = lester.describe, lester.it 3 | 4 | local fs = require 'nelua.utils.fs' 5 | local tabler = require 'nelua.utils.tabler' 6 | 7 | describe("utils", function() 8 | 9 | it("fs.findmodulepath", function() 10 | local function assert_relative_findmodule(s, expected) 11 | expected = fs.abspath(expected) 12 | local neluapath = '/somedir/?.nelua;/somedir/?/init.nelua' 13 | local ok, _, triedpaths = fs.findmodule(s, neluapath, '.', 'nelua') 14 | assert(ok == expected or tabler.ifind(triedpaths, expected)) 15 | end 16 | 17 | assert_relative_findmodule('test', '/somedir/test.nelua') 18 | assert_relative_findmodule('lib.test', '/somedir/lib/test.nelua') 19 | assert_relative_findmodule('test', '/somedir/test/init.nelua') 20 | assert_relative_findmodule('lib.test', '/somedir/lib/test/init.nelua') 21 | assert_relative_findmodule('/tmp/test.nelua', '/tmp/test.nelua') 22 | assert_relative_findmodule('./test.nelua', './test.nelua') 23 | assert_relative_findmodule('../test.nelua', '../test.nelua') 24 | assert_relative_findmodule('./test.nelua', './test.nelua') 25 | assert_relative_findmodule('.test', './test.nelua') 26 | assert_relative_findmodule('.lib.test', './lib/test.nelua') 27 | assert_relative_findmodule('..test', '../test.nelua') 28 | assert_relative_findmodule('..lib.test', '../lib/test.nelua') 29 | assert_relative_findmodule('...test', '../../test.nelua') 30 | assert_relative_findmodule('....test', '../../../test.nelua') 31 | end) 32 | 33 | it("tabler.shallow_compare_nomt", function() 34 | assert(tabler.shallow_compare_nomt({}, {})) 35 | assert(tabler.shallow_compare_nomt({a=1}, {a=1})) 36 | assert(not tabler.shallow_compare_nomt({a=1}, setmetatable({a=1}, {}))) 37 | assert(not tabler.shallow_compare_nomt({a=1}, {a=2})) 38 | assert(not tabler.shallow_compare_nomt({a=1}, {a=1,b=2})) 39 | end) 40 | 41 | end) 42 | -------------------------------------------------------------------------------- /src/lpeglabel/lplcap.h: -------------------------------------------------------------------------------- 1 | /* 2 | ** $Id: lplcap.h $ 3 | */ 4 | 5 | #if !defined(lplcap_h) 6 | #define lplcap_h 7 | 8 | 9 | #include "lpltypes.h" 10 | 11 | 12 | /* kinds of captures */ 13 | typedef enum CapKind { 14 | Cclose, /* not used in trees */ 15 | Cposition, 16 | Cconst, /* ktable[key] is Lua constant */ 17 | Cbackref, /* ktable[key] is "name" of group to get capture */ 18 | Carg, /* 'key' is arg's number */ 19 | Csimple, /* next node is pattern */ 20 | Ctable, /* next node is pattern */ 21 | Cfunction, /* ktable[key] is function; next node is pattern */ 22 | Cquery, /* ktable[key] is table; next node is pattern */ 23 | Cstring, /* ktable[key] is string; next node is pattern */ 24 | Cnum, /* numbered capture; 'key' is number of value to return */ 25 | Csubst, /* substitution capture; next node is pattern */ 26 | Cfold, /* ktable[key] is function; next node is pattern */ 27 | Cruntime, /* not used in trees (is uses another type for tree) */ 28 | Cgroup /* ktable[key] is group's "name" */ 29 | } CapKind; 30 | 31 | 32 | typedef struct Capture { 33 | const char *s; /* subject position */ 34 | unsigned short idx; /* extra info (group name, arg index, etc.) */ 35 | byte kind; /* kind of capture */ 36 | byte siz; /* size of full capture + 1 (0 = not a full capture) */ 37 | } Capture; 38 | 39 | 40 | typedef struct CapState { 41 | Capture *cap; /* current capture */ 42 | Capture *ocap; /* (original) capture list */ 43 | lua_State *L; 44 | int ptop; /* index of last argument to 'match' */ 45 | const char *s; /* original string */ 46 | int valuecached; /* value stored in cache slot */ 47 | int reclevel; /* recursion level */ 48 | } CapState; 49 | 50 | 51 | LUAI_FUNC int runtimecap (CapState *cs, Capture *close, const char *s, int *rem); 52 | LUAI_FUNC int getcaptures (lua_State *L, const char *s, const char *r, int ptop); 53 | LUAI_FUNC int finddyncap (Capture *cap, Capture *last); 54 | 55 | #endif 56 | 57 | 58 | -------------------------------------------------------------------------------- /src/lua/lfunc.h: -------------------------------------------------------------------------------- 1 | /* 2 | ** $Id: lfunc.h $ 3 | ** Auxiliary functions to manipulate prototypes and closures 4 | ** See Copyright Notice in lua.h 5 | */ 6 | 7 | #ifndef lfunc_h 8 | #define lfunc_h 9 | 10 | 11 | #include "lobject.h" 12 | 13 | 14 | #define sizeCclosure(n) (cast_int(offsetof(CClosure, upvalue)) + \ 15 | cast_int(sizeof(TValue)) * (n)) 16 | 17 | #define sizeLclosure(n) (cast_int(offsetof(LClosure, upvals)) + \ 18 | cast_int(sizeof(TValue *)) * (n)) 19 | 20 | 21 | /* test whether thread is in 'twups' list */ 22 | #define isintwups(L) (L->twups != L) 23 | 24 | 25 | /* 26 | ** maximum number of upvalues in a closure (both C and Lua). (Value 27 | ** must fit in a VM register.) 28 | */ 29 | #define MAXUPVAL 255 30 | 31 | 32 | #define upisopen(up) ((up)->v.p != &(up)->u.value) 33 | 34 | 35 | #define uplevel(up) check_exp(upisopen(up), cast(StkId, (up)->v.p)) 36 | 37 | 38 | /* 39 | ** maximum number of misses before giving up the cache of closures 40 | ** in prototypes 41 | */ 42 | #define MAXMISS 10 43 | 44 | 45 | 46 | /* special status to close upvalues preserving the top of the stack */ 47 | #define CLOSEKTOP (-1) 48 | 49 | 50 | LUAI_FUNC Proto *luaF_newproto (lua_State *L); 51 | LUAI_FUNC CClosure *luaF_newCclosure (lua_State *L, int nupvals); 52 | LUAI_FUNC LClosure *luaF_newLclosure (lua_State *L, int nupvals); 53 | LUAI_FUNC void luaF_initupvals (lua_State *L, LClosure *cl); 54 | LUAI_FUNC UpVal *luaF_findupval (lua_State *L, StkId level); 55 | LUAI_FUNC void luaF_newtbcupval (lua_State *L, StkId level); 56 | LUAI_FUNC void luaF_closeupval (lua_State *L, StkId level); 57 | LUAI_FUNC StkId luaF_close (lua_State *L, StkId level, int status, int yy); 58 | LUAI_FUNC void luaF_unlinkupval (UpVal *uv); 59 | LUAI_FUNC void luaF_freeproto (lua_State *L, Proto *f); 60 | LUAI_FUNC const char *luaF_getlocalname (const Proto *func, int local_number, 61 | int pc); 62 | 63 | 64 | #endif 65 | -------------------------------------------------------------------------------- /docs/_layouts/docs.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | {% include head.html %} 5 | 6 | 7 | 8 | {% include navbar.html %} 9 | 10 | 11 |
12 |
13 |
14 | 30 |
31 | 32 |
33 | Edit on GitHub 37 |

{{page.title}}

38 | {{content}} 39 |
40 | {% if page.categories contains 'toc' %} 41 |
42 |
43 | {% include toc.html html=content id="nd-toc" 44 | class="nav.flex-column.flex-nowrap.nav-pills" 45 | submenu_class="nav.flex-column.flex-nowrap" 46 | item_class="nav-item" 47 | anchor_class="nav-link" %} 48 |
49 |
50 | {% endif %} 51 |
52 |
53 | 54 | {% include footer.html %} 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /tests/errorhandling_test.nelua: -------------------------------------------------------------------------------- 1 | require 'errorhandling' 2 | 3 | local fail_defer: boolean = false 4 | local function fail(x: integer): integer 5 | local sb: stringbuilder 6 | sb:write('some', 'stuff') 7 | fail_defer = false 8 | defer fail_defer = true end 9 | if x <= 0 then 10 | error 'error fail' 11 | panic 'impossible' 12 | end 13 | return x 14 | end 15 | 16 | local failproxy_defer: boolean = false 17 | local function failproxy(x: integer): integer 18 | failproxy_defer = false 19 | defer failproxy_defer = true end 20 | return fail(x) 21 | end 22 | 23 | local function xpcall_msghandler(msg: string): string 24 | return msg 25 | end 26 | 27 | do -- pcall 28 | -- fail 29 | local ok, err , ret = pcall(failproxy, 0) 30 | assert(ok == false and ret == 0) 31 | assert(err:find('error fail') > 0) 32 | assert(fail_defer == true and failproxy_defer == true) 33 | -- success 34 | local ok, err , ret = pcall(failproxy, 1) 35 | assert(ok == true and err == '' and ret == 1) 36 | assert(fail_defer == true and failproxy_defer == true) 37 | end 38 | 39 | do -- xpcall 40 | -- fail 41 | local ok, err , ret = xpcall(fail, xpcall_msghandler, 0) 42 | assert(ok == false and ret == 0) 43 | assert(err:find('error fail') > 0) 44 | assert(fail_defer == true and failproxy_defer == true) 45 | -- success 46 | local ok, err , ret = xpcall(fail, xpcall_msghandler, 1) 47 | assert(ok == true and err == '' and ret == 1) 48 | assert(fail_defer == true and failproxy_defer == true) 49 | end 50 | 51 | do -- assert 52 | local ok = assert(true) 53 | assert(ok == true) 54 | local ok, msg = assert(true, 'test') 55 | assert(ok == true and msg == 'test') 56 | local ok, a, b = assert(true, 1, 2) 57 | assert(ok == true and a == 1 and b == 2) 58 | end 59 | 60 | do -- error 61 | local function raiseerr(): integer 62 | error('myerror', 0) 63 | return 0 64 | end 65 | local ok, err = pcall(raiseerr) 66 | assert(ok == false and err == 'myerror') 67 | end 68 | -------------------------------------------------------------------------------- /lualib/nelua/utils/tracker.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Tacker module 3 | 4 | This is an utility used internally to optimize/debug compiler code paths. 5 | ]] 6 | 7 | local tracker = {} 8 | 9 | local cpucycles = require 'nelua.utils.nanotimer'.cpucycles 10 | local tracing = {} 11 | local timings = {} 12 | local counts = {} 13 | 14 | --luacov:disable 15 | 16 | -- Start tracking cycles for section `name`. 17 | function tracker.start(name) 18 | local trace = tracing[name] 19 | if trace then 20 | local depth = trace[2] 21 | trace[2] = depth + 1 22 | counts[name] = counts[name] + 1 23 | if depth == 0 then 24 | trace[1] = cpucycles() 25 | end 26 | else 27 | trace = {0, 1} 28 | tracing[name] = trace 29 | timings[name] = 0 30 | counts[name] = 1 31 | trace[1] = cpucycles() 32 | end 33 | end 34 | 35 | -- Finish tracking cycles for section `name`. 36 | function tracker.finish(name) 37 | local now = cpucycles() 38 | local trace = tracing[name] 39 | local depth = trace[2] 40 | trace[2] = depth - 1 41 | if depth == 1 then 42 | timings[name] = timings[name] + (now - trace[1]) 43 | end 44 | end 45 | 46 | -- Track call count for section `name`. 47 | function tracker.track(name) 48 | if not counts[name] then 49 | counts[name] = 1 50 | else 51 | counts[name] = counts[name] + 1 52 | end 53 | end 54 | 55 | -- Report all measured sections. 56 | function tracker.report() 57 | local list = {} 58 | for name,count in pairs(counts) do 59 | list[#list+1] = {name, count} 60 | end 61 | table.sort(list, function(a,b) return a[2] < b[2] end) 62 | for _,item in ipairs(list) do 63 | local name, count = table.unpack(item) 64 | local timing = timings[name] 65 | if timing then 66 | print(string.format('%s %d %.1f cycles', name, count, timing / count)) 67 | else 68 | print(string.format('%s %d', name, count)) 69 | end 70 | end 71 | end 72 | 73 | --luacov:enable 74 | 75 | -- Make tracker module available in globally (so it's quick to use it). 76 | _G.tracker = tracker 77 | 78 | return tracker 79 | -------------------------------------------------------------------------------- /src/lua/linit.c: -------------------------------------------------------------------------------- 1 | /* 2 | ** $Id: linit.c $ 3 | ** Initialization of libraries for lua.c and other clients 4 | ** See Copyright Notice in lua.h 5 | */ 6 | 7 | 8 | #define linit_c 9 | #define LUA_LIB 10 | 11 | /* 12 | ** If you embed Lua in your program and need to open the standard 13 | ** libraries, call luaL_openlibs in your program. If you need a 14 | ** different set of libraries, copy this file to your project and edit 15 | ** it to suit your needs. 16 | ** 17 | ** You can also *preload* libraries, so that a later 'require' can 18 | ** open the library, which is already linked to the application. 19 | ** For that, do the following code: 20 | ** 21 | ** luaL_getsubtable(L, LUA_REGISTRYINDEX, LUA_PRELOAD_TABLE); 22 | ** lua_pushcfunction(L, luaopen_modname); 23 | ** lua_setfield(L, -2, modname); 24 | ** lua_pop(L, 1); // remove PRELOAD table 25 | */ 26 | 27 | #include "lprefix.h" 28 | 29 | 30 | #include 31 | 32 | #include "lua.h" 33 | 34 | #include "lualib.h" 35 | #include "lauxlib.h" 36 | 37 | 38 | /* 39 | ** these libs are loaded by lua.c and are readily available to any Lua 40 | ** program 41 | */ 42 | static const luaL_Reg loadedlibs[] = { 43 | {LUA_GNAME, luaopen_base}, 44 | {LUA_LOADLIBNAME, luaopen_package}, 45 | {LUA_COLIBNAME, luaopen_coroutine}, 46 | {LUA_TABLIBNAME, luaopen_table}, 47 | {LUA_IOLIBNAME, luaopen_io}, 48 | {LUA_OSLIBNAME, luaopen_os}, 49 | {LUA_STRLIBNAME, luaopen_string}, 50 | {LUA_MATHLIBNAME, luaopen_math}, 51 | {LUA_UTF8LIBNAME, luaopen_utf8}, 52 | {LUA_DBLIBNAME, luaopen_debug}, 53 | {LUA_HASHERLIBNAME, luaopen_hasher}, 54 | {LUA_LFSLIBNAME, luaopen_lfs}, 55 | {LUA_SYSLIBNAME, luaopen_sys}, 56 | {LUA_LPEGLABELLIBNAME, luaopen_lpeglabel}, 57 | {NULL, NULL} 58 | }; 59 | 60 | 61 | LUALIB_API void luaL_openlibs (lua_State *L) { 62 | const luaL_Reg *lib; 63 | /* "require" functions from 'loadedlibs' and set results to global table */ 64 | for (lib = loadedlibs; lib->func; lib++) { 65 | luaL_requiref(L, lib->name, lib->func, 1); 66 | lua_pop(L, 1); /* remove lib */ 67 | } 68 | } 69 | 70 | -------------------------------------------------------------------------------- /lualib/nelua/utils/nanotimer.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Nanotimer class 3 | 4 | The nanotimer is a utility used to count elapsed time in milliseconds. 5 | Used in the compiler to debug compiling time and profiling. 6 | ]] 7 | 8 | -- Find nanotime function in 'sys' or 'chronos' module. 9 | local function get_nanotime() --luacov:disable 10 | local sys = _G.sys 11 | if sys and sys.nanotime then 12 | return sys.nanotime 13 | end 14 | local has_chronos, chronos = pcall(require, 'chronos') 15 | if has_chronos and chronos.nanotime then 16 | return chronos.nanotime 17 | end 18 | return os.clock 19 | end --luacov:enable 20 | 21 | local function get_cpucycles() --luacov:disable 22 | local sys = _G.sys 23 | if sys then 24 | local cpucycles = sys.rdtscp or sys.rdtsc 25 | if cpucycles then 26 | return cpucycles 27 | end 28 | end 29 | local clock, floor = os.clock, math.floor 30 | return function() 31 | return floor(clock() * 1000000) 32 | end 33 | end --luacov:enable 34 | 35 | local nanotime = get_nanotime() 36 | local cpucycles = get_cpucycles() 37 | 38 | -- The nanotimer class is manually instead of using the `class` module to have more efficiency. 39 | local nanotimer = {nanotime = nanotime, cpucycles = cpucycles} 40 | local nanotimer_mt = {__index = nanotimer} 41 | 42 | -- Allow calling nanotimer to create a new timer. 43 | setmetatable(nanotimer, {__call = function(_) 44 | return setmetatable({s = nanotime()}, nanotimer_mt) 45 | end}) 46 | 47 | --luacov:disable 48 | 49 | -- Restart the timer. 50 | function nanotimer.restart(t) 51 | t.s = nanotime() 52 | end 53 | 54 | -- Returns the elapsed time in milliseconds since last restart. 55 | function nanotimer.elapsed(t) 56 | return (nanotime() - t.s) * 1000 57 | end 58 | 59 | --luacov:enable 60 | 61 | -- Restart the timer and returns the elapsed time in milliseconds since last restart. 62 | function nanotimer.elapsedrestart(t) 63 | local s = nanotime() 64 | local e = (s - t.s) * 1000 65 | t.s = s 66 | return e 67 | end 68 | 69 | -- Global timer, used to track overall run time. 70 | nanotimer.globaltimer = nanotimer() 71 | 72 | return nanotimer 73 | -------------------------------------------------------------------------------- /lib/C/time.nelua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Library that imports symbols from the `` header according to C11 specifications. 3 | 4 | For a complete documentation about the functions, 5 | see [C time documentation](https://www.cplusplus.com/reference/ctime/). 6 | ]] 7 | 8 | require 'C' 9 | 10 | -- Types 11 | global C.clock_t: type = @cclock_t 12 | global C.time_t: type = @ctime_t 13 | 14 | global C.tm: type ',ctypedef> = @record{ 15 | tm_sec: cint, 16 | tm_min: cint, 17 | tm_hour: cint, 18 | tm_mday: cint, 19 | tm_mon: cint, 20 | tm_year: cint, 21 | tm_wday: cint, 22 | tm_yday: cint, 23 | tm_isdst: cint 24 | } 25 | global C.timespec: type ',ctypedef> = @record{ 26 | tv_sec: C.time_t, 27 | tv_nsec: clong 28 | } 29 | 30 | -- Time manipulation 31 | 32 | function C.clock(): C.clock_t '> end 33 | function C.difftime(time1: C.time_t, time0: C.time_t): float64 '> end 34 | function C.mktime(tp: *C.tm): C.time_t '> end 35 | function C.strftime(s: cstring, maxsize: csize, format: cstring, tp: *C.tm): csize '> end 36 | function C.time(timer: *C.time_t): C.time_t '> end 37 | 38 | -- Conversion 39 | 40 | function C.asctime(tp: *C.tm): cstring '> end 41 | function C.ctime(timer: *C.time_t): cstring '> end 42 | function C.gmtime(timer: *C.time_t): *C.tm '> end 43 | function C.localtime(timer: *C.time_t): *C.tm '> end 44 | function C.timespec_get(ts: *C.timespec, base: cint): cint '> end 45 | 46 | -- Constants 47 | 48 | global C.CLOCKS_PER_SEC: C.clock_t '> 49 | global C.TIME_UTC: cint '> 50 | 51 | -- C23 52 | 53 | function C.asctime_r(tp: *C.tm, buf: cstring): cstring '> end 54 | function C.ctime_r(timer: *C.time_t, buf: cstring): cstring '> end 55 | function C.gmtime_r(timer: *C.time_t, tp: *C.tm): *C.tm '> end 56 | function C.localtime_r(timer: *C.time_t, tp: *C.tm): *C.tm '> end 57 | 58 | return C 59 | -------------------------------------------------------------------------------- /src/lua/ljumptab.h: -------------------------------------------------------------------------------- 1 | /* 2 | ** $Id: ljumptab.h $ 3 | ** Jump Table for the Lua interpreter 4 | ** See Copyright Notice in lua.h 5 | */ 6 | 7 | 8 | #undef vmdispatch 9 | #undef vmcase 10 | #undef vmbreak 11 | 12 | #define vmdispatch(x) goto *disptab[x]; 13 | 14 | #define vmcase(l) L_##l: 15 | 16 | #define vmbreak vmfetch(); vmdispatch(GET_OPCODE(i)); 17 | 18 | 19 | static const void *const disptab[NUM_OPCODES] = { 20 | 21 | #if 0 22 | ** you can update the following list with this command: 23 | ** 24 | ** sed -n '/^OP_/\!d; s/OP_/\&\&L_OP_/ ; s/,.*/,/ ; s/\/.*// ; p' lopcodes.h 25 | ** 26 | #endif 27 | 28 | &&L_OP_MOVE, 29 | &&L_OP_LOADI, 30 | &&L_OP_LOADF, 31 | &&L_OP_LOADK, 32 | &&L_OP_LOADKX, 33 | &&L_OP_LOADFALSE, 34 | &&L_OP_LFALSESKIP, 35 | &&L_OP_LOADTRUE, 36 | &&L_OP_LOADNIL, 37 | &&L_OP_GETUPVAL, 38 | &&L_OP_SETUPVAL, 39 | &&L_OP_GETTABUP, 40 | &&L_OP_GETTABLE, 41 | &&L_OP_GETI, 42 | &&L_OP_GETFIELD, 43 | &&L_OP_SETTABUP, 44 | &&L_OP_SETTABLE, 45 | &&L_OP_SETI, 46 | &&L_OP_SETFIELD, 47 | &&L_OP_NEWTABLE, 48 | &&L_OP_SELF, 49 | &&L_OP_ADDI, 50 | &&L_OP_ADDK, 51 | &&L_OP_SUBK, 52 | &&L_OP_MULK, 53 | &&L_OP_MODK, 54 | &&L_OP_POWK, 55 | &&L_OP_DIVK, 56 | &&L_OP_IDIVK, 57 | &&L_OP_BANDK, 58 | &&L_OP_BORK, 59 | &&L_OP_BXORK, 60 | &&L_OP_SHRI, 61 | &&L_OP_SHLI, 62 | &&L_OP_ADD, 63 | &&L_OP_SUB, 64 | &&L_OP_MUL, 65 | &&L_OP_MOD, 66 | &&L_OP_POW, 67 | &&L_OP_DIV, 68 | &&L_OP_IDIV, 69 | &&L_OP_BAND, 70 | &&L_OP_BOR, 71 | &&L_OP_BXOR, 72 | &&L_OP_SHL, 73 | &&L_OP_SHR, 74 | &&L_OP_MMBIN, 75 | &&L_OP_MMBINI, 76 | &&L_OP_MMBINK, 77 | &&L_OP_UNM, 78 | &&L_OP_BNOT, 79 | &&L_OP_NOT, 80 | &&L_OP_LEN, 81 | &&L_OP_CONCAT, 82 | &&L_OP_CLOSE, 83 | &&L_OP_TBC, 84 | &&L_OP_JMP, 85 | &&L_OP_EQ, 86 | &&L_OP_LT, 87 | &&L_OP_LE, 88 | &&L_OP_EQK, 89 | &&L_OP_EQI, 90 | &&L_OP_LTI, 91 | &&L_OP_LEI, 92 | &&L_OP_GTI, 93 | &&L_OP_GEI, 94 | &&L_OP_TEST, 95 | &&L_OP_TESTSET, 96 | &&L_OP_CALL, 97 | &&L_OP_TAILCALL, 98 | &&L_OP_RETURN, 99 | &&L_OP_RETURN0, 100 | &&L_OP_RETURN1, 101 | &&L_OP_FORLOOP, 102 | &&L_OP_FORPREP, 103 | &&L_OP_TFORPREP, 104 | &&L_OP_TFORCALL, 105 | &&L_OP_TFORLOOP, 106 | &&L_OP_SETLIST, 107 | &&L_OP_CLOSURE, 108 | &&L_OP_VARARG, 109 | &&L_OP_VARARGPREP, 110 | &&L_OP_EXTRAARG 111 | 112 | }; 113 | -------------------------------------------------------------------------------- /lualib/nelua/utils/traits.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Traits module 3 | 4 | The traits module is used over the code to check if a value is a specific class or Lua type. 5 | 6 | This can be used instead of using the `type()` Lua function in the compiler sources, 7 | because the `type` keyword is overused in the compiler and can sometimes lead to confusion. 8 | ]] 9 | 10 | local traits = {} 11 | 12 | local type = _G.type 13 | local math_type = math.type 14 | 15 | -- Checks if a value is a Lua string. 16 | function traits.is_string(v) 17 | return type(v) == 'string' 18 | end 19 | 20 | -- Checks if a value is a Lua number. 21 | function traits.is_number(v) 22 | return type(v) == 'number' 23 | end 24 | 25 | -- Checks if a value is a Lua table. 26 | function traits.is_table(v) 27 | return type(v) == 'table' 28 | end 29 | 30 | -- Checks if a value is a Lua function. 31 | function traits.is_function(v) 32 | return type(v) == 'function' 33 | end 34 | 35 | -- Checks if a value is a Lua boolean. 36 | function traits.is_boolean(v) 37 | return type(v) == 'boolean' 38 | end 39 | 40 | -- Checsk if a value is a compiler AST node. 41 | function traits.is_astnode(v) 42 | return type(v) == 'table' and v._astnode 43 | end 44 | 45 | -- Checks if a value is a compiler Attr. 46 | function traits.is_attr(v) 47 | return type(v) == 'table' and v._attr 48 | end 49 | 50 | -- Checks if a value is a compiler Symbol. 51 | function traits.is_symbol(v) 52 | return type(v) == 'table' and v._symbol 53 | end 54 | 55 | -- Checks if a value is a compiler Scope. 56 | function traits.is_scope(v) 57 | return type(v) == 'table' and v._scope 58 | end 59 | 60 | -- Checks if a value is a compiler Type. 61 | function traits.is_type(v) 62 | return type(v) == 'table' and v._type 63 | end 64 | 65 | -- Checks if a value is a scalar for the compiler, i.e., a Lua number or a big number. 66 | function traits.is_scalar(v) 67 | local ty = type(v) 68 | return ty == 'number' or (ty == 'table' and v._bn) 69 | end 70 | 71 | -- Checks if a value is a big number. 72 | function traits.is_bn(v) 73 | return type(v) == 'table' and v._bn 74 | end 75 | 76 | -- Checks if a value is an integral (whole number) for the compiler. 77 | function traits.is_integral(v) 78 | return math_type(v) == 'integer' or (type(v) == 'table' and v._bn) 79 | end 80 | 81 | return traits 82 | -------------------------------------------------------------------------------- /docs/Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: https://rubygems.org/ 3 | specs: 4 | addressable (2.8.0) 5 | public_suffix (>= 2.0.2, < 5.0) 6 | colorator (1.1.0) 7 | concurrent-ruby (1.1.8) 8 | em-websocket (0.5.2) 9 | eventmachine (>= 0.12.9) 10 | http_parser.rb (~> 0.6.0) 11 | eventmachine (1.2.7) 12 | ffi (1.15.0) 13 | forwardable-extended (2.6.0) 14 | http_parser.rb (0.6.0) 15 | i18n (1.8.10) 16 | concurrent-ruby (~> 1.0) 17 | jekyll (4.1.1) 18 | addressable (~> 2.4) 19 | colorator (~> 1.0) 20 | em-websocket (~> 0.5) 21 | i18n (~> 1.0) 22 | jekyll-sass-converter (~> 2.0) 23 | jekyll-watch (~> 2.0) 24 | kramdown (~> 2.1) 25 | kramdown-parser-gfm (~> 1.0) 26 | liquid (~> 4.0) 27 | mercenary (~> 0.4.0) 28 | pathutil (~> 0.9) 29 | rouge (~> 3.0) 30 | safe_yaml (~> 1.0) 31 | terminal-table (~> 1.8) 32 | jekyll-feed (0.15.1) 33 | jekyll (>= 3.7, < 5.0) 34 | jekyll-sass-converter (2.1.0) 35 | sassc (> 2.0.1, < 3.0) 36 | jekyll-seo-tag (2.7.1) 37 | jekyll (>= 3.8, < 5.0) 38 | jekyll-sitemap (1.4.0) 39 | jekyll (>= 3.7, < 5.0) 40 | jekyll-watch (2.2.1) 41 | listen (~> 3.0) 42 | kramdown (2.3.1) 43 | rexml 44 | kramdown-parser-gfm (1.1.0) 45 | kramdown (~> 2.0) 46 | liquid (4.0.3) 47 | listen (3.5.1) 48 | rb-fsevent (~> 0.10, >= 0.10.3) 49 | rb-inotify (~> 0.9, >= 0.9.10) 50 | mercenary (0.4.0) 51 | minima (2.5.1) 52 | jekyll (>= 3.5, < 5.0) 53 | jekyll-feed (~> 0.9) 54 | jekyll-seo-tag (~> 2.1) 55 | pathutil (0.16.2) 56 | forwardable-extended (~> 2.6) 57 | public_suffix (4.0.6) 58 | rb-fsevent (0.10.4) 59 | rb-inotify (0.10.1) 60 | ffi (~> 1.0) 61 | rexml (3.2.5) 62 | rouge (3.26.0) 63 | safe_yaml (1.0.5) 64 | sassc (2.4.0) 65 | ffi (~> 1.9) 66 | terminal-table (1.8.0) 67 | unicode-display_width (~> 1.1, >= 1.1.1) 68 | unicode-display_width (1.7.0) 69 | webrick (1.7.0) 70 | 71 | PLATFORMS 72 | x86_64-linux 73 | 74 | DEPENDENCIES 75 | jekyll (~> 4.1.1) 76 | jekyll-feed 77 | jekyll-sitemap 78 | minima (~> 2.5) 79 | tzinfo 80 | tzinfo-data 81 | wdm (~> 0.1.1) 82 | webrick (~> 1.7) 83 | 84 | BUNDLED WITH 85 | 2.2.16 86 | -------------------------------------------------------------------------------- /lib/detail/xoshiro256.nelua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Pseudo Random Number Generator based on Xoshiro256. 3 | 4 | Used internally by the math library. 5 | ]] 6 | 7 | -- Module class. 8 | local Xoshiro256: type = @record{ 9 | state: [4]uint64 10 | } 11 | 12 | -- Left bit rotation utility. 13 | ## local function rotl(x, n) 14 | in (#[x]# << #[n]#) | (#[x]# >> (64 - #[n]#)) 15 | ## end 16 | 17 | -- Generates a random unsigned integral number. 18 | function Xoshiro256:nextrand(): uint64 19 | local state: [4]uint64 = { 20 | self.state[0], 21 | self.state[1], 22 | self.state[2] ~ self.state[0], 23 | self.state[3] ~ self.state[1] 24 | } 25 | local res: uint64 = #[rotl]#(state[1] * 5, 7) * 9 26 | self.state[0] = state[0] ~ state[3] 27 | self.state[1] = state[1] ~ state[2] 28 | self.state[2] = state[2] ~ (state[1] << 17) 29 | self.state[3] = #[rotl]#(state[3], 45) 30 | return res 31 | end 32 | 33 | -- Generates a random unsigned integer in interval [0, n]. 34 | function Xoshiro256:randomuint(n: uint64): uint64 35 | local x: uint64 = self:nextrand() 36 | if n & (n + 1) == 0 then -- is 'n + 1' a power of 2? 37 | x = x & n -- no bias 38 | else -- project the random integer in interval is a power of 2 to maintain uniform property 39 | -- compute the smallest (2^b - 1) not smaller than 'n' 40 | local lim: uint64 = n 41 | lim = lim | (lim >> 1) 42 | lim = lim | (lim >> 2) 43 | lim = lim | (lim >> 4) 44 | lim = lim | (lim >> 8) 45 | lim = lim | (lim >> 16) 46 | lim = lim | (lim >> 32) 47 | -- project 'x' into [0..lim] 48 | x = x & lim 49 | -- check((lim & (lim + 1)) == 0 and lim >= n and (lim >> 1) < n) 50 | while x > n do -- not inside [0..n]? try again 51 | x = self:nextrand() & lim 52 | end 53 | end 54 | return x 55 | end 56 | 57 | -- Generates a random float number in interval [0, 1). 58 | function Xoshiro256:randomfloat(): number 59 | ## local FIGS = math.min(primtypes.number.mantdigits, 64) 60 | return (self:nextrand() >> #[64 - FIGS]#) * (0.5 / (1_u64 << #[FIGS-1]#)) 61 | end 62 | 63 | -- Reinitialize the generator seed. 64 | function Xoshiro256:seed(a: uint64, b: uint64): void 65 | self.state[0] = a 66 | self.state[1] = 0xff 67 | self.state[2] = b 68 | self.state[3] = 0 69 | for i:usize=0,<16 do 70 | self:nextrand() 71 | end 72 | end 73 | 74 | return Xoshiro256 75 | -------------------------------------------------------------------------------- /src/lua/ldebug.h: -------------------------------------------------------------------------------- 1 | /* 2 | ** $Id: ldebug.h $ 3 | ** Auxiliary functions from Debug Interface module 4 | ** See Copyright Notice in lua.h 5 | */ 6 | 7 | #ifndef ldebug_h 8 | #define ldebug_h 9 | 10 | 11 | #include "lstate.h" 12 | 13 | 14 | #define pcRel(pc, p) (cast_int((pc) - (p)->code) - 1) 15 | 16 | 17 | /* Active Lua function (given call info) */ 18 | #define ci_func(ci) (clLvalue(s2v((ci)->func.p))) 19 | 20 | 21 | #define resethookcount(L) (L->hookcount = L->basehookcount) 22 | 23 | /* 24 | ** mark for entries in 'lineinfo' array that has absolute information in 25 | ** 'abslineinfo' array 26 | */ 27 | #define ABSLINEINFO (-0x80) 28 | 29 | 30 | /* 31 | ** MAXimum number of successive Instructions WiTHout ABSolute line 32 | ** information. (A power of two allows fast divisions.) 33 | */ 34 | #if !defined(MAXIWTHABS) 35 | #define MAXIWTHABS 128 36 | #endif 37 | 38 | 39 | LUAI_FUNC int luaG_getfuncline (const Proto *f, int pc); 40 | LUAI_FUNC const char *luaG_findlocal (lua_State *L, CallInfo *ci, int n, 41 | StkId *pos); 42 | LUAI_FUNC l_noret luaG_typeerror (lua_State *L, const TValue *o, 43 | const char *opname); 44 | LUAI_FUNC l_noret luaG_callerror (lua_State *L, const TValue *o); 45 | LUAI_FUNC l_noret luaG_forerror (lua_State *L, const TValue *o, 46 | const char *what); 47 | LUAI_FUNC l_noret luaG_concaterror (lua_State *L, const TValue *p1, 48 | const TValue *p2); 49 | LUAI_FUNC l_noret luaG_opinterror (lua_State *L, const TValue *p1, 50 | const TValue *p2, 51 | const char *msg); 52 | LUAI_FUNC l_noret luaG_tointerror (lua_State *L, const TValue *p1, 53 | const TValue *p2); 54 | LUAI_FUNC l_noret luaG_ordererror (lua_State *L, const TValue *p1, 55 | const TValue *p2); 56 | LUAI_FUNC l_noret luaG_runerror (lua_State *L, const char *fmt, ...); 57 | LUAI_FUNC const char *luaG_addinfo (lua_State *L, const char *msg, 58 | TString *src, int line); 59 | LUAI_FUNC l_noret luaG_errormsg (lua_State *L); 60 | LUAI_FUNC int luaG_traceexec (lua_State *L, const Instruction *pc); 61 | 62 | 63 | #endif 64 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: "test" 2 | 3 | on: [push] 4 | 5 | jobs: 6 | test: 7 | strategy: 8 | fail-fast: false 9 | matrix: 10 | os: [ubuntu-latest, macos-latest, windows-2025] 11 | cc: [gcc, clang] 12 | exclude: 13 | - os: macos-latest 14 | cc: gcc 15 | runs-on: ${{matrix.os}} 16 | env: 17 | CC: ${{matrix.cc}} 18 | CFLAGS: -Wextra -Werror -Wno-implicit-fallthrough 19 | steps: 20 | - uses: actions/checkout@master 21 | with: 22 | fetch-depth: 0 23 | - name: Show C compiler information 24 | run: ${{matrix.cc}} -v 25 | - name: Compile 26 | run: make CC=${{matrix.cc}} 27 | - name: Test 28 | run: make test CC=${{matrix.cc}} 29 | - name: Install (Unix) 30 | if: ${{ matrix.os != 'windows-2025' }} 31 | run: sudo make install 32 | - name: Install (Windows) 33 | if: ${{ matrix.os == 'windows-2025' }} 34 | run: make install 35 | - name: Run hello world example 36 | if: ${{ matrix.os != 'windows-2025' }} 37 | run: nelua --verbose examples/helloworld.nelua 38 | 39 | test-32bits: 40 | runs-on: ubuntu-latest 41 | env: 42 | CFLAGS: -m32 43 | steps: 44 | - uses: actions/checkout@master 45 | - name: Install dependencies 46 | run: sudo apt-get update && sudo apt-get install gcc-multilib 47 | - name: Compile 48 | run: make 49 | - name: Test 50 | run: make test 51 | 52 | coverage: 53 | runs-on: ubuntu-latest 54 | steps: 55 | - uses: actions/checkout@master 56 | - uses: leafo/gh-actions-lua@v11 57 | - uses: leafo/gh-actions-luarocks@v5 58 | - name: Install luacov 59 | run: luarocks install cluacov 60 | - name: Coverage 61 | run: | 62 | echo "return {}" > .neluacfg.lua 63 | make coverage-test 64 | 65 | check: 66 | runs-on: ubuntu-latest 67 | steps: 68 | - uses: actions/checkout@master 69 | - uses: leafo/gh-actions-lua@v11 70 | - uses: leafo/gh-actions-luarocks@v5 71 | - name: Install luacheck 72 | run: luarocks install luacheck 73 | - name: Check 74 | run: luacheck . 75 | 76 | sanitize: 77 | runs-on: ubuntu-latest 78 | env: 79 | CFLAGS: -fsanitize=address -fsanitize=undefined 80 | steps: 81 | - uses: actions/checkout@master 82 | - name: Compile 83 | run: make 84 | - name: Test 85 | run: make test 86 | -------------------------------------------------------------------------------- /src/lua/ltable.h: -------------------------------------------------------------------------------- 1 | /* 2 | ** $Id: ltable.h $ 3 | ** Lua tables (hash) 4 | ** See Copyright Notice in lua.h 5 | */ 6 | 7 | #ifndef ltable_h 8 | #define ltable_h 9 | 10 | #include "lobject.h" 11 | 12 | 13 | #define gnode(t,i) (&(t)->node[i]) 14 | #define gval(n) (&(n)->i_val) 15 | #define gnext(n) ((n)->u.next) 16 | 17 | 18 | /* 19 | ** Clear all bits of fast-access metamethods, which means that the table 20 | ** may have any of these metamethods. (First access that fails after the 21 | ** clearing will set the bit again.) 22 | */ 23 | #define invalidateTMcache(t) ((t)->flags &= ~maskflags) 24 | 25 | 26 | /* true when 't' is using 'dummynode' as its hash part */ 27 | #define isdummy(t) ((t)->lastfree == NULL) 28 | 29 | 30 | /* allocated size for hash nodes */ 31 | #define allocsizenode(t) (isdummy(t) ? 0 : sizenode(t)) 32 | 33 | 34 | /* returns the Node, given the value of a table entry */ 35 | #define nodefromval(v) cast(Node *, (v)) 36 | 37 | 38 | LUAI_FUNC const TValue *luaH_getint (Table *t, lua_Integer key); 39 | LUAI_FUNC void luaH_setint (lua_State *L, Table *t, lua_Integer key, 40 | TValue *value); 41 | LUAI_FUNC const TValue *luaH_getshortstr (Table *t, TString *key); 42 | LUAI_FUNC const TValue *luaH_getstr (Table *t, TString *key); 43 | LUAI_FUNC const TValue *luaH_get (Table *t, const TValue *key); 44 | LUAI_FUNC void luaH_newkey (lua_State *L, Table *t, const TValue *key, 45 | TValue *value); 46 | LUAI_FUNC void luaH_set (lua_State *L, Table *t, const TValue *key, 47 | TValue *value); 48 | LUAI_FUNC void luaH_finishset (lua_State *L, Table *t, const TValue *key, 49 | const TValue *slot, TValue *value); 50 | LUAI_FUNC Table *luaH_new (lua_State *L); 51 | LUAI_FUNC void luaH_resize (lua_State *L, Table *t, unsigned int nasize, 52 | unsigned int nhsize); 53 | LUAI_FUNC void luaH_resizearray (lua_State *L, Table *t, unsigned int nasize); 54 | LUAI_FUNC void luaH_free (lua_State *L, Table *t); 55 | LUAI_FUNC int luaH_next (lua_State *L, Table *t, StkId key); 56 | LUAI_FUNC lua_Unsigned luaH_getn (Table *t); 57 | LUAI_FUNC unsigned int luaH_realasize (const Table *t); 58 | 59 | 60 | #if defined(LUA_DEBUG) 61 | LUAI_FUNC Node *luaH_mainposition (const Table *t, const TValue *key); 62 | #endif 63 | 64 | 65 | #endif 66 | -------------------------------------------------------------------------------- /lualib/nelua/utils/shaper.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Shaper module 3 | 4 | This is an interface to the `tableshape` module with additional 5 | shape checkers to be used in the compiler. 6 | ]] 7 | 8 | local shaper = require 'nelua.thirdparty.tableshape'.types 9 | local tabler = require 'nelua.utils.tabler' 10 | local traits = require 'nelua.utils.traits' 11 | local class = require 'nelua.utils.class' 12 | 13 | -- Shape for an optional boolean. 14 | shaper.optional_boolean = shaper.boolean:is_optional() 15 | 16 | -- Shape for a value that can be either `false` or `nil`. 17 | shaper.falsy = shaper.custom(function(v) 18 | return not v, 'expected false or nil' 19 | end) 20 | 21 | -- Shape for a scalar value (a lua number or a big number). 22 | shaper.scalar = shaper.custom(function(v) 23 | return traits.is_scalar(v), 'expected an scalar' 24 | end):describe('scalar') 25 | 26 | -- Shape for an integral number. 27 | shaper.integral = shaper.custom(function(v) 28 | return traits.is_integral(v), 'expected an integral' 29 | end):describe('integral') 30 | 31 | -- Shape for an AST Node. 32 | shaper.astnode = shaper.custom(function(v) 33 | return traits.is_astnode(v), 'expected a node' 34 | end):describe('ASTNode') 35 | 36 | -- Shape for an Attr. 37 | shaper.attr = shaper.custom(function(v) 38 | return traits.is_attr(v), 'expected an attr' 39 | end):describe('Attr') 40 | 41 | -- Shape for a symbol. 42 | shaper.symbol = shaper.custom(function(v) 43 | return traits.is_symbol(v), 'expected a symbol' 44 | end):describe('Symbol') 45 | 46 | -- Shape for a Scope. 47 | shaper.scope = shaper.custom(function(v) 48 | return traits.is_scope(v), 'expected a scope' 49 | end):describe('Scope') 50 | 51 | -- Shape for a Type. 52 | shaper.type = shaper.custom(function(v) 53 | return traits.is_type(v), 'expected a type' 54 | end):describe('Type') 55 | 56 | -- Utility to create a type check to check whether a shape is an ASTNode. 57 | function shaper.ast_node_of(nodeklass) 58 | return shaper.custom(function(val) 59 | if class.is(val, nodeklass) then return true end 60 | return nil, string.format('expected type aster.%s, got "%s"', nodeklass.tag, type(val)) 61 | end):describe('"aster.'..nodeklass.tag..'"') 62 | end 63 | 64 | -- Utility to fork a shape definition from another shape definition. 65 | function shaper.fork_shape(baseshape, desc) 66 | local shape = shaper.shape(desc) 67 | tabler.update(shape.shape, baseshape.shape) 68 | return shape 69 | end 70 | 71 | return shaper 72 | -------------------------------------------------------------------------------- /src/lpeglabel/lplvm.h: -------------------------------------------------------------------------------- 1 | /* 2 | ** $Id: lplvm.h $ 3 | */ 4 | 5 | #if !defined(lplvm_h) 6 | #define lplvm_h 7 | 8 | #include "lplcap.h" 9 | 10 | 11 | /* Virtual Machine's instructions */ 12 | typedef enum Opcode { 13 | IAny, /* if no char, fail */ 14 | IChar, /* if char != aux, fail */ 15 | ISet, /* if char not in buff, fail */ 16 | ITestAny, /* in no char, jump to 'offset' */ 17 | ITestChar, /* if char != aux, jump to 'offset' */ 18 | ITestSet, /* if char not in buff, jump to 'offset' */ 19 | ISpan, /* read a span of chars in buff */ 20 | IUTFR, /* if codepoint not in range [offset, utf_to], fail */ 21 | IBehind, /* walk back 'aux' characters (fail if not possible) */ 22 | IRet, /* return from a rule */ 23 | IEnd, /* end of pattern */ 24 | IChoice, /* stack a choice; next fail will jump to 'offset' */ 25 | IPredChoice, /* labeld failure: stack a choice; changes label env next fail will jump to 'offset' */ /*labeled failure */ 26 | IJmp, /* jump to 'offset' */ 27 | ICall, /* call rule at 'offset' */ 28 | IOpenCall, /* call rule number 'key' (must be closed to a ICall) */ 29 | ICommit, /* pop choice and jump to 'offset' */ 30 | IPartialCommit, /* update top choice to current position and jump */ 31 | IBackCommit, /* backtrack like "fail" but jump to its own 'offset' */ 32 | IFailTwice, /* pop one choice and then fail */ 33 | IFail, /* go back to saved state on choice and jump to saved offset */ 34 | IGiveup, /* internal use */ 35 | IFullCapture, /* complete capture of last 'off' chars */ 36 | IOpenCapture, /* start a capture */ 37 | ICloseCapture, 38 | ICloseRunTime, 39 | IThrow, /* fails with a given label */ /*labeled failure */ 40 | IThrowRec, /* fails with a given label and call rule at 'offset' */ /*labeled failure */ 41 | IEmpty /* to fill empty slots left by optimizations */ 42 | } Opcode; 43 | 44 | 45 | 46 | typedef union Instruction { 47 | struct Inst { 48 | byte code; 49 | byte aux; 50 | short key; 51 | } i; 52 | int offset; 53 | byte buff[1]; 54 | } Instruction; 55 | 56 | 57 | /* extract 24-bit value from an instruction */ 58 | #define utf_to(inst) (((inst)->i.key << 8) | (inst)->i.aux) 59 | 60 | 61 | LUAI_FUNC void printpatt (Instruction *p, int n); 62 | LUAI_FUNC const char *match (lua_State *L, const char *o, const char *s, const char *e, 63 | Instruction *op, Capture *capture, int ptop, short *labelf, const char **sfail); /* labeled failure */ 64 | 65 | #endif 66 | 67 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | ## Contributing to Nelua 2 | 3 | Hello contributors! Welcome to Nelua! 4 | 5 | Do you enjoy Nelua and want to contribute? Nice! You can help with the following points: 6 | 7 | - Documentation - Can you write or improve the documentation? 8 | - Tutorials - Can you write a tutorial? 9 | - Examples - Can you share something cool made with Nelua? 10 | - Testing - Can you find bugs? 11 | - Support - Can you help newcomers? 12 | 13 | ### Opening new Issues 14 | 15 | To open new issue for Nelua (bug or enhancement), just try to follow these rules: 16 | 17 | - If you need support, have questions, ideas to discuss you should use the [Nelua Discussions](https://github.com/edubart/nelua-lang/discussions) forum or the [Nelua Discord server](https://discord.gg/7aaGeG7) instead of opening a new issue. 18 | - Make sure you test the issue on the latest master branch. 19 | - Make sure the issue has not already been reported before by searching on GitHub under issues. 20 | - Make sure you are not misusing Nelua. 21 | - Be sure to include a title, a clear description, expected behavior, relevant information, and a code sample demonstrating the unexpected behavior. 22 | 23 | ### Sending a Pull Request 24 | 25 | Nelua is open source, 26 | but not very open to contributions in the form of pull requests, 27 | if you would like something fixed or implemented in the core language 28 | try first submitting a bug report or opening a discussion instead of doing a PR. 29 | The authors prefer it this way, so that the ideal solution is always provided, 30 | without unwanted consequences on the project, thus keeping the quality of the software. 31 | 32 | If you insist doing a PR, typically for a small bug fix, then follow these guidelines: 33 | 34 | - Make sure the PR description clearly describes the problem and solution. Include the relevant issue number if applicable. 35 | - Don't send big pull requests (lots of changes), they are difficult to review. It's better to send small pull requests, one at a time. 36 | - Use different pull requests for different issues, each pull request should only address one issue. 37 | - When fixing a bug or adding a feature add tests related to your changes to assure the changes will always work as intended in the future and also to cover new lines added. 38 | - Verify that changes don't break the tests. 39 | - Don't change the code style, follow the same coding style rules as the code base. 40 | - Pull requests just doing style changes are not welcome, a PR must address a real issue. 41 | -------------------------------------------------------------------------------- /src/onelua.c: -------------------------------------------------------------------------------- 1 | /* 2 | * one.c -- Lua core, libraries, and interpreter in a single file 3 | */ 4 | 5 | /* default is to build the full interpreter */ 6 | #ifndef MAKE_LIB 7 | #ifndef MAKE_LUAC 8 | #ifndef MAKE_LUA 9 | #define MAKE_LUA 10 | #endif 11 | #endif 12 | #endif 13 | 14 | /* choose suitable platform-specific features */ 15 | /* some of these may need extra libraries such as -ldl -lreadline -lncurses */ 16 | #if 0 17 | #define LUA_USE_LINUX 18 | #define LUA_USE_MACOSX 19 | #define LUA_USE_POSIX 20 | #define LUA_ANSI 21 | #endif 22 | 23 | /* no need to change anything below this line ----------------------------- */ 24 | 25 | #include "lprefix.h" 26 | 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | 43 | 44 | /* setup for luaconf.h */ 45 | #define LUA_CORE 46 | #define LUA_LIB 47 | #define ltable_c 48 | #define lvm_c 49 | #include "luaconf.h" 50 | 51 | /* do not export internal symbols */ 52 | #undef LUAI_FUNC 53 | #undef LUAI_DDEC 54 | #undef LUAI_DDEF 55 | #define LUAI_FUNC static 56 | #define LUAI_DDEC(def) /* empty */ 57 | #define LUAI_DDEF static 58 | 59 | /* core -- used by all */ 60 | #include "lzio.c" 61 | #include "lctype.c" 62 | #include "lopcodes.c" 63 | #include "lmem.c" 64 | #include "lundump.c" 65 | #include "ldump.c" 66 | #include "lstate.c" 67 | #include "lgc.c" 68 | #include "llex.c" 69 | #include "lcode.c" 70 | #include "lparser.c" 71 | #include "ldebug.c" 72 | #include "lfunc.c" 73 | #include "lobject.c" 74 | #include "ltm.c" 75 | #include "lstring.c" 76 | #include "ltable.c" 77 | #include "ldo.c" 78 | #include "lvm.c" 79 | #include "lapi.c" 80 | 81 | /* auxiliary library -- used by all */ 82 | #include "lauxlib.c" 83 | 84 | /* standard library -- not used by luac */ 85 | #ifndef MAKE_LUAC 86 | #include "lbaselib.c" 87 | #include "lcorolib.c" 88 | #include "ldblib.c" 89 | #include "liolib.c" 90 | #include "lmathlib.c" 91 | #include "loadlib.c" 92 | #include "loslib.c" 93 | #include "lstrlib.c" 94 | #include "ltablib.c" 95 | #include "lutf8lib.c" 96 | #include "linit.c" 97 | #endif 98 | 99 | /* lua */ 100 | #ifdef MAKE_LUA 101 | #include "lua.c" 102 | #endif 103 | 104 | /* luac */ 105 | #ifdef MAKE_LUAC 106 | #include "luac.c" 107 | #endif 108 | -------------------------------------------------------------------------------- /lib/traits.nelua: -------------------------------------------------------------------------------- 1 | --[[ 2 | The traits library provides utilities to gather type information. 3 | ]] 4 | 5 | ##[[ 6 | -- Counter that increment on every new defined type that are fundamentally different. 7 | local typeid_counter = 0 8 | 9 | -- Table of type's id by its codename. 10 | local typeid_by_codename = {} 11 | local function get_typeidof(vtype) 12 | local id = typeid_by_codename[vtype.codename] 13 | if not id then -- generate an id 14 | id = typeid_counter 15 | typeid_counter = typeid_counter + 1 16 | typeid_by_codename[vtype.codename] = id 17 | end 18 | return id 19 | end 20 | ]] 21 | -- Namespace for traits module. 22 | global traits: type = @record{} 23 | 24 | -- Type of the identifier for types. 25 | global traits.typeid: type = @uint32 26 | 27 | -- Record for type information. 28 | global traits.typeinfo: type = @record{ 29 | id: traits.typeid, 30 | name: string, 31 | nickname: string, 32 | codename: string 33 | } 34 | 35 | --[[ 36 | Returns the `typeid` of `v`. 37 | The given `v` can be either a runtime value or a compile-time type. 38 | ]] 39 | function traits.typeidof(v: auto): traits.typeid 40 | ## local vtype = v.type.is_type and v.value or v.type 41 | return #[get_typeidof(vtype)]# 42 | end 43 | 44 | --[[ 45 | Returns type information of `v`. 46 | The given `v` can be either a runtime value or a compile-time type. 47 | ]] 48 | function traits.typeinfoof(v: auto): traits.typeinfo 49 | ## local vtype = v.type.is_type and v.value or v.type 50 | return (@traits.typeinfo){ 51 | id = #[get_typeidof(vtype)]#, 52 | name = #[vtype.name]#, 53 | nickname = #[vtype.nickname or '']#, 54 | codename = #[vtype.codename]# 55 | } 56 | end 57 | 58 | --[[ 59 | Returns the type of `v`, coded as a string, as follows: 60 | * `"nil"` for `niltype` 61 | * `"pointer"` for pointers and `nilptr` 62 | * `"number"` for scalar types (including enums) 63 | * `"string"` for types that can represent a string 64 | * `"record"` for records 65 | * `"union"` for unions 66 | * `"type"` for compile-time types 67 | * `"function"` for functions 68 | * `"polyfunction"` for polymorphic functions 69 | 70 | This function behaves as describe to be compatible with Lua APIs. 71 | ]] 72 | global function type(v: auto): string 73 | ## if v.type.is_niltype then 74 | return 'nil' 75 | ## elseif v.type.is_nilptr then 76 | return 'pointer' 77 | ## elseif v.type.is_scalar then 78 | return 'number' 79 | ## elseif v.type.is_stringy then 80 | return 'string' 81 | ## else 82 | return #[v.type.name]# 83 | ## end 84 | end 85 | 86 | return traits 87 | -------------------------------------------------------------------------------- /docs/_config.yml: -------------------------------------------------------------------------------- 1 | # Welcome to Jekyll! 2 | # 3 | # This config file is meant for settings that affect your whole blog, values 4 | # which you are expected to set up once and rarely edit after that. If you find 5 | # yourself editing this file very often, consider using Jekyll's data files 6 | # feature for the data you need to update frequently. 7 | # 8 | # For technical reasons, this file is *NOT* reloaded automatically when you use 9 | # 'bundle exec jekyll serve'. If you change this file, please restart the server process. 10 | # 11 | # If you need help with YAML syntax, here are some quick references for you: 12 | # https://learn-the-web.algonquindesign.ca/topics/markdown-yaml-cheat-sheet/#yaml 13 | # https://learnxinyminutes.com/docs/yaml/ 14 | # 15 | # Site settings 16 | # These are used to personalize your new site. If you look in the HTML files, 17 | # you will see them accessed via {{ site.title }}, {{ site.email }}, and so on. 18 | # You can create any custom variable you would like, and they will be accessible 19 | # in the templates via {{ site.myvariable }}. 20 | 21 | title: Nelua 22 | email: edub4rt@gmail.com 23 | description: >- # this means to ignore newlines until "baseurl:" 24 | Minimal, simple, efficient, statically typed, compiled, meta programmable, 25 | safe and extensible systems programming language with a Lua flavor. 26 | baseurl: "" # the subpath of your site, e.g. /blog 27 | url: "https://nelua.io" # the base hostname & protocol for your site, e.g. http://example.com 28 | twitter_username: jekyllrb 29 | github_username: jekyll 30 | 31 | # Build settings 32 | markdown: kramdown 33 | kramdown: 34 | input: GFM 35 | hard_wrap: false 36 | syntax_highlighter_opts: 37 | disable: true 38 | theme: minima 39 | plugins: 40 | - jekyll-feed 41 | - jekyll-sitemap 42 | algolia: 43 | index_name: nelua 44 | search_only_api_key: c8cc0350725c7374769edcdcfc48aeef 45 | 46 | #application_id: 80N9D5X8WH 47 | #index_name: docsearch 48 | #search_only_api_key: c2fb3443da3264ee960c872ad6f31c5b 49 | 50 | # Exclude from processing. 51 | # The following items will not be processed, by default. 52 | # Any item listed under the `exclude:` key here will be automatically added to 53 | # the internal "default list". 54 | # 55 | # Excluded items can be processed by explicitly listing the directories or 56 | # their entries' file path in the `include:` list. 57 | # 58 | # exclude: 59 | # - .sass-cache/ 60 | # - .jekyll-cache/ 61 | # - gemfiles/ 62 | # - Gemfile 63 | # - Gemfile.lock 64 | # - node_modules/ 65 | # - vendor/bundle/ 66 | # - vendor/cache/ 67 | # - vendor/gems/ 68 | # - vendor/ruby/ 69 | -------------------------------------------------------------------------------- /src/lpeglabel/lpltree.h: -------------------------------------------------------------------------------- 1 | /* 2 | ** $Id: lpltree.h $ 3 | */ 4 | 5 | #if !defined(lpltree_h) 6 | #define lpltree_h 7 | 8 | 9 | #include "lpltypes.h" 10 | 11 | 12 | /* 13 | ** types of trees 14 | */ 15 | typedef enum TTag { 16 | TChar = 0, /* 'n' = char */ 17 | TSet, /* the set is stored in next CHARSETSIZE bytes */ 18 | TAny, 19 | TTrue, 20 | TFalse, 21 | TUTFR, /* range of UTF-8 codepoints; 'n' has initial codepoint; 22 | 'cap' has length; 'key' has first byte; 23 | extra info is similar for end codepoint */ 24 | TRep, /* 'sib1'* */ 25 | TSeq, /* 'sib1' 'sib2' */ 26 | TChoice, /* 'sib1' / 'sib2' */ 27 | TNot, /* !'sib1' */ 28 | TAnd, /* &'sib1' */ 29 | TCall, /* ktable[key] is rule's key; 'sib2' is rule being called */ 30 | TOpenCall, /* ktable[key] is rule's key */ 31 | TRule, /* ktable[key] is rule's key (but key == 0 for unused rules); 32 | 'sib1' is rule's pattern pre-rule; 'sib2' is next rule; 33 | extra info 'n' is rule's sequential number */ 34 | TXInfo, /* extra info */ 35 | TGrammar, /* 'sib1' is initial (and first) rule */ 36 | TBehind, /* 'sib1' is pattern, 'n' is how much to go back */ 37 | TCapture, /* captures: 'cap' is kind of capture (enum 'CapKind'); 38 | ktable[key] is Lua value associated with capture; 39 | 'sib1' is capture body */ 40 | TRunTime, /* run-time capture: 'key' is Lua function; 41 | 'sib1' is capture body */ 42 | TThrow, /* labeled failure: ktable[key] is label's name */ 43 | 44 | } TTag; 45 | 46 | 47 | /* 48 | ** Tree trees 49 | ** The first child of a tree (if there is one) is immediately after 50 | ** the tree. A reference to a second child (ps) is its position 51 | ** relative to the position of the tree itself. 52 | */ 53 | typedef struct TTree { 54 | byte tag; 55 | byte cap; /* kind of capture (if it is a capture) */ 56 | unsigned short key; /* key in ktable for Lua data (0 if no key) */ 57 | union { 58 | int ps; /* occasional second child */ 59 | int n; /* occasional counter */ 60 | } u; 61 | } TTree; 62 | 63 | 64 | /* 65 | ** A complete pattern has its tree plus, if already compiled, 66 | ** its corresponding code 67 | */ 68 | typedef struct Pattern { 69 | union Instruction *code; 70 | int codesize; 71 | TTree tree[1]; 72 | } Pattern; 73 | 74 | 75 | /* number of children for each tree */ 76 | LUAI_FUNC const byte numsiblings[]; 77 | 78 | /* access to children */ 79 | #define sib1(t) ((t) + 1) 80 | #define sib2(t) ((t) + (t)->u.ps) 81 | 82 | 83 | 84 | 85 | 86 | 87 | #endif 88 | 89 | -------------------------------------------------------------------------------- /lib/C/string.nelua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Library that imports symbols from the `` header according to C11 specifications. 3 | 4 | For a complete documentation about the functions, 5 | see [C string documentation](https://www.cplusplus.com/reference/cstring/). 6 | ]] 7 | 8 | require 'C' 9 | 10 | -- Memory manipulation 11 | 12 | function C.memcpy(dest: pointer, src: pointer, n: csize): pointer '> end 13 | function C.memmove(dest: pointer, src: pointer, n: csize): pointer '> end 14 | function C.memset(s: pointer, c: cint, n: csize): pointer '> end 15 | function C.memcmp(s1: pointer, s2: pointer, n: csize): cint '> end 16 | function C.memchr(s: pointer, c: cint, n: csize): pointer '> end 17 | 18 | -- String examination 19 | 20 | function C.strcpy(dest: cstring, src: cstring): cstring '> end 21 | function C.strncpy(dest: cstring, src: cstring, n: csize): cstring '> end 22 | function C.strcat(dest: cstring, src: cstring): cstring '> end 23 | function C.strncat(dest: cstring, src: cstring, n: csize): cstring '> end 24 | function C.strcmp(s1: cstring, s2: cstring): cint '> end 25 | function C.strncmp(s1: cstring, s2: cstring, n: csize): cint '> end 26 | function C.strcoll(s1: cstring, s2: cstring): cint '> end 27 | function C.strxfrm(dest: cstring, src: cstring, n: csize): csize '> end 28 | function C.strchr(s: cstring, c: cint): cstring '> end 29 | function C.strrchr(s: cstring, c: cint): cstring '> end 30 | function C.strcspn(s: cstring, reject: cstring): csize '> end 31 | function C.strspn(s: cstring, accept: cstring): csize '> end 32 | function C.strpbrk(s: cstring, accept: cstring): cstring '> end 33 | function C.strstr(haystack: cstring, needle: cstring): cstring '> end 34 | function C.strlen(s: cstring): csize '> end 35 | function C.strerror(errnum: cint): cstring '> end 36 | 37 | -- C23 38 | 39 | function C.memccpy(dest: pointer, src: pointer, c: cint, n: csize): pointer '> end 40 | function C.strdup(s: cstring): cstring '> end 41 | function C.strndup(string: cstring, n: csize): cstring '> end 42 | 43 | return C 44 | -------------------------------------------------------------------------------- /src/lua/lctype.h: -------------------------------------------------------------------------------- 1 | /* 2 | ** $Id: lctype.h $ 3 | ** 'ctype' functions for Lua 4 | ** See Copyright Notice in lua.h 5 | */ 6 | 7 | #ifndef lctype_h 8 | #define lctype_h 9 | 10 | #include "lua.h" 11 | 12 | 13 | /* 14 | ** WARNING: the functions defined here do not necessarily correspond 15 | ** to the similar functions in the standard C ctype.h. They are 16 | ** optimized for the specific needs of Lua. 17 | */ 18 | 19 | #if !defined(LUA_USE_CTYPE) 20 | 21 | #if 'A' == 65 && '0' == 48 22 | /* ASCII case: can use its own tables; faster and fixed */ 23 | #define LUA_USE_CTYPE 0 24 | #else 25 | /* must use standard C ctype */ 26 | #define LUA_USE_CTYPE 1 27 | #endif 28 | 29 | #endif 30 | 31 | 32 | #if !LUA_USE_CTYPE /* { */ 33 | 34 | #include 35 | 36 | #include "llimits.h" 37 | 38 | 39 | #define ALPHABIT 0 40 | #define DIGITBIT 1 41 | #define PRINTBIT 2 42 | #define SPACEBIT 3 43 | #define XDIGITBIT 4 44 | 45 | 46 | #define MASK(B) (1 << (B)) 47 | 48 | 49 | /* 50 | ** add 1 to char to allow index -1 (EOZ) 51 | */ 52 | #define testprop(c,p) (luai_ctype_[(c)+1] & (p)) 53 | 54 | /* 55 | ** 'lalpha' (Lua alphabetic) and 'lalnum' (Lua alphanumeric) both include '_' 56 | */ 57 | #define lislalpha(c) testprop(c, MASK(ALPHABIT)) 58 | #define lislalnum(c) testprop(c, (MASK(ALPHABIT) | MASK(DIGITBIT))) 59 | #define lisdigit(c) testprop(c, MASK(DIGITBIT)) 60 | #define lisspace(c) testprop(c, MASK(SPACEBIT)) 61 | #define lisprint(c) testprop(c, MASK(PRINTBIT)) 62 | #define lisxdigit(c) testprop(c, MASK(XDIGITBIT)) 63 | 64 | 65 | /* 66 | ** In ASCII, this 'ltolower' is correct for alphabetic characters and 67 | ** for '.'. That is enough for Lua needs. ('check_exp' ensures that 68 | ** the character either is an upper-case letter or is unchanged by 69 | ** the transformation, which holds for lower-case letters and '.'.) 70 | */ 71 | #define ltolower(c) \ 72 | check_exp(('A' <= (c) && (c) <= 'Z') || (c) == ((c) | ('A' ^ 'a')), \ 73 | (c) | ('A' ^ 'a')) 74 | 75 | 76 | /* one entry for each character and for -1 (EOZ) */ 77 | LUAI_DDEC(const lu_byte luai_ctype_[UCHAR_MAX + 2];) 78 | 79 | 80 | #else /* }{ */ 81 | 82 | /* 83 | ** use standard C ctypes 84 | */ 85 | 86 | #include 87 | 88 | 89 | #define lislalpha(c) (isalpha(c) || (c) == '_') 90 | #define lislalnum(c) (isalnum(c) || (c) == '_') 91 | #define lisdigit(c) (isdigit(c)) 92 | #define lisspace(c) (isspace(c)) 93 | #define lisprint(c) (isprint(c)) 94 | #define lisxdigit(c) (isxdigit(c)) 95 | 96 | #define ltolower(c) (tolower(c)) 97 | 98 | #endif /* } */ 99 | 100 | #endif 101 | 102 | -------------------------------------------------------------------------------- /src/lua/lctype.c: -------------------------------------------------------------------------------- 1 | /* 2 | ** $Id: lctype.c $ 3 | ** 'ctype' functions for Lua 4 | ** See Copyright Notice in lua.h 5 | */ 6 | 7 | #define lctype_c 8 | #define LUA_CORE 9 | 10 | #include "lprefix.h" 11 | 12 | 13 | #include "lctype.h" 14 | 15 | #if !LUA_USE_CTYPE /* { */ 16 | 17 | #include 18 | 19 | 20 | #if defined (LUA_UCID) /* accept UniCode IDentifiers? */ 21 | /* consider all non-ascii codepoints to be alphabetic */ 22 | #define NONA 0x01 23 | #else 24 | #define NONA 0x00 /* default */ 25 | #endif 26 | 27 | 28 | LUAI_DDEF const lu_byte luai_ctype_[UCHAR_MAX + 2] = { 29 | 0x00, /* EOZ */ 30 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0. */ 31 | 0x00, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, 32 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 1. */ 33 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 34 | 0x0c, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, /* 2. */ 35 | 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 36 | 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, /* 3. */ 37 | 0x16, 0x16, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 38 | 0x04, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x05, /* 4. */ 39 | 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 40 | 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, /* 5. */ 41 | 0x05, 0x05, 0x05, 0x04, 0x04, 0x04, 0x04, 0x05, 42 | 0x04, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x05, /* 6. */ 43 | 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 44 | 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, /* 7. */ 45 | 0x05, 0x05, 0x05, 0x04, 0x04, 0x04, 0x04, 0x00, 46 | NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, /* 8. */ 47 | NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, 48 | NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, /* 9. */ 49 | NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, 50 | NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, /* a. */ 51 | NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, 52 | NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, /* b. */ 53 | NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, 54 | 0x00, 0x00, NONA, NONA, NONA, NONA, NONA, NONA, /* c. */ 55 | NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, 56 | NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, /* d. */ 57 | NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, 58 | NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, /* e. */ 59 | NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, 60 | NONA, NONA, NONA, NONA, NONA, 0x00, 0x00, 0x00, /* f. */ 61 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 62 | }; 63 | 64 | #endif /* } */ 65 | -------------------------------------------------------------------------------- /tests/os_test.nelua: -------------------------------------------------------------------------------- 1 | require 'os' 2 | 3 | do -- os.clock 4 | assert(os.clock() >= 0) 5 | end 6 | 7 | do -- os.difftime 8 | assert(os.difftime(0,0) == 0 and os.difftime(1,0) == 1) 9 | end 10 | 11 | do -- os.getenv/os.setenv 12 | ## if not ccinfo.is_wasm then 13 | os.setenv('TESTVAR') 14 | assert(os.setenv('TESTVAR', 'TEST')) 15 | local value: string = os.getenv('TESTVAR') 16 | assert(value == 'TEST') 17 | assert(os.setenv('TESTVAR')) 18 | assert(os.getenv('TESTVAR') == '') 19 | ## end 20 | end 21 | 22 | do -- os.date 23 | local date = os.date() 24 | date:destroy() 25 | 26 | date = os.date('%Y/%m/%d %H:%M:%S') 27 | assert(date:find('^[0-9]+/[0-9]+/[0-9]+ [0-9]+:[0-9]+:[0-9]+$') > 0) 28 | date:destroy() 29 | 30 | date = os.date('!%c', 0) 31 | assert(date:find('00:00:00') > 0) 32 | date:destroy() 33 | 34 | date = os.date('') 35 | assert(date == '') 36 | end 37 | 38 | do -- os.execute 39 | ## if not ccinfo.is_wasm then 40 | assert(os.execute() == true) 41 | ## else 42 | assert(os.execute() == false) 43 | ## end 44 | end 45 | 46 | do -- os.tmpname 47 | ## if not ccinfo.is_wasm then 48 | local tmpname = os.tmpname() 49 | os.remove(tmpname) 50 | tmpname:destroy() 51 | ## end 52 | end 53 | 54 | do -- os.rename 55 | assert(os.rename('my_invalid_file', 'my_invalid_file') == false) 56 | end 57 | 58 | do -- os.remove 59 | assert(os.remove('my_invalid_file') == false) 60 | end 61 | 62 | do -- os.locale 63 | local deflocale: string = os.setlocale() 64 | local locale: string = os.setlocale(deflocale) 65 | assert(locale == deflocale) 66 | locale:destroy() 67 | 68 | locale = os.setlocale(deflocale, 'all') 69 | assert(locale == deflocale) 70 | locale:destroy() 71 | 72 | deflocale:destroy() 73 | end 74 | 75 | do -- os.time 76 | assert(os.time() >= 0) 77 | assert(os.time(os.timedesc{year=2020,month=7,day=18,hour=12,isdst=false}) > 0) 78 | end 79 | 80 | do -- os.realtime 81 | if os.realtime() >= 0 then -- os.realtime is supported 82 | local t1 = os.time() 83 | local t2, t2ns = os.realtime() 84 | assert(t2 - t1 <= 1) 85 | assert(t2ns >= 0) 86 | end 87 | end 88 | 89 | do -- os.now/os.sleep 90 | local start = os.now() 91 | if start >= 0 then -- os.now is supported 92 | assert(start == 0.0) 93 | assert(os.sleep(-1) == false) 94 | if os.sleep(0.02) then -- os.sleep succeeded 95 | assert(os.now() >= 0.01) 96 | end 97 | end 98 | end 99 | 100 | print 'os OK!' 101 | 102 | do -- os.exit 103 | local function f(): integer 104 | os.exit(true) 105 | return 0 106 | end 107 | f() 108 | assert(false) 109 | end 110 | -------------------------------------------------------------------------------- /src/lua/llex.h: -------------------------------------------------------------------------------- 1 | /* 2 | ** $Id: llex.h $ 3 | ** Lexical Analyzer 4 | ** See Copyright Notice in lua.h 5 | */ 6 | 7 | #ifndef llex_h 8 | #define llex_h 9 | 10 | #include 11 | 12 | #include "lobject.h" 13 | #include "lzio.h" 14 | 15 | 16 | /* 17 | ** Single-char tokens (terminal symbols) are represented by their own 18 | ** numeric code. Other tokens start at the following value. 19 | */ 20 | #define FIRST_RESERVED (UCHAR_MAX + 1) 21 | 22 | 23 | #if !defined(LUA_ENV) 24 | #define LUA_ENV "_ENV" 25 | #endif 26 | 27 | 28 | /* 29 | * WARNING: if you change the order of this enumeration, 30 | * grep "ORDER RESERVED" 31 | */ 32 | enum RESERVED { 33 | /* terminal symbols denoted by reserved words */ 34 | TK_AND = FIRST_RESERVED, TK_BREAK, 35 | TK_DO, TK_ELSE, TK_ELSEIF, TK_END, TK_FALSE, TK_FOR, TK_FUNCTION, 36 | TK_GOTO, TK_IF, TK_IN, TK_LOCAL, TK_NIL, TK_NOT, TK_OR, TK_REPEAT, 37 | TK_RETURN, TK_THEN, TK_TRUE, TK_UNTIL, TK_WHILE, 38 | /* other terminal symbols */ 39 | TK_IDIV, TK_CONCAT, TK_DOTS, TK_EQ, TK_GE, TK_LE, TK_NE, 40 | TK_SHL, TK_SHR, 41 | TK_DBCOLON, TK_EOS, 42 | TK_FLT, TK_INT, TK_NAME, TK_STRING 43 | }; 44 | 45 | /* number of reserved words */ 46 | #define NUM_RESERVED (cast_int(TK_WHILE-FIRST_RESERVED + 1)) 47 | 48 | 49 | typedef union { 50 | lua_Number r; 51 | lua_Integer i; 52 | TString *ts; 53 | } SemInfo; /* semantics information */ 54 | 55 | 56 | typedef struct Token { 57 | int token; 58 | SemInfo seminfo; 59 | } Token; 60 | 61 | 62 | /* state of the lexer plus state of the parser when shared by all 63 | functions */ 64 | typedef struct LexState { 65 | int current; /* current character (charint) */ 66 | int linenumber; /* input line counter */ 67 | int lastline; /* line of last token 'consumed' */ 68 | Token t; /* current token */ 69 | Token lookahead; /* look ahead token */ 70 | struct FuncState *fs; /* current function (parser) */ 71 | struct lua_State *L; 72 | ZIO *z; /* input stream */ 73 | Mbuffer *buff; /* buffer for tokens */ 74 | Table *h; /* to avoid collection/reuse strings */ 75 | struct Dyndata *dyd; /* dynamic structures used by the parser */ 76 | TString *source; /* current source name */ 77 | TString *envn; /* environment variable name */ 78 | } LexState; 79 | 80 | 81 | LUAI_FUNC void luaX_init (lua_State *L); 82 | LUAI_FUNC void luaX_setinput (lua_State *L, LexState *ls, ZIO *z, 83 | TString *source, int firstchar); 84 | LUAI_FUNC TString *luaX_newstring (LexState *ls, const char *str, size_t l); 85 | LUAI_FUNC void luaX_next (LexState *ls); 86 | LUAI_FUNC int luaX_lookahead (LexState *ls); 87 | LUAI_FUNC l_noret luaX_syntaxerror (LexState *ls, const char *s); 88 | LUAI_FUNC const char *luaX_token2str (LexState *ls, int token); 89 | 90 | 91 | #endif 92 | -------------------------------------------------------------------------------- /spec/stdlib_spec.lua: -------------------------------------------------------------------------------- 1 | local lester = require 'nelua.thirdparty.lester' 2 | local describe, it = lester.describe, lester.it 3 | 4 | local expect = require 'spec.tools.expect' 5 | local ccompiler = require 'nelua.ccompiler' 6 | 7 | describe("standard library", function() 8 | 9 | it("require", function() 10 | expect.run_c_from_file('tests/require_test.nelua') 11 | expect.run_error('tests/require_itself.nelua', "cannot require itself") 12 | end) 13 | it("builtins", function() 14 | expect.run_c_from_file('tests/builtins_test.nelua', 'hello', 'warning: hi') 15 | end) 16 | it("traits", function() 17 | expect.run_c_from_file('tests/traits_test.nelua') 18 | end) 19 | it("libc", function() 20 | expect.run_c_from_file('tests/libc_test.nelua') 21 | end) 22 | it("math", function() 23 | expect.run_c_from_file('tests/math_test.nelua') 24 | end) 25 | it("memory", function() 26 | expect.run_c_from_file('tests/memory_test.nelua') 27 | end) 28 | it("gc", function() 29 | expect.run_c_from_file('tests/gc_test.nelua') 30 | end) 31 | it("allocators", function() 32 | expect.run_c_from_file('tests/allocators_test.nelua') 33 | end) 34 | it("span", function() 35 | expect.run_c_from_file('tests/span_test.nelua') 36 | end) 37 | it("sequence", function() 38 | expect.run_c_from_file('tests/sequence_test.nelua') 39 | end) 40 | it("vector", function() 41 | expect.run_c_from_file('tests/vector_test.nelua') 42 | end) 43 | it("list", function() 44 | expect.run_c_from_file('tests/list_test.nelua') 45 | end) 46 | it("hashmap", function() 47 | expect.run_c_from_file('tests/hashmap_test.nelua') 48 | end) 49 | it("hash", function() 50 | expect.run_c_from_file('tests/hash_test.nelua') 51 | end) 52 | it("io", function() 53 | expect.run_c_from_file('tests/io_test.nelua') 54 | end) 55 | it("os", function() 56 | expect.run_c_from_file('tests/os_test.nelua') 57 | end) 58 | it("stringbuilder", function() 59 | expect.run_c_from_file('tests/stringbuilder_test.nelua') 60 | end) 61 | it("string", function() 62 | expect.run_c_from_file('tests/string_test.nelua') 63 | end) 64 | it("utf8", function() 65 | expect.run_c_from_file('tests/utf8_test.nelua') 66 | end) 67 | it("pattern matching", function() 68 | expect.run_c_from_file('tests/pattern_matching_test.nelua') 69 | end) 70 | it("pack", function() 71 | expect.run_c_from_file('tests/pack_test.nelua') 72 | end) 73 | it("defer", function() 74 | expect.run_c_from_file('tests/defer_test.nelua') 75 | end) 76 | it("coroutine", function() 77 | expect.run_c_from_file('tests/coroutine_test.nelua') 78 | end) 79 | 80 | local ccinfo = ccompiler.get_cc_info() 81 | if (ccinfo.is_gcc or ccinfo.is_clang) and not ccinfo.is_wasm and not ccinfo.is_windows then 82 | it("threads", function() 83 | expect.run_c_from_file('tests/threads_test.nelua') 84 | end) 85 | end 86 | 87 | end) 88 | -------------------------------------------------------------------------------- /examples/gameoflife.nelua: -------------------------------------------------------------------------------- 1 | require 'math' 2 | require 'os' 3 | require 'io' 4 | 5 | local GRID_WIDTH = 40 6 | local GRID_HEIGHT = 15 7 | 8 | -- Two-dimensional field of cells. 9 | local CellField = @record{ 10 | cells: [GRID_HEIGHT][GRID_WIDTH]boolean, 11 | } 12 | 13 | -- Returns an empty field. 14 | function CellField.new(): *CellField 15 | return new(@CellField) 16 | end 17 | 18 | -- Sets the state of the specified cell to the given value.. 19 | function CellField:set_alive(x: integer, y: integer, b: boolean) 20 | self.cells[y][x] = b 21 | end 22 | 23 | --[[ 24 | Reports whether the specified cell is alive. 25 | If the x or y coordinates are outside the field boundaries they are wrapped. 26 | ]] 27 | function CellField:is_alive(x: integer, y: integer) 28 | return self.cells[(y + GRID_HEIGHT) % GRID_HEIGHT][(x + GRID_WIDTH) % GRID_WIDTH] 29 | end 30 | 31 | -- Returns the state of the specified cell at the next time step. 32 | function CellField:next(x: integer, y: integer): boolean 33 | -- Count adjacent cells that are alive. 34 | local alive = 0 35 | for i=-1,1 do 36 | for j=-1,1 do 37 | if (j ~= 0 or i ~= 0) and self:is_alive(x+i, y+j) then 38 | alive = alive + 1 39 | end 40 | end 41 | end 42 | --[[ 43 | Returns next state according to the game rules: 44 | - exactly 3 neighbors: on 45 | - exactly 2 neighbors: maintain current state 46 | - otherwise: off 47 | ]] 48 | return alive == 3 or (alive == 2 and self:is_alive(x, y)) 49 | end 50 | 51 | -- Stores round state of Conway's Game of Life. 52 | local GameState = @record{ 53 | a: *CellField, b: *CellField 54 | } 55 | 56 | -- Returns a new game state with a random initial cells. 57 | function GameState.new(seed: integer): GameState 58 | math.randomseed(seed) 59 | local a = CellField.new() 60 | for i=1,GRID_WIDTH*GRID_HEIGHT/4 do 61 | a:set_alive(math.random(0, GRID_WIDTH-1), math.random(0, GRID_HEIGHT-1), true) 62 | end 63 | return (@GameState){a=a, b=CellField.new()} 64 | end 65 | 66 | -- Advances the game by one instant, recomputing and updating all cells. 67 | function GameState:step() 68 | for y=0,GRID_HEIGHT-1 do 69 | for x=0,GRID_WIDTH-1 do 70 | self.b:set_alive(x, y, self.a:next(x, y)) 71 | end 72 | end 73 | self.a, self.b = self.b, self.a -- swap fields 74 | end 75 | 76 | -- Returns the game board as a string. 77 | function GameState:render() 78 | io.write("\27c") -- clear screen 79 | for y=0,GRID_HEIGHT-1 do 80 | for x=0,GRID_WIDTH-1 do 81 | io.write(self.a:is_alive(x, y) and 'O' or ' ') -- draw cell 82 | end 83 | io.write('\n') 84 | end 85 | io.flush() -- flush screen 86 | end 87 | 88 | -- Create new game. 89 | local l = GameState.new(1) 90 | 91 | -- Run game of life for 300 frames. 92 | for i=1,300 do 93 | l:step() 94 | l:render() 95 | os.sleep(1/24) 96 | end 97 | -------------------------------------------------------------------------------- /lualib/nelua/utils/errorer.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Erroer module 3 | 4 | The erroer module is used to format and print pretty error messages. 5 | ]] 6 | 7 | local colors = require 'nelua.utils.console'.colors 8 | local stringer = require 'nelua.utils.stringer' 9 | 10 | local errorer = {} 11 | 12 | -- Throw a formatted error message if condition is not met. 13 | function errorer.assertf(cond, message, ...) 14 | if not cond then 15 | error(stringer.pformat(message, ...), 2) 16 | end 17 | return cond 18 | end 19 | 20 | -- Throw a formatted error message. 21 | function errorer.errorf(message, ...) 22 | error(stringer.pformat(message, ...), 2) 23 | end 24 | 25 | -- Generate a pretty error message associated with location from a source. 26 | function errorer.get_pretty_source_pos_errmsg(loc, errmsg, errname) 27 | local srcname, lineno, colno, line, len = loc.srcname, loc.lineno, loc.colno, loc.line, loc.len 28 | local colbright, colreset = colors.bright, colors.reset 29 | local lineloc = '' 30 | if line and colno then 31 | -- count number of tabs and spaces up to the text 32 | local ntabs = select(2, line:sub(1,colno-1):gsub('\t','')) 33 | local nspaces = colno-1-ntabs 34 | -- generate a line helper to assist showing the exact line column for the error 35 | local linehelper = string.rep('\t', ntabs)..string.rep(' ', nspaces)..colbright..colors.green..'^'..colreset 36 | if len and len > 1 then 37 | -- remove comments and trailing spaces 38 | local trimmedline = line:sub(1,colno+len-1):gsub('%-%-.*',''):gsub('%s+$','') 39 | len = math.min(#trimmedline-colno, len) 40 | linehelper = linehelper..colors.magenta..string.rep('~',len)..colreset 41 | end 42 | lineloc = '\n'..line..'\n'..linehelper 43 | end 44 | local errtraceback = '' 45 | -- extract traceback from message, to move it to the end of the message 46 | local tracebackpos = errmsg:find('%s*stack traceback%:') 47 | if tracebackpos then 48 | errtraceback = '\n'..errmsg:sub(tracebackpos) 49 | errmsg = errmsg:sub(1, tracebackpos-1) 50 | end 51 | -- choose the color for the message 52 | local errcolor = colreset 53 | if string.find(errname, 'error', 1, true) then 54 | errcolor = colors.error 55 | elseif string.find(errname, 'warning', 1, true) then 56 | errcolor = colors.warn 57 | end 58 | -- generate the error message 59 | local msg = '' 60 | if srcname then 61 | if errcolor == '' then -- no colored output 62 | srcname = srcname:match('[^/\\]+$') -- remove path prefix 63 | end 64 | msg = msg..srcname..colbright..':' 65 | if lineno then 66 | msg = msg..lineno..':' 67 | if colno then 68 | msg = msg..colno..':' 69 | end 70 | end 71 | msg=msg..' ' 72 | end 73 | msg = msg..errcolor..errname..': '..colreset..colbright..errmsg..colreset.. 74 | lineloc..errtraceback..'\n' 75 | return msg 76 | end 77 | 78 | return errorer 79 | -------------------------------------------------------------------------------- /spec/tools/covreporter.lua: -------------------------------------------------------------------------------- 1 | local re = require 'nelua.thirdparty.lpegrex' 2 | local colors = require 'nelua.utils.console'.colors 3 | 4 | local function readfile(filename) 5 | local f = assert(io.open(filename,'r')) 6 | local res = assert(f:read('*a')) 7 | f:close() 8 | return res 9 | end 10 | 11 | local function colored_percent(value) 12 | local colored_value 13 | if value == 100 then 14 | colored_value = colors.green..string.format('%.2f%%', value)..colors.reset 15 | else 16 | colored_value = colors.red..string.format('%.2f%%', value)..colors.reset 17 | end 18 | return colored_value 19 | end 20 | 21 | local function report_coverage(reportfile) 22 | local reportdata = readfile(reportfile) 23 | if not reportdata then 24 | error('no coverage report found') 25 | end 26 | local pat = re.compile([[ 27 | body <- {| sfile* |} summary 28 | sfile <- {| {:name: '' -> 'file' :} '='+%s+!'Summary'{:file: [-/_.%w]+ :}%s+'='+%nl (miss / sline / eline)* |} 29 | miss <- !div '*'+'0 '{[^%nl]+} %nl 30 | sline <- !div ' '* %d* ' '* {''} [^%nl]* %nl 31 | eline <- [ ]* {''} %nl 32 | div <- '==='+%s+[-/_.%w]+%s+'==='+%nl 33 | summary <- {| heading {| line+ |} footer |} 34 | footer <- tbldiv {| 'Total' sp num num percent |} !. 35 | heading <- '='+%s+'Summary'%s+'='+%s+'File'%s+'Hits'%s+'Missed'%s+'Coverage'%s+'-'+%s+ 36 | line <- !tbldiv {| file num num percent |} 37 | tbldiv <- '-'+ sp 38 | num <- {[%d]+} -> tonumber sp 39 | percent <- {[.%d]+} -> tonumber '%' sp 40 | file <- {[-/_.%w]+} sp 41 | sp <- %s+ 42 | ]], { 43 | tonumber = tonumber 44 | }) 45 | 46 | local sources, summary = pat:match(reportdata) 47 | assert(sources and summary, 'failed to parse luacov report output') 48 | 49 | local filelines, totalline = summary[1], summary[2] 50 | local total_coverage = totalline[3] 51 | print(colored_percent(total_coverage) .. ' coverage') 52 | 53 | if total_coverage < 100 then 54 | print('\nNot fully covered files:') 55 | end 56 | for _,fileline in ipairs(filelines) do 57 | local filename, coverage = fileline[1], fileline[4] 58 | if coverage < 100 then 59 | print(colored_percent(coverage) .. ' ' .. filename) 60 | 61 | for _,source in ipairs(sources) do 62 | if source.file == filename then 63 | local last = nil 64 | for i,line in ipairs(source) do 65 | if line ~= '' then 66 | if last and last ~= i - 1 then 67 | print() 68 | end 69 | print(colors.cyan..string.format('%6d\t',i)..colors.reset..line) 70 | last = i 71 | end 72 | end 73 | end 74 | end 75 | end 76 | end 77 | 78 | local threshold = 98.0 79 | if total_coverage < threshold then 80 | print(colors.red..string.format('Coverage threshold is below %.2f!', threshold)) 81 | os.exit(-1) 82 | end 83 | end 84 | 85 | report_coverage('luacov.report.out') 86 | -------------------------------------------------------------------------------- /docs/_includes/footer.html: -------------------------------------------------------------------------------- 1 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 59 | -------------------------------------------------------------------------------- /spec/except_spec.lua: -------------------------------------------------------------------------------- 1 | local lester = require 'nelua.thirdparty.lester' 2 | local describe, it = lester.describe, lester.it 3 | 4 | local expect = require 'spec.tools.expect' 5 | local except = require 'nelua.utils.except' 6 | 7 | describe("except", function() 8 | 9 | it("raising strings", function() 10 | local e 11 | except.try(function() 12 | except.assertraise(false, 'an error') 13 | end, function(ee) 14 | e = ee 15 | return true 16 | end) 17 | assert(e and e:get_message() == 'an error') 18 | end) 19 | 20 | it("raising tables", function() 21 | local e 22 | except.try(function() 23 | except.assertraise(true, 'not happening') 24 | except.raise({message='an error'}) 25 | end, function(ee) 26 | e = ee 27 | return true 28 | end) 29 | assert(e and e.message == 'an error') 30 | end) 31 | 32 | it("raising something", function() 33 | local e 34 | except.try(function() 35 | except.raise({message='an error'}) 36 | end, function(ee) 37 | e = ee 38 | return true 39 | end) 40 | assert(e and e.message == 'an error') 41 | end) 42 | 43 | it("raising Exception", function() 44 | local e 45 | except.try(function() 46 | except.raise(except.Exception({message = 'an error'})) 47 | end, function(ee) 48 | e = ee 49 | return true 50 | end) 51 | assert(e and e.message == 'an error') 52 | end) 53 | 54 | it("raising labeled Exception", function() 55 | local e 56 | except.try(function() 57 | except.raise(except.Exception({label = 'MyError'})) 58 | end, function(ee) 59 | e = ee 60 | return true 61 | end) 62 | assert(e and e.label == 'MyError' and e:get_message() == 'MyError') 63 | assert(except.isexception(e, 'MyError')) 64 | end) 65 | 66 | it("reraising", function() 67 | local e 68 | except.try(function() 69 | except.try(function() 70 | except.raise('an error') 71 | end, function() return false end) 72 | end, function(ee) 73 | e = ee 74 | return true 75 | end) 76 | assert(e and e.message == 'an error') 77 | end) 78 | 79 | it("ignore errors", function() 80 | expect.fail(function() 81 | except.try(function() 82 | error('an error') 83 | end, function() return true end) 84 | end) 85 | end) 86 | 87 | it("ignore number errors", function() 88 | expect.fail(function() 89 | except.try(function() 90 | error(1) 91 | end, function() return true end) 92 | end) 93 | end) 94 | 95 | it("errors with __tostring metamethod", function() 96 | local ok, errmsg = pcall(function() 97 | except.try(function() 98 | error(setmetatable({}, {__tostring = function() return 'myerr' end})) 99 | end, function() return true end) 100 | end) 101 | assert(not ok and errmsg:match("^myerr")) 102 | end) 103 | 104 | it("raising in pcall", function() 105 | local ok, e = pcall(function() 106 | except.assertraise(false, 'an error') 107 | end) 108 | assert(not ok and tostring(e):match('an error')) 109 | end) 110 | 111 | end) 112 | -------------------------------------------------------------------------------- /lib/allocators/aligned.nelua: -------------------------------------------------------------------------------- 1 | --[[ 2 | The aligned allocator wraps another allocator to support aligned allocations. 3 | 4 | In most platforms the default allocator perform allocations aligned to the size 5 | of the largest primitive type (usually 16), however in some situations you may 6 | want to allocate a memory with higher alignment requirements so the CPU can perform 7 | memory operations using SIMD instructions. This allocator can be used in such situations. 8 | ]] 9 | 10 | require 'allocators.allocator' 11 | 12 | -- Aligns an address. 13 | local function align_forward(addr: usize, align: usize): usize 14 | return (addr + (align-1)) & ~(align-1) 15 | end 16 | 17 | ## local function make_AlignedAllocatorT(WrappedAllocator, ALIGN) 18 | ## static_assert(ALIGN and ALIGN & (ALIGN-1) == 0, 'align must be a power of two') 19 | ## static_assert(ALIGN >= primtypes.usize.size, 'align must be greater than '..primtypes.pointer.size) 20 | 21 | local WrappedAllocator: type = #[WrappedAllocator]# 22 | local SIZE = #[SIZE]# 23 | local ALIGN = #[ALIGN]# 24 | 25 | -- Aligned allocator record defined when instantiating the generic `AlignedAllocator`. 26 | local AlignedAllocatorT: type = @record{ 27 | allocator: WrappedAllocator 28 | } 29 | 30 | --[[ 31 | Allocates `size` bytes and returns a pointer to the allocated memory block. 32 | The address of the allocated memory is guaranteed to a multiple of `ALIGN`. 33 | 34 | The allocated memory is not initialized. 35 | If `size` is zero or the operation fails, then returns `nilptr`. 36 | ]] 37 | function AlignedAllocatorT:alloc(size: usize, flags: facultative(usize)): pointer 38 | local origp: pointer = self.allocator:alloc(size + (@usize)(#@pointer + ALIGN - 1), flags) 39 | if not origp then return nilptr end 40 | local addr: usize = align_forward((@usize)(origp) + #@pointer, ALIGN) 41 | local header: *pointer = (@*pointer)(addr - #@pointer) 42 | $header = origp 43 | return (@pointer)(addr) 44 | end 45 | 46 | --[[ 47 | Get pointer to the real allocated block, this may be used with the GC to mark 48 | references to real allocations. 49 | ]] 50 | function AlignedAllocatorT:get_realptr(p: pointer) 51 | if unlikely(p == nilptr) then return nilptr end 52 | return $(@*pointer)((@usize)(p) - #@pointer) 53 | end 54 | 55 | -- Deallocates the allocated memory block pointed by `p`. 56 | function AlignedAllocatorT:dealloc(p: pointer): void 57 | if unlikely(p == nilptr) then return end 58 | self.allocator:dealloc(self:get_realptr(p)) 59 | end 60 | 61 | --TODO: optimized realloc 62 | 63 | ## Allocator_implement_interface(AlignedAllocatorT) 64 | 65 | ## return AlignedAllocatorT 66 | ## end 67 | 68 | --[[ 69 | Generic used to instantiate a aligned allocator type in the form of `AlignedAllocator(Allocator, ALIGN)`. 70 | 71 | Argument `Allocator` is the allocator to be wrapped, typically `DefaultAllocator`. 72 | Argument `ALIGN` is the default alignment for new allocations, must be in power of two. 73 | ]] 74 | global AlignedAllocator: type = #[generalize(make_AlignedAllocatorT)]# 75 | 76 | return AlignedAllocator 77 | -------------------------------------------------------------------------------- /lualib/nelua/utils/stringer.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Stringer module 3 | 4 | Stringer module contains some utilities for working with lua strings. 5 | ]] 6 | 7 | local hasher = require 'hasher' 8 | 9 | local stringer = {} 10 | 11 | --[[ 12 | Compute a hash with the desired length for a string. 13 | The hash uses the Blake2B algorithm and is encoded in Base58. 14 | This is used to generate unique names for large strings. 15 | ]] 16 | function stringer.hash(s, len, key) 17 | len = len or 20 18 | local hash = hasher.blake2b(s, len, key) 19 | return hasher.base58encode(hash) 20 | end 21 | 22 | -- Concatenate many arguments into a string separated by tabular (similar to lua print). 23 | function stringer.pconcat(...) 24 | local t = table.pack(...) 25 | for i=1,t.n do 26 | t[i] = tostring(t[i]) 27 | end 28 | return table.concat(t, '\t') 29 | end 30 | 31 | -- Like `string.format` but skip format when not passing any argument. 32 | function stringer.pformat(format, ...) 33 | if select('#', ...) == 0 then 34 | return format 35 | end 36 | return format:format(...) 37 | end 38 | 39 | -- Checks if a string starts with a prefix. 40 | function stringer.startswith(s, prefix) 41 | return s:find(prefix,1,true) == 1 42 | end 43 | 44 | -- Checks if a string ends with a prefix. 45 | function stringer.endswith(s, suffix) 46 | local init = #s - #suffix + 1 47 | return init >= 1 and s:find(suffix, init, true) ~= nil 48 | end 49 | 50 | -- Returns a string with right white spaces trimmed. 51 | function stringer.rtrim(s) 52 | return (s:gsub("%s*$", "")) 53 | end 54 | 55 | -- Split a string into a table using a separator (default to space). 56 | function stringer.split(s, sep) 57 | sep = sep or ' ' 58 | local res = {} 59 | local pattern = ("([^%s]+)"):format(sep or ' ') 60 | for each in s:gmatch(pattern) do 61 | res[#res+1] = each 62 | end 63 | return res 64 | end 65 | 66 | -- Extract a specific line from a text. 67 | function stringer.getline(text, lineno) 68 | if lineno <= 0 then return nil end 69 | local linestart = 1 70 | if lineno > 1 then 71 | local l = 1 72 | for pos in text:gmatch('\n()') do 73 | l = l + 1 74 | if l >= lineno then 75 | linestart = pos 76 | break 77 | end 78 | end 79 | if not linestart then return nil end -- not found 80 | end 81 | local lineend = text:find('\n', linestart, true) 82 | lineend = lineend and lineend-1 or nil 83 | return text:sub(linestart, lineend), linestart, lineend 84 | end 85 | 86 | -- Insert a text in the middle of string and return the new string. 87 | function stringer.insert(s, pos, text) 88 | return s:sub(1, pos-1)..text..s:sub(pos) 89 | end 90 | 91 | -- Insert a text after another matched text and return the new string. 92 | function stringer.insertafter(s, matchtext, text) 93 | local matchpos = s:find(matchtext, 1, true) 94 | if matchpos then 95 | return stringer.insert(s, matchpos+#matchtext, text) 96 | end 97 | end 98 | 99 | -- Ensures string `s` ends with a new line. 100 | function stringer.ensurenewline(s) 101 | if not stringer.endswith(s, '\n') then 102 | return s..'\n' 103 | end 104 | return s 105 | end 106 | 107 | return stringer 108 | -------------------------------------------------------------------------------- /tests/defer_test.nelua: -------------------------------------------------------------------------------- 1 | require 'string' 2 | 3 | local sb: stringbuilder 4 | local s: string 5 | 6 | -- return 7 | local function f(sb: *stringbuilder) 8 | sb:clear() 9 | sb:write'a' 10 | defer sb:write'd' end 11 | defer sb:write'c' end 12 | sb:write'b' 13 | return 14 | end 15 | f(&sb) 16 | assert(sb:view() == 'abcd') 17 | 18 | -- do block 19 | local function f(sb: *stringbuilder) 20 | sb:clear() 21 | sb:write'a' 22 | defer sb:write'd' end 23 | do 24 | defer sb:write'b' end 25 | end 26 | sb:write'c' 27 | end 28 | f(&sb) 29 | assert(sb:view() == 'abcd') 30 | 31 | -- nested do blocks 32 | local function f(sb: *stringbuilder) 33 | sb:clear() 34 | sb:write'a' 35 | defer sb:write'e' end 36 | do 37 | defer sb:write'c' end 38 | do 39 | defer sb:write'b' end 40 | end 41 | end 42 | sb:write'd' 43 | end 44 | f(&sb) 45 | assert(sb:view() == 'abcde') 46 | 47 | -- return in middle nested 48 | local function f(sb: *stringbuilder) 49 | sb:clear() 50 | sb:write'a' 51 | defer sb:write'd' end 52 | if true then 53 | defer sb:write'c' end 54 | if true then 55 | defer sb:write'b' end 56 | end 57 | return 58 | end 59 | end 60 | f(&sb) 61 | assert(sb:view() == 'abcd') 62 | 63 | -- middle return 64 | local function f(sb: *stringbuilder) 65 | sb:clear() 66 | sb:write'a' 67 | defer sb:write'd' end 68 | if true then 69 | defer sb:write'c' end 70 | return 71 | if true then 72 | defer sb:write'b' end 73 | end 74 | end 75 | end 76 | f(&sb) 77 | assert(sb:view() == 'acd') 78 | 79 | -- while and break 80 | local function f(sb: *stringbuilder) 81 | sb:clear() 82 | sb:write'a' 83 | defer sb:write'c' end 84 | while true do 85 | defer sb:write'b' end 86 | break 87 | end 88 | end 89 | f(&sb) 90 | assert(sb:view() == 'abc') 91 | 92 | -- for and continue 93 | local function f(sb: *stringbuilder) 94 | sb:clear() 95 | sb:write'a' 96 | defer sb:write'c' end 97 | for i=1,3 do 98 | defer sb:write'b' end 99 | continue 100 | defer sb:write' ' end 101 | end 102 | end 103 | f(&sb) 104 | assert(sb:view() == 'abbbc') 105 | 106 | -- repeat 107 | local function f(sb: *stringbuilder) 108 | sb:clear() 109 | sb:write'a' 110 | defer sb:write'c' end 111 | repeat 112 | defer sb:write'b' end 113 | local x = true 114 | until x 115 | end 116 | f(&sb) 117 | assert(sb:view() == 'abc') 118 | 119 | sb:destroy() 120 | 121 | do -- issue #46 122 | local n = 0 123 | do 124 | defer n = n+1 end 125 | local r = (do 126 | in 0 127 | end) 128 | n = n + r 129 | end 130 | assert(n == 1) 131 | 132 | n = 0 133 | do 134 | defer n = n+1 end 135 | local function x() 136 | return 0 137 | end 138 | local r = x() 139 | end 140 | assert(n == 1) 141 | end 142 | 143 | do -- issue #233 144 | local function f(n: integer): integer 145 | defer n = n + 1 end 146 | return n 147 | end 148 | 149 | local function g(n: integer, m: integer): (integer, integer) 150 | defer n = n + 1 m = m + 1 end 151 | return n, m 152 | end 153 | 154 | assert(f(1) == 1) 155 | local n, m = g(1,2) 156 | assert(n == 1 and m == 2) 157 | end 158 | 159 | print 'defer OK!' 160 | -------------------------------------------------------------------------------- /lualib/nelua/utils/tabler.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Tabler module 3 | 4 | The tabler module contains some utilities for working with lua tables. 5 | ]] 6 | 7 | local tabler = {} 8 | 9 | -- Copy a table into another, in-place. 10 | function tabler.update(t, src) 11 | for k,v in next,src do 12 | t[k] = v 13 | end 14 | return t 15 | end 16 | 17 | -- Find a value inside an array table. 18 | function tabler.ifind(t, val, idx) 19 | for i=idx or 1,#t do 20 | if t[i] == val then 21 | return i 22 | end 23 | end 24 | return nil 25 | end 26 | 27 | -- Insert values into an array table. 28 | function tabler.insertvalues(t, st) 29 | local pos = #t 30 | for i=1,#st do 31 | t[pos + i] = st[i] 32 | end 33 | return t 34 | end 35 | 36 | -- Create a new table of mapped array values. 37 | function tabler.imap(t, fn) 38 | local ot = {} 39 | for i=1,#t do 40 | ot[i] = fn(t[i]) 41 | end 42 | return ot 43 | end 44 | 45 | -- Shallow copy for an array. 46 | function tabler.icopy(t) 47 | local ot = {} 48 | for i=1,#t do 49 | ot[i] = t[i] 50 | end 51 | return ot 52 | end 53 | 54 | -- Shallow copy for table. 55 | function tabler.copy(t) 56 | local ot = {} 57 | for i,v in next,t do 58 | ot[i] = v 59 | end 60 | return ot 61 | end 62 | 63 | -- Shallow copy for table (including metatables). 64 | function tabler.copymt(t) 65 | local ot = {} 66 | for i,v in next,t do 67 | ot[i] = v 68 | end 69 | return setmetatable(ot, getmetatable(t)) 70 | end 71 | 72 | -- Update a table with shallow copy of children (including metatables). 73 | function tabler.updatecopymt(dest, src) 74 | for k,v in pairs(src) do dest[k] = tabler.copymt(v) end 75 | return dest 76 | end 77 | 78 | -- Shallow copy a table and update its elements. 79 | function tabler.copyupdate(s, t) 80 | return tabler.update(tabler.copy(s), t) 81 | end 82 | 83 | -- Check if a field is present in all values of an array table. 84 | function tabler.iallfield(t, field) 85 | for i=1,#t do 86 | if not t[i][field] then 87 | return false 88 | end 89 | end 90 | return true 91 | end 92 | 93 | -- Clear a table. 94 | function tabler.clear(t) 95 | for k in next,t do 96 | t[k] = nil 97 | end 98 | return t 99 | end 100 | 101 | -- Shallow compare two array tables. 102 | function tabler.icompare(t1, t2) 103 | for i=1,math.max(#t1, #t2) do 104 | if t1[i] ~= t2[i] then 105 | return false 106 | end 107 | end 108 | return true 109 | end 110 | 111 | -- Shallow compare two tables without metatable set. 112 | function tabler.shallow_compare_nomt(t1, t2) 113 | if not getmetatable(t1) and not getmetatable(t2) and 114 | type(t1) == 'table' and type(t2) == 'table' then 115 | for k,v in next,t1 do 116 | if v ~= t2[k] then return false end 117 | end 118 | for k,v in next,t2 do 119 | if v ~= t1[k] then return false end 120 | end 121 | return true 122 | end 123 | return false 124 | end 125 | 126 | -- Make table `dst` identical to table `src` (including metatables). 127 | function tabler.mirror(dst, src) 128 | if rawequal(dst, src) then 129 | return 130 | end 131 | return setmetatable(tabler.update(tabler.clear(setmetatable(dst, nil)), src), getmetatable(src)) 132 | end 133 | 134 | return tabler 135 | -------------------------------------------------------------------------------- /lib/allocators/general.nelua: -------------------------------------------------------------------------------- 1 | --[[ 2 | The general allocator uses the system's general purpose allocator to allocate dynamic memory, 3 | usually this an efficient allocator for the general use case. 4 | It uses the allocation functions provided by the system. 5 | 6 | The general allocator can also be overridden by an allocator instance in case 7 | the global `embedded_general_allocator` is declared before this library is required. 8 | ]] 9 | 10 | -- General allocator record. 11 | global GeneralAllocator: type = @record{} 12 | 13 | -- General allocator instance, that must be used to perform allocations. 14 | global general_allocator: GeneralAllocator 15 | 16 | --[[ 17 | Allocates `size` bytes and returns a pointer of the allocated memory block. 18 | 19 | The allocated memory is not initialized. 20 | For more details see `Allocator:alloc`. 21 | 22 | This function calls system's `malloc()`. 23 | ]] 24 | function GeneralAllocator:alloc(size: usize, flags: facultative(usize)): pointer 25 | ## if embedded_general_allocator then 26 | return embedded_general_allocator:alloc(size) 27 | ## else 28 | local function malloc(size: csize): pointer '> end 29 | if unlikely(size == 0) then return nilptr end 30 | return malloc(size) 31 | ## end 32 | end 33 | 34 | --[[ 35 | Like `alloc`, but the allocated memory is initialized with zeros. 36 | 37 | This function calls system's `calloc()`. 38 | ]] 39 | function GeneralAllocator:alloc0(size: usize, flags: facultative(usize)): pointer 40 | ## if embedded_general_allocator then 41 | return embedded_general_allocator:alloc0(size) 42 | ## else 43 | local function calloc(nmemb: csize, size: csize): pointer '> end 44 | if unlikely(size == 0) then return nilptr end 45 | return calloc(size, 1) 46 | ## end 47 | end 48 | 49 | --[[ 50 | Deallocates the allocated memory block pointed by `p`. 51 | 52 | For more details see `Allocator:dealloc`. 53 | This function calls system's `free()`. 54 | ]] 55 | function GeneralAllocator:dealloc(p: pointer): void 56 | ## if embedded_general_allocator then 57 | embedded_general_allocator:dealloc(p) 58 | ## else 59 | local function free(ptr: pointer): void '> end 60 | if unlikely(p == nilptr) then return end 61 | free(p) 62 | ## end 63 | end 64 | 65 | --[[ 66 | Changes the size of the memory block pointer by `p` from size `oldsize` bytes to `newsize` bytes. 67 | 68 | For more details see `Allocator:realloc`. 69 | This function calls system's `realloc()`. 70 | ]] 71 | function GeneralAllocator:realloc(p: pointer, newsize: usize, oldsize: usize): pointer 72 | ## if embedded_general_allocator then 73 | return embedded_general_allocator:realloc(p, newsize, oldsize) 74 | ## else 75 | local function free(ptr: pointer): void '> end 76 | local function realloc(ptr: pointer, size: csize): pointer '> end 77 | if unlikely(newsize == 0) then 78 | if likely(p ~= nilptr) then 79 | free(p) 80 | end 81 | return nilptr 82 | elseif unlikely(newsize == oldsize) then 83 | return p 84 | end 85 | return realloc(p, newsize) 86 | ## end 87 | end 88 | 89 | require 'allocators.allocator' 90 | 91 | ## Allocator_implement_interface(GeneralAllocator) 92 | 93 | return GeneralAllocator 94 | -------------------------------------------------------------------------------- /src/lua/ltm.h: -------------------------------------------------------------------------------- 1 | /* 2 | ** $Id: ltm.h $ 3 | ** Tag methods 4 | ** See Copyright Notice in lua.h 5 | */ 6 | 7 | #ifndef ltm_h 8 | #define ltm_h 9 | 10 | 11 | #include "lobject.h" 12 | #include "lstate.h" 13 | 14 | 15 | /* 16 | * WARNING: if you change the order of this enumeration, 17 | * grep "ORDER TM" and "ORDER OP" 18 | */ 19 | typedef enum { 20 | TM_INDEX, 21 | TM_NEWINDEX, 22 | TM_GC, 23 | TM_MODE, 24 | TM_LEN, 25 | TM_EQ, /* last tag method with fast access */ 26 | TM_ADD, 27 | TM_SUB, 28 | TM_MUL, 29 | TM_MOD, 30 | TM_POW, 31 | TM_DIV, 32 | TM_IDIV, 33 | TM_BAND, 34 | TM_BOR, 35 | TM_BXOR, 36 | TM_SHL, 37 | TM_SHR, 38 | TM_UNM, 39 | TM_BNOT, 40 | TM_LT, 41 | TM_LE, 42 | TM_CONCAT, 43 | TM_CALL, 44 | TM_CLOSE, 45 | TM_N /* number of elements in the enum */ 46 | } TMS; 47 | 48 | 49 | /* 50 | ** Mask with 1 in all fast-access methods. A 1 in any of these bits 51 | ** in the flag of a (meta)table means the metatable does not have the 52 | ** corresponding metamethod field. (Bit 7 of the flag is used for 53 | ** 'isrealasize'.) 54 | */ 55 | #define maskflags (~(~0u << (TM_EQ + 1))) 56 | 57 | 58 | /* 59 | ** Test whether there is no tagmethod. 60 | ** (Because tagmethods use raw accesses, the result may be an "empty" nil.) 61 | */ 62 | #define notm(tm) ttisnil(tm) 63 | 64 | 65 | #define gfasttm(g,et,e) ((et) == NULL ? NULL : \ 66 | ((et)->flags & (1u<<(e))) ? NULL : luaT_gettm(et, e, (g)->tmname[e])) 67 | 68 | #define fasttm(l,et,e) gfasttm(G(l), et, e) 69 | 70 | #define ttypename(x) luaT_typenames_[(x) + 1] 71 | 72 | LUAI_DDEC(const char *const luaT_typenames_[LUA_TOTALTYPES];) 73 | 74 | 75 | LUAI_FUNC const char *luaT_objtypename (lua_State *L, const TValue *o); 76 | 77 | LUAI_FUNC const TValue *luaT_gettm (Table *events, TMS event, TString *ename); 78 | LUAI_FUNC const TValue *luaT_gettmbyobj (lua_State *L, const TValue *o, 79 | TMS event); 80 | LUAI_FUNC void luaT_init (lua_State *L); 81 | 82 | LUAI_FUNC void luaT_callTM (lua_State *L, const TValue *f, const TValue *p1, 83 | const TValue *p2, const TValue *p3); 84 | LUAI_FUNC void luaT_callTMres (lua_State *L, const TValue *f, 85 | const TValue *p1, const TValue *p2, StkId p3); 86 | LUAI_FUNC void luaT_trybinTM (lua_State *L, const TValue *p1, const TValue *p2, 87 | StkId res, TMS event); 88 | LUAI_FUNC void luaT_tryconcatTM (lua_State *L); 89 | LUAI_FUNC void luaT_trybinassocTM (lua_State *L, const TValue *p1, 90 | const TValue *p2, int inv, StkId res, TMS event); 91 | LUAI_FUNC void luaT_trybiniTM (lua_State *L, const TValue *p1, lua_Integer i2, 92 | int inv, StkId res, TMS event); 93 | LUAI_FUNC int luaT_callorderTM (lua_State *L, const TValue *p1, 94 | const TValue *p2, TMS event); 95 | LUAI_FUNC int luaT_callorderiTM (lua_State *L, const TValue *p1, int v2, 96 | int inv, int isfloat, TMS event); 97 | 98 | LUAI_FUNC void luaT_adjustvarargs (lua_State *L, int nfixparams, 99 | CallInfo *ci, const Proto *p); 100 | LUAI_FUNC void luaT_getvarargs (lua_State *L, CallInfo *ci, 101 | StkId where, int wanted); 102 | 103 | 104 | #endif 105 | -------------------------------------------------------------------------------- /src/lua/ldo.h: -------------------------------------------------------------------------------- 1 | /* 2 | ** $Id: ldo.h $ 3 | ** Stack and Call structure of Lua 4 | ** See Copyright Notice in lua.h 5 | */ 6 | 7 | #ifndef ldo_h 8 | #define ldo_h 9 | 10 | 11 | #include "llimits.h" 12 | #include "lobject.h" 13 | #include "lstate.h" 14 | #include "lzio.h" 15 | 16 | 17 | /* 18 | ** Macro to check stack size and grow stack if needed. Parameters 19 | ** 'pre'/'pos' allow the macro to preserve a pointer into the 20 | ** stack across reallocations, doing the work only when needed. 21 | ** It also allows the running of one GC step when the stack is 22 | ** reallocated. 23 | ** 'condmovestack' is used in heavy tests to force a stack reallocation 24 | ** at every check. 25 | */ 26 | #define luaD_checkstackaux(L,n,pre,pos) \ 27 | if (l_unlikely(L->stack_last.p - L->top.p <= (n))) \ 28 | { pre; luaD_growstack(L, n, 1); pos; } \ 29 | else { condmovestack(L,pre,pos); } 30 | 31 | /* In general, 'pre'/'pos' are empty (nothing to save) */ 32 | #define luaD_checkstack(L,n) luaD_checkstackaux(L,n,(void)0,(void)0) 33 | 34 | 35 | 36 | #define savestack(L,pt) (cast_charp(pt) - cast_charp(L->stack.p)) 37 | #define restorestack(L,n) cast(StkId, cast_charp(L->stack.p) + (n)) 38 | 39 | 40 | /* macro to check stack size, preserving 'p' */ 41 | #define checkstackp(L,n,p) \ 42 | luaD_checkstackaux(L, n, \ 43 | ptrdiff_t t__ = savestack(L, p), /* save 'p' */ \ 44 | p = restorestack(L, t__)) /* 'pos' part: restore 'p' */ 45 | 46 | 47 | /* macro to check stack size and GC, preserving 'p' */ 48 | #define checkstackGCp(L,n,p) \ 49 | luaD_checkstackaux(L, n, \ 50 | ptrdiff_t t__ = savestack(L, p); /* save 'p' */ \ 51 | luaC_checkGC(L), /* stack grow uses memory */ \ 52 | p = restorestack(L, t__)) /* 'pos' part: restore 'p' */ 53 | 54 | 55 | /* macro to check stack size and GC */ 56 | #define checkstackGC(L,fsize) \ 57 | luaD_checkstackaux(L, (fsize), luaC_checkGC(L), (void)0) 58 | 59 | 60 | /* type of protected functions, to be ran by 'runprotected' */ 61 | typedef void (*Pfunc) (lua_State *L, void *ud); 62 | 63 | LUAI_FUNC void luaD_seterrorobj (lua_State *L, int errcode, StkId oldtop); 64 | LUAI_FUNC int luaD_protectedparser (lua_State *L, ZIO *z, const char *name, 65 | const char *mode); 66 | LUAI_FUNC void luaD_hook (lua_State *L, int event, int line, 67 | int fTransfer, int nTransfer); 68 | LUAI_FUNC void luaD_hookcall (lua_State *L, CallInfo *ci); 69 | LUAI_FUNC int luaD_pretailcall (lua_State *L, CallInfo *ci, StkId func, 70 | int narg1, int delta); 71 | LUAI_FUNC CallInfo *luaD_precall (lua_State *L, StkId func, int nResults); 72 | LUAI_FUNC void luaD_call (lua_State *L, StkId func, int nResults); 73 | LUAI_FUNC void luaD_callnoyield (lua_State *L, StkId func, int nResults); 74 | LUAI_FUNC StkId luaD_tryfuncTM (lua_State *L, StkId func); 75 | LUAI_FUNC int luaD_closeprotected (lua_State *L, ptrdiff_t level, int status); 76 | LUAI_FUNC int luaD_pcall (lua_State *L, Pfunc func, void *u, 77 | ptrdiff_t oldtop, ptrdiff_t ef); 78 | LUAI_FUNC void luaD_poscall (lua_State *L, CallInfo *ci, int nres); 79 | LUAI_FUNC int luaD_reallocstack (lua_State *L, int newsize, int raiseerror); 80 | LUAI_FUNC int luaD_growstack (lua_State *L, int n, int raiseerror); 81 | LUAI_FUNC void luaD_shrinkstack (lua_State *L); 82 | LUAI_FUNC void luaD_inctop (lua_State *L); 83 | 84 | LUAI_FUNC l_noret luaD_throw (lua_State *L, int errcode); 85 | LUAI_FUNC int luaD_rawrunprotected (lua_State *L, Pfunc f, void *ud); 86 | 87 | #endif 88 | 89 | -------------------------------------------------------------------------------- /docs/pages/tutorial.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: docs 3 | title: Tutorial 4 | permalink: /tutorial/ 5 | categories: docs toc 6 | order: 2 7 | --- 8 | 9 | {% raw %} 10 | 11 | This is a basic tutorial for writing your first application in the Nelua language. 12 | {: .lead} 13 | 14 | **Don't have Nelua installed yet?** Read the [installation tutorial](/installing/) first. 15 | {: .callout.callout-info} 16 | 17 | ## Your first program 18 | 19 | You can code in Nelua much like you would code in Lua. For example, a hello world program is written much the same: 20 | 21 | ```nelua 22 | print 'Hello world' 23 | ``` 24 | 25 | This example is already in the examples folder in the repository. First clone the language repository 26 | if you haven't yet: 27 | 28 | ```bash 29 | git clone git@github.com:edubart/nelua-lang.git 30 | cd nelua-lang 31 | ``` 32 | 33 | Now you can run it: 34 | ```bash 35 | nelua examples/helloworld.nelua 36 | ``` 37 | 38 | When running it you should get an output that looks like this: 39 | ```bash 40 | hello world 41 | ``` 42 | 43 | Now run again with: 44 | ```bash 45 | nelua --verbose examples/helloworld.nelua 46 | ``` 47 | 48 | You will notice some messages of what the compiler is doing, 49 | and notice that it generated a file called `helloworld.c`. 50 | This is your program translated to C source code. 51 | If you know how to read C, I encourage you to open it and have a look. 52 | The compiler tries to generate efficient, compact, and readable C code. 53 | 54 | After the C source file is generated, GCC is invoked to compile it, 55 | and then the program is executed. 56 | 57 | If your machine does not have GCC, you can use another C compiler with the flag `--cc`. 58 | For example, if you are on MacOS, you probably want to use Clang. In that case 59 | do `nelua --cc clang examples/helloworld.nelua`. 60 | If you are on Windows, you probably want to use MinGW, so 61 | do `nelua --cc x86_64-w64-mingw32-gcc examples/helloworld.nelua`. 62 | 63 | ## Syntax highlighting for editors 64 | 65 | Syntax definitions for the language are available for 66 | Sublime Text with [nelua-sublime](https://github.com/edubart/nelua-sublime). 67 | At the moment, only Sublime Text has a full definition, so I recommend giving it a try. 68 | If you use another code editor you can use Lua syntax highlighting, 69 | as it is very similar (but of course, incomplete). 70 | 71 | I recommend using the syntax highlighter, 72 | as it makes the experience of playing around with the language more pleasant, since 73 | it can highlight type notations. 74 | 75 | ## Language features 76 | 77 | A quick tour of the language features can be found in the [overview page](/overview/), 78 | I highly recommend reading it if you haven't yet. 79 | 80 | ## More examples 81 | 82 | As the language is being developed, this tutorial is quite short. 83 | However you can see and run more interesting examples of the language in the 84 | [examples](https://github.com/edubart/nelua-lang/tree/master/examples) or 85 | [tests](https://github.com/edubart/nelua-lang/tree/master/tests) 86 | folders. 87 | 88 | The most interesing examples are: 89 | * `examples/fibonacci.nelua` multiple implementations of the classic [Fibonnaci sequence](https://en.wikipedia.org/wiki/Fibonacci_number) 90 | * `examples/brainfuck.nelua` use of metaprogramming to code the esoteric [Brainfuck language](https://en.wikipedia.org/wiki/Brainfuck) 91 | * `examples/snakesdl.nelua` the classic Snake game (requires SDL) 92 | * `examples/condots.nelua` connected dots graphic animation made in parallel (requires SDL and OpenMP) 93 | 94 | Overview >> 95 | 96 | {% endraw %} 97 | -------------------------------------------------------------------------------- /src/lua/lmem.h: -------------------------------------------------------------------------------- 1 | /* 2 | ** $Id: lmem.h $ 3 | ** Interface to Memory Manager 4 | ** See Copyright Notice in lua.h 5 | */ 6 | 7 | #ifndef lmem_h 8 | #define lmem_h 9 | 10 | 11 | #include 12 | 13 | #include "llimits.h" 14 | #include "lua.h" 15 | 16 | 17 | #define luaM_error(L) luaD_throw(L, LUA_ERRMEM) 18 | 19 | 20 | /* 21 | ** This macro tests whether it is safe to multiply 'n' by the size of 22 | ** type 't' without overflows. Because 'e' is always constant, it avoids 23 | ** the runtime division MAX_SIZET/(e). 24 | ** (The macro is somewhat complex to avoid warnings: The 'sizeof' 25 | ** comparison avoids a runtime comparison when overflow cannot occur. 26 | ** The compiler should be able to optimize the real test by itself, but 27 | ** when it does it, it may give a warning about "comparison is always 28 | ** false due to limited range of data type"; the +1 tricks the compiler, 29 | ** avoiding this warning but also this optimization.) 30 | */ 31 | #define luaM_testsize(n,e) \ 32 | (sizeof(n) >= sizeof(size_t) && cast_sizet((n)) + 1 > MAX_SIZET/(e)) 33 | 34 | #define luaM_checksize(L,n,e) \ 35 | (luaM_testsize(n,e) ? luaM_toobig(L) : cast_void(0)) 36 | 37 | 38 | /* 39 | ** Computes the minimum between 'n' and 'MAX_SIZET/sizeof(t)', so that 40 | ** the result is not larger than 'n' and cannot overflow a 'size_t' 41 | ** when multiplied by the size of type 't'. (Assumes that 'n' is an 42 | ** 'int' or 'unsigned int' and that 'int' is not larger than 'size_t'.) 43 | */ 44 | #define luaM_limitN(n,t) \ 45 | ((cast_sizet(n) <= MAX_SIZET/sizeof(t)) ? (n) : \ 46 | cast_uint((MAX_SIZET/sizeof(t)))) 47 | 48 | 49 | /* 50 | ** Arrays of chars do not need any test 51 | */ 52 | #define luaM_reallocvchar(L,b,on,n) \ 53 | cast_charp(luaM_saferealloc_(L, (b), (on)*sizeof(char), (n)*sizeof(char))) 54 | 55 | #define luaM_freemem(L, b, s) luaM_free_(L, (b), (s)) 56 | #define luaM_free(L, b) luaM_free_(L, (b), sizeof(*(b))) 57 | #define luaM_freearray(L, b, n) luaM_free_(L, (b), (n)*sizeof(*(b))) 58 | 59 | #define luaM_new(L,t) cast(t*, luaM_malloc_(L, sizeof(t), 0)) 60 | #define luaM_newvector(L,n,t) cast(t*, luaM_malloc_(L, (n)*sizeof(t), 0)) 61 | #define luaM_newvectorchecked(L,n,t) \ 62 | (luaM_checksize(L,n,sizeof(t)), luaM_newvector(L,n,t)) 63 | 64 | #define luaM_newobject(L,tag,s) luaM_malloc_(L, (s), tag) 65 | 66 | #define luaM_growvector(L,v,nelems,size,t,limit,e) \ 67 | ((v)=cast(t *, luaM_growaux_(L,v,nelems,&(size),sizeof(t), \ 68 | luaM_limitN(limit,t),e))) 69 | 70 | #define luaM_reallocvector(L, v,oldn,n,t) \ 71 | (cast(t *, luaM_realloc_(L, v, cast_sizet(oldn) * sizeof(t), \ 72 | cast_sizet(n) * sizeof(t)))) 73 | 74 | #define luaM_shrinkvector(L,v,size,fs,t) \ 75 | ((v)=cast(t *, luaM_shrinkvector_(L, v, &(size), fs, sizeof(t)))) 76 | 77 | LUAI_FUNC l_noret luaM_toobig (lua_State *L); 78 | 79 | /* not to be called directly */ 80 | LUAI_FUNC void *luaM_realloc_ (lua_State *L, void *block, size_t oldsize, 81 | size_t size); 82 | LUAI_FUNC void *luaM_saferealloc_ (lua_State *L, void *block, size_t oldsize, 83 | size_t size); 84 | LUAI_FUNC void luaM_free_ (lua_State *L, void *block, size_t osize); 85 | LUAI_FUNC void *luaM_growaux_ (lua_State *L, void *block, int nelems, 86 | int *size, int size_elem, int limit, 87 | const char *what); 88 | LUAI_FUNC void *luaM_shrinkvector_ (lua_State *L, void *block, int *nelem, 89 | int final_n, int size_elem); 90 | LUAI_FUNC void *luaM_malloc_ (lua_State *L, size_t size, int tag); 91 | 92 | #endif 93 | 94 | -------------------------------------------------------------------------------- /lualib/nelua/analyzercontext.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Analyzer context. 3 | 4 | This is the context used while analyzing an AST. 5 | It extends the visitor context and adds some methods to assist analyzing. 6 | ]] 7 | 8 | local class = require 'nelua.utils.class' 9 | local stringer = require 'nelua.utils.stringer' 10 | local Scope = require 'nelua.scope' 11 | local VisitorContext = require 'nelua.visitorcontext' 12 | 13 | -- The analyzer context class. 14 | local AnalyzerContext = class(VisitorContext) 15 | 16 | -- Used to quickly check whether a table is an analyzer context. 17 | AnalyzerContext._analyzercontext = true 18 | 19 | function AnalyzerContext:_init(visitors, ast, generator) 20 | assert(visitors and ast and generator) 21 | local rootscope = Scope.create_root(self, ast) 22 | VisitorContext._init(self, visitors, rootscope) 23 | self.ast = ast 24 | self.scope = self.rootscope 25 | self.usedbuiltins = {} 26 | self.requires = {} 27 | self.usedcodenames = {} 28 | self.afteranalyzes = {} 29 | self.afterinfers = {} 30 | self.unresolvedcount = 0 31 | self.generator = generator 32 | end 33 | 34 | function AnalyzerContext:push_forked_cleaned_scope(node) 35 | local scope = self:push_forked_scope(node) 36 | scope:clear_symbols() 37 | return scope 38 | end 39 | 40 | function AnalyzerContext:mark_funcscope_sideeffect() 41 | local funcscope = self.state.funcscope 42 | if funcscope then 43 | funcscope.sideeffect = true 44 | end 45 | end 46 | 47 | function AnalyzerContext:choose_codename(name) 48 | local unitname = self.pragmas.unitname or self.state.unitname 49 | name = name:gsub('[^%w_]','_') 50 | if unitname and unitname ~= '' then 51 | unitname = unitname .. '_' 52 | if not stringer.startswith(name, unitname) then 53 | name = unitname .. name 54 | end 55 | end 56 | local usedcodenames = self.usedcodenames 57 | local count = usedcodenames[name] 58 | if count then 59 | usedcodenames[name] = count + 1 60 | name = string.format('%s_%d', name, count) 61 | end 62 | usedcodenames[name] = 1 63 | return name 64 | end 65 | 66 | function AnalyzerContext:choose_type_symbol_names(symbol) 67 | local type = symbol.value 68 | if type:suggest_nickname(symbol.name) then 69 | if symbol.staticstorage and symbol.codename then 70 | type:set_codename(symbol.codename) 71 | else 72 | local codename = self:choose_codename(symbol.name) 73 | type:set_codename(codename) 74 | end 75 | type.symbol = symbol 76 | end 77 | end 78 | 79 | --[[ 80 | local nodetravs, numretravs, numtravs = {}, {}, {} 81 | local function bench_traverse(self, node) 82 | if node._astnode then 83 | local tag = node.tag 84 | numtravs[tag] = (numtravs[tag] or 0) + 1 85 | if nodetravs[node] then 86 | numretravs[tag] = (numretravs[tag] or 0) + 1 87 | end 88 | nodetravs[node] = true 89 | end 90 | if #self.nodestack == 0 then 91 | print '============================report' 92 | for k,v in require'nelua.utils.iterators'.ospairs(numretravs) do 93 | print(v,k, string.format('%.2f', v*100/numtravs[k])) 94 | end 95 | end 96 | end 97 | ]] 98 | 99 | --[[ 100 | Like `VisitorContext:traverse_node`, but optimized for analyzer context. 101 | When analyzing in case the node is marked as `done` its traversal will be skipped. 102 | ]] 103 | function AnalyzerContext:traverse_node(node, ...) 104 | local done = node.done 105 | if done then 106 | return done ~= true and done or nil 107 | end 108 | -- bench_traverse(self, node) 109 | local nodestack = self.nodestack 110 | local index = #nodestack+1 111 | nodestack[index] = node -- push node 112 | local ret = self.visitors[node.tag](self, node, ...) 113 | nodestack[index] = nil -- pop node 114 | return ret 115 | end 116 | 117 | return AnalyzerContext 118 | -------------------------------------------------------------------------------- /src/lpeglabel/lpltypes.h: -------------------------------------------------------------------------------- 1 | /* 2 | ** $Id: lpltypes.h $ 3 | ** LPeg - PEG pattern matching for Lua 4 | ** Copyright 2007-2019, Lua.org & PUC-Rio (see 'lpeg.html' for license) 5 | ** written by Roberto Ierusalimschy 6 | */ 7 | 8 | #if !defined(lpltypes_h) 9 | #define lpltypes_h 10 | 11 | 12 | #include 13 | #include 14 | 15 | #include "lua.h" 16 | 17 | 18 | #define VERSION "1.6.1" 19 | 20 | 21 | #define PATTERN_T "lpeglabel-pattern" 22 | #define MAXSTACKIDX "lpeglabel-maxstack" 23 | 24 | 25 | /* 26 | ** compatibility with Lua 5.1 27 | */ 28 | #if (LUA_VERSION_NUM == 501) 29 | 30 | #define lp_equal lua_equal 31 | 32 | #define lua_getuservalue lua_getfenv 33 | #define lua_setuservalue lua_setfenv 34 | 35 | #define lua_rawlen lua_objlen 36 | 37 | #define luaL_setfuncs(L,f,n) luaL_register(L,NULL,f) 38 | #define luaL_newlib(L,f) luaL_register(L,"lpeglabel",f) 39 | 40 | typedef size_t lua_Unsigned; 41 | 42 | #endif 43 | 44 | 45 | #if !defined(lp_equal) 46 | #define lp_equal(L,idx1,idx2) lua_compare(L,(idx1),(idx2),LUA_OPEQ) 47 | #endif 48 | 49 | 50 | /* default maximum size for call/backtrack stack */ 51 | #if !defined(MAXBACK) 52 | #define MAXBACK 400 53 | #endif 54 | 55 | 56 | /* maximum number of rules in a grammar (limited by 'unsigned short') */ 57 | #if !defined(MAXRULES) 58 | #define MAXRULES 1000 59 | #endif 60 | 61 | 62 | 63 | /* initial size for capture's list */ 64 | #define INITCAPSIZE 32 65 | 66 | 67 | /* index, on Lua stack, for subject */ 68 | #define SUBJIDX 2 69 | 70 | /* number of fixed arguments to 'match' (before capture arguments) */ 71 | #define FIXEDARGS 3 72 | 73 | /* index, on Lua stack, for capture list */ 74 | #define caplistidx(ptop) ((ptop) + 2) 75 | 76 | /* index, on Lua stack, for pattern's ktable */ 77 | #define ktableidx(ptop) ((ptop) + 3) 78 | 79 | /* index, on Lua stack, for backtracking stack */ 80 | #define stackidx(ptop) ((ptop) + 4) 81 | 82 | 83 | 84 | typedef unsigned char byte; 85 | 86 | 87 | #define BITSPERCHAR 8 88 | 89 | #define CHARSETSIZE ((UCHAR_MAX/BITSPERCHAR) + 1) 90 | 91 | 92 | 93 | typedef struct Charset { 94 | byte cs[CHARSETSIZE]; 95 | } Charset; 96 | 97 | 98 | 99 | #define loopset(v,b) { int v; for (v = 0; v < CHARSETSIZE; v++) {b;} } 100 | 101 | /* access to charset */ 102 | #define treebuffer(t) ((byte *)((t) + 1)) 103 | 104 | /* number of slots needed for 'n' bytes */ 105 | #define bytes2slots(n) (((n) - 1) / sizeof(TTree) + 1) 106 | 107 | /* set 'b' bit in charset 'cs' */ 108 | #define setchar(cs,b) ((cs)[(b) >> 3] |= (1 << ((b) & 7))) 109 | 110 | 111 | /* 112 | ** in capture instructions, 'kind' of capture and its offset are 113 | ** packed in field 'aux', 4 bits for each 114 | */ 115 | #define getkind(op) ((op)->i.aux & 0xF) 116 | #define getoff(op) (((op)->i.aux >> 4) & 0xF) 117 | #define joinkindoff(k,o) ((k) | ((o) << 4)) 118 | 119 | #define MAXOFF 0xF 120 | #define MAXAUX 0xFF 121 | 122 | 123 | /* maximum number of bytes to look behind */ 124 | #define MAXBEHIND MAXAUX 125 | 126 | 127 | /* maximum size (in elements) for a pattern */ 128 | #define MAXPATTSIZE (SHRT_MAX - 10) 129 | 130 | 131 | /* size (in elements) for an instruction plus extra l bytes */ 132 | #define instsize(l) (((l) + sizeof(Instruction) - 1)/sizeof(Instruction) + 1) 133 | 134 | 135 | /* size (in elements) for a ISet instruction */ 136 | #define CHARSETINSTSIZE instsize(CHARSETSIZE) 137 | 138 | /* size (in elements) for a IFunc instruction */ 139 | #define funcinstsize(p) ((p)->i.aux + 2) 140 | 141 | 142 | 143 | #define testchar(st,c) (((int)(st)[((c) >> 3)] & (1 << ((c) & 7)))) 144 | 145 | /* labeled failure begin */ 146 | #if !defined(LUAI_FUNC) 147 | #define LUAI_FUNC extern 148 | #endif 149 | 150 | #define LFAIL 0 151 | 152 | /* update the farthest failure */ 153 | #define updatefarthest(s1,s2) { if ((s2) > (s1)) s1 = s2; } 154 | 155 | #define OUTPRED 0 156 | 157 | #define INPRED 1 158 | /* labeled failure end */ 159 | 160 | #endif 161 | 162 | -------------------------------------------------------------------------------- /docs/pages/installing.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: docs 3 | title: Installing 4 | permalink: /installing/ 5 | categories: docs toc 6 | order: 1 7 | --- 8 | 9 | {% raw %} 10 | 11 | Instructions for installing Nelua on Windows or Linux. 12 | {: .lead} 13 | 14 | To install Nelua you need a system with the following: 15 | 16 | * Git (for cloning Nelua) 17 | * A C compiler (GCC or Clang are recommended) 18 | * Build tools (such as make) 19 | * GDB debugger (in case you want to debug runtime errors) 20 | 21 | ## Linux 22 | 23 | Use your system's package manager to install all of the required tools first: 24 | 25 | For example, on **Ubuntu** you should do: 26 | 27 | ```bash 28 | sudo apt-get install build-essential git gcc gdb 29 | ``` 30 | 31 | While on **ArchLinux**, you should do: 32 | 33 | ```bash 34 | sudo pacman -S base-devel git gcc gdb 35 | ``` 36 | 37 | ## MacOS 38 | 39 | First make sure you have installed **brew**, then run: 40 | 41 | ```bash 42 | brew install gcc gdb git make 43 | ``` 44 | 45 | ## Windows 46 | 47 | MSYS2 is the recommended and supported environment to use Nelua on Windows. 48 | Although you could use other tools, MSYS2 makes using Nelua very easy on Windows, 49 | plus there are many useful C packages on MSYS2 that you can install with ease, such as 50 | SDL2. 51 | 52 | Download and install [MSYS2](https://www.msys2.org/). 53 | After installing open the **64 bit terminal**, that is, 54 | **msys64**, and update: 55 | 56 | ```bash 57 | pacman -Syu 58 | ``` 59 | 60 | You may need close and reopen the terminal and update a second time using the same command. 61 | 62 | Now install all the required tools: 63 | 64 | ```bash 65 | pacman -S base-devel git mingw-w64-x86_64-gcc gdb 66 | ``` 67 | 68 | ## Clone and Install 69 | 70 | Now you can clone the project and compile: 71 | 72 | ```bash 73 | git clone https://github.com/edubart/nelua-lang.git && cd nelua-lang 74 | make 75 | ``` 76 | 77 | Now install in your system: 78 | 79 | ```bash 80 | sudo make install 81 | ``` 82 | 83 | On Linux this will install in `/usr/local` by default, 84 | you could install it somewhere else using the `PREFIX` argument. 85 | For example, suppose you want to install in your home directory, 86 | then you could use `make install PREFIX=/home/user/nelua` 87 | and the Nelua compiler would be available at `/home/user/nelua/bin/nelua`. 88 | 89 | Alternatively, if you want to run Nelua directly from the cloned repository, 90 | then you have the following options: 91 | * You could add the cloned `nelua-lang` directory to your `PATH` environment variable, 92 | then the `nelua` command will become available in your terminal. 93 | * You could create symbolic links to `./nelua` and `./nelua-lua` 94 | in one directory of your `PATH` environment variable. 95 | * You could run the `./nelua` file directly. 96 | 97 | Proceed to the [testing section](#testing). 98 | 99 | ## Testing 100 | 101 | Nelua should be installed. Run `nelua -h` in your terminal to check if it is working. 102 | If doesn't work, your environment `PATH` variable is missing the `bin` folder of the Nelua installation. 103 | Add it or find and execute the full path to the installed Nelua compiler to use it. 104 | 105 | Run the hello world example: 106 | 107 | ```bash 108 | nelua examples/helloworld.nelua 109 | ``` 110 | 111 | You can run any file in the `examples` or `tests` directories, 112 | play with them to test or to learn how to code in Nelua. 113 | 114 | The most interesting examples are perhaps the graphical ones, 115 | such as `snakesdl.nelua` and `condots.nelua`. 116 | 117 | To run the Snake SDL game demo, for example, you will need to have the SDL2 library installed. 118 | Install it using your system's package manager and run the example: 119 | 120 | ```bash 121 | # install SDL2 on MSYS2 122 | pacman -S mingw-w64-x86_64-SDL2 123 | # install SDL2 on Ubuntu 124 | sudo apt-get install libsdl2-dev 125 | # run it 126 | nelua examples/snakesdl.nelua 127 | ``` 128 | 129 | Tutorial >> 130 | 131 | {% endraw %} 132 | -------------------------------------------------------------------------------- /lib/C/stdlib.nelua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Library that imports symbols from the `` header according to C11 specifications. 3 | 4 | For a complete documentation about the functions, 5 | see [C stdlib documentation](https://www.cplusplus.com/reference/cstdlib/). 6 | ]] 7 | 8 | require 'C' 9 | 10 | -- Types 11 | 12 | global C.div_t: type '> = @record{quot: cint, rem: cint} 13 | global C.ldiv_t: type '> = @record{quot: cint, rem: cint} 14 | global C.lldiv_t: type '> = @record{quot: cint, rem: cint} 15 | 16 | -- Memory management 17 | 18 | function C.malloc(size: csize): pointer '> end 19 | function C.calloc(nmemb: csize, size: csize): pointer '> end 20 | function C.realloc(ptr: pointer, size: csize): pointer '> end 21 | function C.free(ptr: pointer): void '> end 22 | function C.aligned_alloc(alignment: csize, size: csize): pointer '> end 23 | 24 | -- Program termination 25 | 26 | function C.abort(): void '> end 27 | function C.exit(status: cint): void '> end 28 | function C.quick_exit(status: cint): void '> end 29 | function C._Exit(status: cint): void '> end 30 | function C.atexit(func: function(): void): cint '> end 31 | function C.at_quick_exit(func: function(): void): cint '> end 32 | 33 | -- Communicating with the environment 34 | 35 | function C.system(command: cstring): cint '> end 36 | function C.getenv(name: cstring): cstring '> end 37 | 38 | -- Searching and sorting 39 | 40 | function C.bsearch(key: pointer, base: pointer, nmemb: csize, size: csize, compar: function(pointer, pointer): cint): pointer '> end 41 | function C.qsort(base: pointer, nmemb: csize, size: csize, compar: function(pointer, pointer): cint): void '> end 42 | 43 | -- Pseudo-random sequence generation 44 | 45 | function C.rand(): cint '> end 46 | function C.srand(seed: cuint): void '> end 47 | 48 | -- Conversions to numeric formats 49 | 50 | function C.atof(nptr: cstring): float64 '> end 51 | function C.atoi(nptr: cstring): cint '> end 52 | function C.atol(nptr: cstring): clong '> end 53 | function C.atoll(nptr: cstring): clonglong '> end 54 | 55 | function C.strtof(nptr: cstring, endptr: *cstring): float32 '> end 56 | function C.strtod(nptr: cstring, endptr: *cstring): float64 '> end 57 | function C.strtold(nptr: cstring, endptr: *cstring): clongdouble '> end 58 | 59 | function C.strtol(nptr: cstring, endptr: *cstring, base: cint): clong '> end 60 | function C.strtoll(nptr: cstring, endptr: *cstring, base: cint): clonglong '> end 61 | function C.strtoul(nptr: cstring, endptr: *cstring, base: cint): culong '> end 62 | function C.strtoull(nptr: cstring, endptr: *cstring, base: cint): culonglong '> end 63 | 64 | -- Integer arithmetic 65 | 66 | function C.abs(x: cint): cint '> end 67 | function C.labs(x: clong): clong '> end 68 | function C.llabs(x: clonglong): clonglong '> end 69 | 70 | function C.div(numer: cint, denom: cint): C.div_t '> end 71 | function C.ldiv(numer: clong, denom: clong): C.ldiv_t '> end 72 | function C.lldiv(numer: clonglong, denom: clonglong): C.lldiv_t '> end 73 | 74 | -- Constants 75 | 76 | global C.EXIT_SUCCESS: cint '> 77 | global C.EXIT_FAILURE: cint '> 78 | global C.RAND_MAX: cint '> 79 | 80 | return C 81 | -------------------------------------------------------------------------------- /lualib/nelua/attr.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Attr module. 3 | 4 | The Attr class and in short 'attr' is used by the compiler to store many 5 | attributes associated to a symbol or a AST node during compilation. 6 | Usually the AST nodes are liked to an attr, multiple nodes can be linked 7 | to the same attr, this happens for example with variable identifiers. 8 | The compiler can promote an attr to a symbol in case it have a named 9 | identifier or in case it needs to perform type resolution. 10 | ]] 11 | 12 | local class = require 'nelua.utils.class' 13 | 14 | local Attr = class() 15 | 16 | -- Used to check if this table is an attr. 17 | Attr._attr = true 18 | 19 | -- Initialize an attr from a table of fields. 20 | function Attr._create(klass, attr) 21 | attr = setmetatable(attr or {}, klass) 22 | return attr 23 | end 24 | getmetatable(Attr).__call = Attr._create 25 | 26 | --[[ 27 | local tabler = require 'nelua.utils.tabler' 28 | -- Clone the attr, shallow copying all fields. 29 | function Attr:clone() 30 | -- getmetatable should be used here because this attr could be a promoted symbol 31 | -- so we should copy its underlying metatable 32 | local attr = setmetatable(tabler.copy(self), getmetatable(self) or Attr) 33 | return attr 34 | end 35 | ]] 36 | 37 | --[[ 38 | Merge fields from another attr into this attr. 39 | Mostly used when linking new nodes to the same attr. 40 | ]] 41 | function Attr:merge(attr) 42 | for k,v in pairs(attr) do 43 | if self[k] == nil then -- no collision 44 | self[k] = v 45 | else 46 | --[[ 47 | when the field is already set, 48 | the merge is not permitted to overwrite to a new value, 49 | otherwise cause bugs on what already have been processed by the compiler 50 | ]] 51 | assert(self[k] == v, 'cannot combine different attrs') 52 | end 53 | end 54 | return self 55 | end 56 | 57 | --[[ 58 | Checks if this attr is stored in the program static storage. 59 | Used for example by the GC to know if a variable should be scanned. 60 | ]] 61 | function Attr:is_on_static_storage() 62 | if self.vardecl and 63 | self.staticstorage and 64 | not self.comptime and 65 | (not self.type or (not self.type.size or self.type.size > 0)) 66 | then 67 | return true 68 | end 69 | return false 70 | end 71 | 72 | --[[ 73 | Checks if this attr could be holding a negative scalar value. 74 | Used by the C generator to optimize operations on non negatives values. 75 | ]] 76 | function Attr:is_maybe_negative() 77 | local type = self.type 78 | if type and type.is_scalar then -- must be a scalar to proper check this 79 | if type.is_unsigned then -- unsigned is never negative 80 | return false 81 | end 82 | if self.comptime and self.value >= 0 then -- comptime positive is never negative 83 | return false 84 | end 85 | end 86 | -- could be negative if the type is unknown yet, or some union, any.. 87 | return true 88 | end 89 | 90 | function Attr:is_readonly() 91 | return self.const or self.comptime or self.close or (self.type and self.type.is_comptime) 92 | end 93 | 94 | function Attr:is_forward_declare_type() 95 | return self.type and self.type.is_type and self.value.forwarddecl 96 | end 97 | 98 | function Attr:can_copy() 99 | local type = self.type 100 | return not (type and type.nocopy and self.lvalue) 101 | end 102 | 103 | function Attr:must_declare_at_runtime() 104 | local type = self.type 105 | return not (type.is_comptime or self.comptime or self.nodecl) 106 | end 107 | 108 | function Attr:must_define_at_runtime() 109 | local type = self.type 110 | return not (type.is_comptime or 111 | type.is_nilptr or 112 | self.comptime or 113 | (type.size == 0 and not self.refed and not type.is_array)) 114 | end 115 | 116 | function Attr:must_zero_initialize() 117 | return not (self.noinit or self.cexport or self.cimport or self.type.is_cvalist) 118 | end 119 | 120 | -- Returns compile-time value associated with this attr. 121 | function Attr:get_comptime_value() 122 | local type = self.type 123 | if type and (type.comptime or self.comptime) then 124 | return self.value 125 | end 126 | end 127 | 128 | return Attr 129 | --------------------------------------------------------------------------------