├── .github └── workflows │ └── ci.yml ├── .gitignore ├── CHANGELOG.md ├── Emakefile ├── LICENSE ├── Makefile ├── README.md ├── VERSION ├── doc_legacy ├── luerl.txt ├── luerl_old.txt ├── luerl_sandbox.txt ├── man │ ├── luerl.3 │ ├── luerl_old.3 │ └── luerl_sandbox.3 ├── pdf │ ├── luerl.pdf │ ├── luerl_old.pdf │ └── luerl_sandbox.pdf └── src │ ├── luerl.3.md │ ├── luerl_old.3.md │ └── luerl_sandbox.3.md ├── ebin └── luerl.app ├── examples ├── Makefile ├── benchmark │ ├── Makefile │ ├── benchmarks.erl │ ├── suites │ │ ├── COPYRIGHT │ │ ├── accum.lua │ │ ├── arguments.lua │ │ ├── callmap1.lua │ │ ├── callmap10.lua │ │ ├── chaincall.lua │ │ ├── concat.lua │ │ ├── elseif_large.lua │ │ ├── factory.lua │ │ ├── get.lua │ │ ├── inf.lua │ │ ├── is_integer.lua │ │ ├── mtvsclosure.lua │ │ ├── next_vs_pairs.lua │ │ ├── nloop.lua │ │ ├── nloop_simple.lua │ │ ├── return.lua │ │ ├── selectvstable.lua │ │ ├── sort-simple.lua │ │ ├── str_is_empty.lua │ │ ├── tailcall.lua │ │ ├── tclone.lua │ │ └── vararg.lua │ └── util │ │ └── extract_bench_keys.lua ├── euler │ ├── Makefile │ ├── euler.erl │ ├── problem_001.lua │ ├── problem_002.lua │ ├── problem_003.lua │ ├── problem_004.lua │ ├── problem_005.lua │ ├── problem_006.lua │ ├── problem_007.lua │ ├── problem_008.lua │ ├── problem_009.lua │ └── problem_010.lua ├── hello │ ├── Makefile │ ├── hello.erl │ ├── hello.lua │ ├── hello2-1.lua │ ├── hello2-10.lua │ ├── hello2-2.lua │ ├── hello2-3.lua │ ├── hello2-4.lua │ ├── hello2-5.lua │ ├── hello2-6.lua │ ├── hello2-7.lua │ ├── hello2-8.lua │ ├── hello2-9.lua │ ├── hello2.erl │ ├── hello_funcalls.erl │ ├── hello_sandbox.erl │ ├── hello_table.erl │ └── hello_userdata.erl └── minibench │ ├── Makefile │ ├── minibench.erl │ └── minibench2.erl ├── get_comp_opts.escript ├── include └── luerl.hrl ├── priv └── images │ ├── logo-large.png │ └── logo.png ├── rebar.config ├── rebar.config.script ├── rebar.lock ├── src ├── Elixir.Luerl.Old.erl ├── Elixir.Luerl.erl ├── NOTES ├── luerl.app.src ├── luerl.erl ├── luerl.hrl ├── luerl_anno.erl ├── luerl_app.erl ├── luerl_comp.erl ├── luerl_comp.hrl ├── luerl_comp_cg.erl ├── luerl_comp_env.erl ├── luerl_comp_lint.erl ├── luerl_comp_locf.erl ├── luerl_comp_normalise.erl ├── luerl_comp_peep.erl ├── luerl_comp_vars.erl ├── luerl_emul.erl ├── luerl_heap.erl ├── luerl_instrs.hrl ├── luerl_lib.erl ├── luerl_lib_basic.erl ├── luerl_lib_bit32.erl ├── luerl_lib_debug.erl ├── luerl_lib_io.erl ├── luerl_lib_math.erl ├── luerl_lib_os.erl ├── luerl_lib_os_date.erl ├── luerl_lib_package.erl ├── luerl_lib_string.erl ├── luerl_lib_string_format.erl ├── luerl_lib_table.erl ├── luerl_lib_utf8.erl ├── luerl_old.erl ├── luerl_parse.yrl ├── luerl_sandbox.erl ├── luerl_scan.xrl ├── luerl_sup.erl ├── luerl_util.erl ├── ttdict.erl └── ttsets.erl └── test ├── Elixir.Luerl_tests.erl ├── lib_os_SUITE.erl ├── luerl_funcall_tests.erl ├── luerl_old_funcall_tests.erl ├── luerl_old_tests.erl ├── luerl_return_SUITE.erl ├── luerl_return_SUITE_data ├── check_unicode.lua ├── decimal.lua ├── decimal_test.lua ├── fun_return_multi.lua ├── only_comments.lua ├── simple_return_1.lua ├── simple_return_multi.lua ├── table_indexed_table.lua ├── variable_args_1.lua └── variable_args_multi.lua ├── luerl_tests.erl └── luerl_time_SUITE.erl /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: Test 2 | 3 | on: 4 | workflow_dispatch: 5 | pull_request: 6 | types: 7 | - edited 8 | - opened 9 | - reopened 10 | - synchronize 11 | schedule: 12 | - cron: "0 0 * * 0" 13 | 14 | jobs: 15 | older-builds: 16 | name: Older Erlang/OTP ${{matrix.otp}} 17 | runs-on: ubuntu-22.04 18 | strategy: 19 | fail-fast: false 20 | matrix: 21 | otp: ["24"] 22 | rebar3: ["3.22.1"] 23 | steps: 24 | - uses: actions/checkout@v4 25 | - uses: erlef/setup-beam@v1 26 | with: 27 | version-type: loose 28 | otp-version: ${{matrix.otp}} 29 | rebar3-version: ${{matrix.rebar3}} 30 | - run: DIAGNOSTIC=1 rebar3 do eunit, ct --cover, cover 31 | 32 | newer-builds: 33 | name: Newer Erlang/OTP ${{matrix.otp}} 34 | runs-on: ubuntu-22.04 35 | strategy: 36 | matrix: 37 | otp: ["25", "26", "27"] 38 | rebar3: ["3.24"] 39 | steps: 40 | - uses: actions/checkout@v4 41 | - uses: erlef/setup-beam@v1 42 | with: 43 | otp-version: ${{matrix.otp}} 44 | rebar3-version: ${{matrix.rebar3}} 45 | - run: rebar3 do eunit, ct --cover, cover 46 | 47 | dialyzer: 48 | name: Run Dialyzer 49 | runs-on: ubuntu-22.04 50 | steps: 51 | - uses: actions/checkout@v4 52 | - uses: erlef/setup-beam@v1 53 | with: 54 | otp-version: "latest" 55 | rebar3-version: "latest" 56 | - run: rebar3 dialyzer 57 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .rebar3 2 | _* 3 | *.beam 4 | *.o 5 | *.plt 6 | *.swp 7 | *.swo 8 | *~ 9 | .DS_Store 10 | .rebar/* 11 | .eunit 12 | .erlang.cookie 13 | _build 14 | src/luerl_scan.erl 15 | src/luerl_parse.erl 16 | comp_opts.mk 17 | doc_legacy/epub/* 18 | doc/ 19 | ebin/*.beam 20 | log 21 | logs 22 | .idea 23 | *.iml 24 | rebar3.crashdump 25 | erl_crash.dump 26 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), 6 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 7 | 8 | ## Unreleased 9 | 10 | ## [1.4.1] 11 | 12 | ### Fixed 13 | 14 | - Move `doc/` folder to `doc_legacy/` so it doesn't collide with `ex_doc` artificats 15 | 16 | 17 | ## [1.4.0] 18 | 19 | This version was not released to Hex.pm due to a build issue 20 | 21 | ### Changed 22 | 23 | - (breaking) `luerl:get_private` returns `{ok, Val} | error` tuple 24 | 25 | ### Fixed 26 | 27 | - files with only comments can now be loaded 28 | - atoms are now decoded as strings 29 | - Erlang functions that return errors are now properly propagated upward and state is updated 30 | - binary error messages captured in pcall are not formatted 31 | 32 | 33 | [unreleased]: https://github.com/olivierlacan/keep-a-changelog/compare/v1.4.1...HEAD 34 | [1.3.0]: https://github.com/rvirding/luerl/compare/v1.4.0...v1.4.1 35 | [1.3.0]: https://github.com/rvirding/luerl/compare/v1.3.0...v1.4.0 36 | [1.3.0]: https://github.com/rvirding/luerl/compare/v1.2.3...v1.3.0 37 | [1.2.3]: https://github.com/rvirding/luerl/compare/v1.2.2...v1.2.3 38 | -------------------------------------------------------------------------------- /Emakefile: -------------------------------------------------------------------------------- 1 | %% -*- erlang -*- 2 | 3 | {'src/luerl*',[{outdir,ebin}]}. 4 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2016-2023 Robert Virding 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | # Makefile for Luerl 16 | # Building from .xrl, .yrl and .erl 17 | # Intermediaries from leex and yecc stay in ./src 18 | 19 | BINDIR = ./bin 20 | EBINDIR = ./ebin 21 | SRCDIR = ./src 22 | 23 | LIB = luerl 24 | 25 | # To run erl as bash 26 | FINISH = -run init stop -noshell 27 | 28 | # Scripts to be evaluated 29 | 30 | GET_VERSION = '{ok,[App]}=file:consult("src/$(LIB).app.src"), \ 31 | V=proplists:get_value(vsn,element(3,App)), \ 32 | io:format("~p~n",[V])' \ 33 | $(FINISH) 34 | 35 | 36 | ## The .erl, .xrl, .yrl and .beam files 37 | ESRCS := $(notdir $(wildcard $(SRCDIR)/*.erl)) 38 | XSRCS := $(notdir $(wildcard $(SRCDIR)/*.xrl)) 39 | YSRCS := $(notdir $(wildcard $(SRCDIR)/*.yrl)) 40 | EBINS = $(ESRCS:.erl=.beam) $(XSRCS:.xrl=.beam) $(YSRCS:.yrl=.beam) 41 | 42 | ERLCFLAGS = -W1 43 | ERLC ?= erlc 44 | 45 | all: compile 46 | 47 | .PHONY: all compile clean echo examples debug docs 48 | 49 | compile: comp_opts.mk $(addprefix $(EBINDIR)/, $(EBINS)) 50 | 51 | $(EBINDIR)/%.beam: $(SRCDIR)/%.erl $(SRCDIR)/luerl.hrl comp_opts.mk 52 | @ mkdir -p $(EBINDIR) 53 | $(ERLC) $(ERLCFLAGS) -o $(EBINDIR) $(COMP_OPTS) $(ERLCFLAGS) $< 54 | 55 | %.erl: %.xrl 56 | $(ERLC) -o $(SRCDIR) $< 57 | 58 | %.erl: %.yrl 59 | $(ERLC) -o $(SRCDIR) $< 60 | 61 | comp_opts.mk: get_comp_opts.escript 62 | escript get_comp_opts.escript 63 | 64 | -include comp_opts.mk 65 | 66 | clean: 67 | @ rm -f $(EBINDIR)/*.beam 68 | @ rm -f *.beam 69 | @ rm -f erl_crash.dump 70 | @ rm comp_opts.mk 71 | $(MAKE) -C examples clean 72 | 73 | clean-all: clean 74 | rm -rf _build 75 | 76 | echo: 77 | echo $(OBJECTS) 78 | 79 | get-version: 80 | @echo 81 | @echo "Getting version info ..." 82 | @echo 83 | @echo -n app.src: '' 84 | @erl -eval $(GET_VERSION) 85 | 86 | examples: all 87 | $(MAKE) -C examples 88 | 89 | debug: 90 | ERLCFLAGS="+debug_info" make all 91 | 92 | ############### 93 | ### TESTING ### 94 | ############### 95 | 96 | # XXX for some reason, the first pass of eunit doesn't run the tests?! 97 | eunit: 98 | @rebar3 as test do compile,eunit,eunit 99 | 100 | common-test: 101 | @rebar3 as test do compile,ct 102 | 103 | ct: common-test 104 | 105 | tests: 106 | @rebar3 as test do compile,eunit,eunit,ct 107 | 108 | # this protects the intermediate .erl files from make's auto deletion 109 | #.SECONDARY: $(XRL_INTERM) $(YRL_INTERM) 110 | 111 | ##################### 112 | ### DOCUMENTATION ### 113 | ##################### 114 | 115 | # Targets for generating docs and man pages 116 | DOCDIR = doc 117 | DOCSRC = $(DOCDIR)/src 118 | MANDIR = $(DOCDIR)/man 119 | PDFDIR = $(DOCDIR)/pdf 120 | EPUBDIR = $(DOCDIR)/epub 121 | MANINSTDIR ?= $(PREFIX)/share/man 122 | 123 | MAN1_SRCS = $(notdir $(wildcard $(DOCSRC)/*1.md)) 124 | MAN1S = $(MAN1_SRCS:.1.md=.1) 125 | TXT1S = $(MAN1_SRCS:.1.md=.txt) 126 | PDF1S = $(MAN1_SRCS:.1.md=.pdf) 127 | MAN3_SRCS = $(notdir $(wildcard $(DOCSRC)/*3.md)) 128 | MAN3S = $(MAN3_SRCS:.3.md=.3) 129 | PDF3S = $(MAN3_SRCS:.3.md=.pdf) 130 | TXT3S = $(MAN3_SRCS:.3.md=.txt) 131 | MAN7_SRCS = $(notdir $(wildcard $(DOCSRC)/*7.md)) 132 | MAN7S = $(MAN7_SRCS:.7.md=.7) 133 | TXT7S = $(MAN7_SRCS:.7.md=.txt) 134 | PDF7S = $(MAN7_SRCS:.7.md=.pdf) 135 | 136 | # For pandoc for generating PDFs as it omly accepts a few options. 137 | # xelatex is a reasonable default or wkhtmltopdf. 138 | PANDOCPDF ?= xelatex 139 | 140 | # Just generate the docs that are tracked in git 141 | docs: docs-txt 142 | 143 | docs-man: \ 144 | $(addprefix $(MANDIR)/, $(MAN1S)) \ 145 | $(addprefix $(MANDIR)/, $(MAN3S)) \ 146 | $(addprefix $(MANDIR)/, $(MAN7S)) 147 | 148 | 149 | $(MANDIR)/%.1: $(DOCSRC)/%.1.md 150 | pandoc -f markdown -s -t man -o $@ $< 151 | 152 | $(MANDIR)/%.3: $(DOCSRC)/%.3.md 153 | pandoc -f markdown -s -t man -o $@ $< 154 | 155 | $(MANDIR)/%.7: $(DOCSRC)/%.7.md 156 | pandoc -f markdown -s -t man -o $@ $< 157 | 158 | clean-docs: 159 | rm -f $(DOCDIR)/*.txt $(MANDIR)/*.[0-9] $(PDFDIR)/*.pdf $(EPUBDIR)/*.epub 160 | 161 | docs-txt: docs-man \ 162 | $(addprefix $(DOCDIR)/, $(TXT1S)) \ 163 | $(addprefix $(DOCDIR)/, $(TXT3S)) \ 164 | $(addprefix $(DOCDIR)/, $(TXT7S)) 165 | @if [ -f $(DOCDIR)/luerl_guide.txt ]; then \ 166 | cp $(DOCDIR)/luerl_guide.txt $(DOCDIR)/user_guide.txt ; \ 167 | fi 168 | 169 | $(DOCDIR)/%.txt: export GROFF_NO_SGR=1 170 | 171 | $(DOCDIR)/%.txt: $(MANDIR)/%.1 172 | groff -t -e -mandoc -Tutf8 $< | col -bx > $@ 173 | 174 | $(DOCDIR)/%.txt: $(MANDIR)/%.3 175 | groff -t -e -mandoc -Tutf8 $< | col -bx > $@ 176 | 177 | $(DOCDIR)/%.txt: $(MANDIR)/%.7 178 | groff -t -e -mandoc -Tutf8 $< | col -bx > $@ 179 | 180 | $(PDFDIR): 181 | @$(INSTALL_DIR) $(PDFDIR) 182 | 183 | docs-pdf: $(PDFDIR) \ 184 | $(addprefix $(PDFDIR)/, $(PDF1S)) \ 185 | $(addprefix $(PDFDIR)/, $(PDF3S)) \ 186 | $(addprefix $(PDFDIR)/, $(PDF7S)) 187 | 188 | $(PDFDIR)/%.pdf: $(DOCSRC)/%.1.md 189 | pandoc -f markdown --pdf-engine=$(PANDOCPDF) -o $@ $< 190 | 191 | $(PDFDIR)/%.pdf: $(DOCSRC)/%.3.md 192 | pandoc -f markdown --pdf-engine=$(PANDOCPDF) -o $@ $< 193 | 194 | $(PDFDIR)/%.pdf: $(DOCSRC)/%.7.md 195 | pandoc -f markdown --pdf-engine=$(PANDOCPDF) -o $@ $< 196 | 197 | ################ 198 | ### RELEASES ### 199 | ################ 200 | 201 | hex-publish: clean-all compile 202 | rebar3 hex publish package 203 | 204 | tags: 205 | git tag $(shell erl -eval $(GET_VERSION)|tr -d '"') 206 | git tag v$(shell erl -eval $(GET_VERSION)|tr -d '"') 207 | git push --tags 208 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Luerl 2 | 3 | *An implementation of Lua in Erlang* 4 | 5 | [![Build Status][gh-actions-badge]][gh-actions] 6 | [![Luerl Versions][luerl-badge]][luerl] 7 | [![Erlang Versions][erlang-badge]][versions] 8 | [![Tag][github-tag-badge]][github-tag] 9 | 10 | [![Project Logo][logo]][logo-large] 11 | 12 | **Alert**: The migration from Lua 5.2 to 5.3 is very much Work-In-Progress. Please test it but there are as yet no guarantees. 13 | 14 | ## About 15 | 16 | Luerl is an implementation of standard Lua 5.3 written in Erlang/OTP. 17 | 18 | Lua is a powerful, efficient, lightweight, embeddable scripting language common in games, IoT devices, AI bots, machine learning and scientific computing research. 19 | 20 | It supports procedural, object-oriented, functional, data-driven, reactive, organizational programming and data description. 21 | 22 | Being an extension language, Lua has no notion of a "main" program: it works as a library embedded in a host simple called the embedding program. The host program can invoke functions to execute a piece of Lua code, can write and read Lua variables, and can call Erlang functions by Lua code. 23 | 24 | Through the use of Erlang functions, Luerl can be augmented to cope with a wide range of different domains, creating a customized language sharing a syntactical framework. 25 | 26 | Luerl is implemented as a library, written in clean Erlang/OTP. For more information, read the [documentation](https://github.com/rvirding/luerl/wiki) and follow the [get started](https://github.com/rvirding/luerl/wiki/0.2-Getting-started) tutorial. You may also browse the [examples](https://github.com/rvirding/luerl/tree/develop/examples). 27 | 28 | ## Join the Community 29 | 30 | [Luerl on Discord](https://discord.gg/uxNjyysc) 31 | 32 | [Luerl Forum - Erlang Forums](https://erlangforums.com/luerl) 33 | 34 | Luerl embraces both [#Erlang](https://twitter.com/hashtag/erlang?src=hash) and [#LuaLang](https://twitter.com/hashtag/lualang?src=hash) communities and ecosystems. 35 | 36 | [//]: ---Named-Links--- 37 | 38 | [logo]: priv/images/logo.png 39 | [logo-large]: priv/images/logo-large.png 40 | [gh-actions-badge]: https://github.com/rvirding/luerl/workflows/Test/badge.svg 41 | [gh-actions]: https://github.com/rvirding/luerl/actions 42 | [luerl]: https://github.com/rvirding/luerl 43 | [luerl-badge]: https://img.shields.io/badge/luerl-1.1-blue.svg 44 | [erlang-badge]: https://img.shields.io/badge/erlang-24%20to%2026-blue.svg 45 | [versions]: https://github.com/rvirding/luerl/blob/master/.github/workflows/ci.yml 46 | [github-tag]: https://github.com/rvirding/luerl/tags 47 | [github-tag-badge]: https://img.shields.io/github/tag/rvirding/luerl.svg 48 | -------------------------------------------------------------------------------- /VERSION: -------------------------------------------------------------------------------- 1 | 1.4.1 2 | -------------------------------------------------------------------------------- /doc_legacy/luerl_old.txt: -------------------------------------------------------------------------------- 1 | luerl_old(3) Library Functions Manual luerl_old(3) 2 | 3 | Name 4 | luerl - The old original interface to the Luerl system 5 | 6 | Interface functions 7 | The Lua State parameter is the state of a Lua VM instance. It must be 8 | created with the luerl:init() call and be carried from one call to the 9 | next. 10 | 11 | As it is possible in Lua to create self-referencing data structures, 12 | indeed the standard libraries have many instances of this, then using 13 | the functions which decode their return values will generate an error 14 | when they would cause an infinite loop during the decoding. An simple 15 | example is the top level table which contains a key _G which references 16 | the top-level table. 17 | 18 | Note that Lua Chunks (see definition below) can travel between differ‐ 19 | ent States. They are precompiled bits of code, independent of State. 20 | That you can ‘carry around’ this is no unique to Luerl but a low-level 21 | implementation detail of the standard Lua language ⟨https://lua.org⟩, 22 | for more on chunks read ⟨https://www.lua.org/manual/5.3/man‐ 23 | ual.html#3.3.2⟩ the official Lua 5.3 reference manual 24 | ⟨https://www.lua.org/manual/5.3/manual.html⟩. 25 | 26 | Spec Definitions 27 | Binary means an Erlang binary string. 28 | Chunks means a portion of precompiled bytecode. 29 | State means a Lua State, this is a Lua VM instance. 30 | Path means a file system path and file name. 31 | KeyPath means an Erlang list of atoms representing nested names, 32 | e.g. [table,pack] for table.pack. 33 | Keys means Lua table keys, the keys of a key-value structure. 34 | 35 | Functions 36 | eval and do functions differ only in what they return. The do func‐ 37 | tions return results and a new Lua State, the eval functions return a 38 | tuple starting on ‘ok’ or ‘error’, then the result, or cause of error. 39 | 40 | do --> {Result, State} 41 | 42 | eval --> {ok, Result} | {error, Reason} 43 | 44 | luerl:eval(String|Binary|Form, State) -> {ok, Result} | {error, Reason, 45 | StackTrace}. 46 | Evaluate a Lua expression passed in as a string or binary, and return 47 | its result. 48 | 49 | luerl:evalfile(Path, State) -> {ok, Result} | {error, Reason, StackTrace}. 50 | 51 | Load and execute a file, and return the result. 52 | 53 | luerl:do(String|Binary|Form, State) -> {Result, NewState}. 54 | Evaluate a Lua expression and return its result, and the new Lua State. 55 | 56 | luerl:dofile(Path, State) -> {Result, NewState}. 57 | Load and execute the Lua code in the file and return its result, and 58 | the new Lua State. Equivalent to doing luerl:do(“return dofile(‘File‐ 59 | Name’)”). 60 | 61 | luerl:load(String|Binary[, CompileOptions], State) -> {ok,Function,New‐ 62 | State} | {error, Reason}. 63 | Parse a Lua chunk as string or binary, and return a compiled chunk 64 | (‘form’). 65 | 66 | luerl:loadfile(FileName[, CompileOptions], State) -> {ok,Function,NewState} 67 | | {error, Reason}. 68 | Parse a Lua file, and return a compiled chunk (‘form’). 69 | 70 | luerl:path_loadfile([Path, ], FileName[, CompileOptions], State) -> 71 | {ok,Function,FullName,State} | {error, Reason}. 72 | Search Path until the file FileName is found. Parse the file and re‐ 73 | turn a compiled chunk (‘form’). If Path is not given then the path de‐ 74 | fined in the environment variable LUA_LOAD_PATH is used. 75 | 76 | luerl:load_module(KeyPath, ErlangModule, State) -> State. 77 | Load ErlangModule and install its table at KeyPath which is encoded. 78 | 79 | luerl:load_module1(KeyPath, ErlangModule, State) -> State. 80 | Load ErlangModule and install its table at KeyPath which is NOT encoded 81 | 82 | luerl:init() -> State. 83 | Get a new Lua State = a fresh Lua VM instance. 84 | 85 | luerl:call(Form, Args, State) -> {Result,State} 86 | luerl:call_chunk(Form, Args, State) -> {Result,State} 87 | Call a compiled chunk or function. Use the call_chunk, call has been 88 | kept for backwards compatibility. 89 | 90 | luerl:call_function(KeyPath, Args, State) -> {Result,NewState} 91 | Call a function already defined in the state. KeyPath is a list of 92 | names to the function. KeyPath, Args and Result are automatically en‐ 93 | coded/decoded. 94 | 95 | luerl:call_function1(KeyPath, Args, State) -> {Result,NewState} 96 | Call a function already defined in the state. KeyPath is a list of 97 | keys to the function. KeyPath, Args and Result are NOT encoded/de‐ 98 | coded. 99 | 100 | luerl:call_method(MethPath, Args, State) -> {Result,NewState}. 101 | Call a method already defined in the state. MethPath is a list of 102 | names to the method. MethPath, Args and Result are automatically en‐ 103 | coded/decoded. 104 | 105 | luerl:call_method1(MethPath, Args, State) -> {Result,NewState} 106 | Call a method already defined in the state. MethPath is a list of keys 107 | to the method. Keys, Args and Result are NOT encoded/decoded. 108 | 109 | luerl:stop(State) -> GCedState. 110 | Garbage collects the state and (todo:) does away with it. 111 | 112 | luerl:gc(State) -> State. 113 | Runs the garbage collector on a state and returns the new state. 114 | 115 | luerl:set_table(KeyPath, Value, State) -> State. 116 | Sets a value inside the Lua state. Value is automatically encoded. 117 | 118 | luerl:set_table1(KeyPath, Value, State) -> State. 119 | Sets a value inside the Lua state. KeyPath and Value are NOT encoded. 120 | 121 | luerl:get_table(KeyPath, State) -> {Result,State}. 122 | Gets a value inside the Lua state. KeyPath and Result are automati‐ 123 | cally encoded. 124 | 125 | luerl:get_table1(KeyPath, State) -> {Result,State}. 126 | Gets a value inside the Lua state. KeyPath and Result are NOT en‐ 127 | coded/decoded. 128 | 129 | You can use this function to expose an function to the Lua code by us‐ 130 | ing this interface: fun(Args, State) -> {Results, State} 131 | 132 | Args and Results must be a list of Luerl compatible Erlang values. 133 | 134 | AUTHORS 135 | Jean Chassoul, Robert Virding. 136 | 137 | 2018-2024 luerl_old(3) 138 | -------------------------------------------------------------------------------- /doc_legacy/luerl_sandbox.txt: -------------------------------------------------------------------------------- 1 | luerl_sandbox(3) Library Functions Manual luerl_sandbox(3) 2 | 3 | Name 4 | luerl_sandbox - Fuctions for sandboxing Luerl evaluation 5 | 6 | Interface Functions 7 | The Lua State parameter is the state of a Lua VM instance. It must be 8 | created with the luerl:init() call and be carried from one call to the 9 | next. 10 | 11 | As it is possible in Lua to create self-referencing data structures, 12 | indeed the standard libraries have many instances of this, then using 13 | the functions which decode their return values will generate an error 14 | when they would cause an infinite loop during the decoding. An simple 15 | example is the top level table which contains a key _G which references 16 | the top-level table. 17 | 18 | Note that Lua Chunks (see definition below) can travel between differ‐ 19 | ent States. They are precompiled bits of code, independent of State. 20 | That you can ‘carry around’ this is no unique to Luerl but a low-level 21 | implementation detail of the standard Lua language ⟨https://lua.org⟩, 22 | for more on chunks read ⟨https://www.lua.org/manual/5.3/man‐ 23 | ual.html#3.3.2⟩ the official Lua 5.3 reference manual 24 | ⟨https://www.lua.org/manual/5.3/manual.html⟩. 25 | 26 | Spec Definitions 27 | Binary means an Erlang binary string. 28 | Chunks means a portion of precompiled bytecode. 29 | State means a Lua State, this is a Lua VM instance. 30 | Path means a file system path and file name. 31 | KeyPath means an Erlang list of atoms representing nested names, 32 | e.g. [table,pack] for table.pack. 33 | Keys means Lua table keys, the keys of a key-value structure. 34 | 35 | Functions 36 | init() -> State. 37 | init([ State | TablePaths]) -> State. 38 | init(State, TablePaths) -> State. 39 | Create a new sandboxed state. If a state is given as an argument then 40 | that state will be used otherwise a new default be be generated. 41 | TablePaths is a list of paths to functions which will be blocked. If 42 | none is given then the default list will be used. 43 | 44 | run(String | Binary) -> {Result, State} | {error, Reason}. 45 | run(String | Binary, State) -> {Result, State} | {error, Reason}. 46 | run(String | Binary, Flags, State) -> {Result, State} | {error, Reason}. 47 | Spawn a new process which runs the string String in State where the de‐ 48 | fault sandbox state will be used if none is given. Flags is a map or 49 | keyword list which can contain the following fields 50 | 51 | #{max_time => MaxTime, 52 | max_reductions => MaxReds, 53 | spawn_opts => SpawnOpts} 54 | 55 | MaxReds limits the number of reductions and MaxTime (default 100 msecs) 56 | the time to run the string, SpawnOpts are spawn options to the process 57 | running the evaluation. 58 | 59 | run(String | Binary) -> {Result, State} | {error, Reason}. 60 | run(String | Binary, State) -> {Result, State} | {error, Reason}. 61 | run(String | Binary, State, [ MaxReds | Flags ]) -> {Result, State} | {er‐ 62 | ror, Reason}. 63 | run(String | Binary, State, MaxReds, Flags) -> {Result, State} | {error, 64 | Reason}. 65 | run(String | Binary, State, MaxReds, Flags, Timeout) -> {Result, State} | 66 | {error, Reason}. 67 | This is the old interface to run. It still works but the new interface 68 | is recommended. Spawn a new process which runs the string String in 69 | State where the default sandbox state will be used if none is given. 70 | MaxReds limits the number of reductions and TimeOut (default 100 msecs) 71 | the time to run the string, Flags are spawn options to the process run‐ 72 | ning the evaluation. 73 | 74 | AUTHORS 75 | Robert Virding. 76 | 77 | 2023 luerl_sandbox(3) 78 | -------------------------------------------------------------------------------- /doc_legacy/man/luerl_old.3: -------------------------------------------------------------------------------- 1 | .\" Automatically generated by Pandoc 3.6 2 | .\" 3 | .TH "luerl_old" "3" "2018\-2024" "" 4 | .SH Name 5 | luerl \- The old original interface to the Luerl system 6 | .SH Interface functions 7 | The \f[B]Lua State\f[R] parameter is the state of a Lua VM instance. 8 | It must be created with the \f[B]luerl:init()\f[R] call and be carried 9 | from one call to the next. 10 | .PP 11 | As it is possible in Lua to create self\-referencing data structures, 12 | indeed the standard libraries have many instances of this, then using 13 | the functions which decode their return values will generate an error 14 | when they would cause an infinite loop during the decoding. 15 | An simple example is the top level table which contains a key 16 | \f[B]\f[CB]_G\f[B]\f[R] which references the top\-level table. 17 | .PP 18 | Note that Lua \f[B]Chunks\f[R] (see definition below) can travel between 19 | different States. 20 | They are precompiled bits of code, independent of State. 21 | That you can `carry around' this is no unique to Luerl but a low\-level 22 | implementation detail of the standard Lua \c 23 | .UR https://lua.org 24 | language 25 | .UE \c 26 | , for more on chunks \c 27 | .UR https://www.lua.org/manual/5.3/manual.html#3.3.2 28 | read 29 | .UE \c 30 | \ the official Lua 5.3 \c 31 | .UR https://www.lua.org/manual/5.3/manual.html 32 | reference manual 33 | .UE \c 34 | \&. 35 | .SS Spec Definitions 36 | \f[B]Binary\f[R] means an Erlang binary string. 37 | .PD 0 38 | .P 39 | .PD 40 | \f[B]Chunks\f[R] means a portion of precompiled bytecode. 41 | .PD 0 42 | .P 43 | .PD 44 | \f[B]State\f[R] means a Lua State, this \f[I]is\f[R] a Lua VM instance. 45 | .PD 0 46 | .P 47 | .PD 48 | \f[B]Path\f[R] means a file system path and file name. 49 | .PD 0 50 | .P 51 | .PD 52 | \f[B]KeyPath\f[R] means an Erlang list of \f[B]atoms\f[R] representing 53 | nested names, e.g.\ [table,pack] for table.pack. 54 | .PD 0 55 | .P 56 | .PD 57 | \f[B]Keys\f[R] means Lua table keys, the keys of a key\-value structure. 58 | .SS Functions 59 | \f[B]eval\f[R] and \f[B]do\f[R] functions differ only in what they 60 | return. 61 | The \f[B]do\f[R] functions return results and a new Lua State, the 62 | \f[B]eval\f[R] functions return a tuple starting on `ok' or `error', 63 | then the result, or cause of error. 64 | .IP 65 | .EX 66 | do \-\-> {Result, State} 67 | 68 | eval \-\-> {ok, Result} | {error, Reason} 69 | .EE 70 | .SS \f[B]\f[CB]luerl:eval(String|Binary|Form, State) \-> {ok, Result} | {error, Reason, StackTrace}.\f[B]\f[R] 71 | Evaluate a Lua expression passed in as a string or binary, and return 72 | its result. 73 | .SS \f[B]\f[CB]luerl:evalfile(Path, State) \-> {ok, Result} | {error, Reason, StackTrace}.\f[B]\f[R] 74 | Load and execute a file, and return the result. 75 | .SS \f[B]\f[CB]luerl:do(String|Binary|Form, State) \-> {Result, NewState}.\f[B]\f[R] 76 | Evaluate a Lua expression and return its result, and the new Lua State. 77 | .SS \f[B]\f[CB]luerl:dofile(Path, State) \-> {Result, NewState}.\f[B]\f[R] 78 | Load and execute the Lua code in the file and return its result, and the 79 | new Lua State. 80 | Equivalent to doing luerl:do(\[lq]return dofile(`FileName')\[rq]). 81 | .SS \f[B]\f[CB]luerl:load(String|Binary[, CompileOptions], State) \-> {ok,Function,NewState} | {error, Reason}.\f[B]\f[R] 82 | Parse a Lua chunk as string or binary, and return a compiled chunk 83 | (`form'). 84 | .SS \f[B]\f[CB]luerl:loadfile(FileName[, CompileOptions], State) \-> {ok,Function,NewState} | {error, Reason}.\f[B]\f[R] 85 | Parse a Lua file, and return a compiled chunk (`form'). 86 | .SS \f[B]\f[CB]luerl:path_loadfile([Path, ], FileName[, CompileOptions], State) \-> {ok,Function,FullName,State} | {error, Reason}.\f[B]\f[R] 87 | Search Path until the file FileName is found. 88 | Parse the file and return a compiled chunk (`form'). 89 | If Path is not given then the path defined in the environment variable 90 | LUA_LOAD_PATH is used. 91 | .SS \f[B]\f[CB]luerl:load_module(KeyPath, ErlangModule, State) \-> State.\f[B]\f[R] 92 | Load \f[CR]ErlangModule\f[R] and install its table at \f[CR]KeyPath\f[R] 93 | which is encoded. 94 | .SS \f[B]\f[CB]luerl:load_module1(KeyPath, ErlangModule, State) \-> State.\f[B]\f[R] 95 | Load \f[CR]ErlangModule\f[R] and install its table at \f[CR]KeyPath\f[R] 96 | which is \f[B]NOT\f[R] encoded 97 | .SS \f[B]\f[CB]luerl:init() \-> State.\f[B]\f[R] 98 | Get a new Lua State = a fresh Lua VM instance. 99 | .SS \f[B]\f[CB]luerl:call(Form, Args, State) \-> {Result,State}\f[B]\f[R] 100 | .SS \f[B]\f[CB]luerl:call_chunk(Form, Args, State) \-> {Result,State}\f[B]\f[R] 101 | Call a compiled chunk or function. 102 | Use the call_chunk, call has been kept for backwards compatibility. 103 | .SS \f[B]\f[CB]luerl:call_function(KeyPath, Args, State) \-> {Result,NewState}\f[B]\f[R] 104 | Call a function already defined in the state. 105 | \f[CR]KeyPath\f[R] is a list of names to the function. 106 | \f[CR]KeyPath\f[R], \f[CR]Args\f[R] and \f[CR]Result\f[R] are 107 | automatically encoded/decoded. 108 | .SS \f[B]\f[CB]luerl:call_function1(KeyPath, Args, State) \-> {Result,NewState}\f[B]\f[R] 109 | Call a function already defined in the state. 110 | \f[CR]KeyPath\f[R] is a list of keys to the function. 111 | \f[CR]KeyPath\f[R], \f[CR]Args\f[R] and \f[CR]Result\f[R] are 112 | \f[B]NOT\f[R] encoded/decoded. 113 | .SS \f[B]\f[CB]luerl:call_method(MethPath, Args, State) \-> {Result,NewState}.\f[B]\f[R] 114 | Call a method already defined in the state. 115 | \f[CR]MethPath\f[R] is a list of names to the method. 116 | \f[CR]MethPath\f[R], \f[CR]Args\f[R] and \f[CR]Result\f[R] are 117 | automatically encoded/decoded. 118 | .SS \f[B]\f[CB]luerl:call_method1(MethPath, Args, State) \-> {Result,NewState}\f[B]\f[R] 119 | Call a method already defined in the state. 120 | \f[CR]MethPath\f[R] is a list of keys to the method. 121 | \f[CR]Keys\f[R], \f[CR]Args\f[R] and \f[CR]Result\f[R] are \f[B]NOT\f[R] 122 | encoded/decoded. 123 | .SS \f[B]\f[CB]luerl:stop(State) \-> GCedState.\f[B]\f[R] 124 | Garbage collects the state and (todo:) does away with it. 125 | .SS \f[B]\f[CB]luerl:gc(State) \-> State.\f[B]\f[R] 126 | Runs the garbage collector on a state and returns the new state. 127 | .SS \f[B]\f[CB]luerl:set_table(KeyPath, Value, State) \-> State.\f[B]\f[R] 128 | Sets a value inside the Lua state. 129 | Value is automatically encoded. 130 | .SS \f[B]\f[CB]luerl:set_table1(KeyPath, Value, State) \-> State.\f[B]\f[R] 131 | Sets a value inside the Lua state. 132 | \f[CR]KeyPath\f[R] and \f[CR]Value\f[R] are \f[B]NOT\f[R] encoded. 133 | .SS \f[B]\f[CB]luerl:get_table(KeyPath, State) \-> {Result,State}.\f[B]\f[R] 134 | Gets a value inside the Lua state. 135 | \f[CR]KeyPath\f[R] and \f[CR]Result\f[R] are automatically encoded. 136 | .SS \f[B]\f[CB]luerl:get_table1(KeyPath, State) \-> {Result,State}.\f[B]\f[R] 137 | Gets a value inside the Lua state. 138 | \f[CR]KeyPath\f[R] and \f[CR]Result\f[R] are \f[B]NOT\f[R] 139 | encoded/decoded. 140 | .PP 141 | You can use this function to expose an function to the Lua code by using 142 | this interface: \f[CR]fun(Args, State) \-> {Results, State}\f[R] 143 | .PP 144 | Args and Results must be a list of Luerl compatible Erlang values. 145 | .SH AUTHORS 146 | Jean Chassoul, Robert Virding. 147 | -------------------------------------------------------------------------------- /doc_legacy/man/luerl_sandbox.3: -------------------------------------------------------------------------------- 1 | .\" Automatically generated by Pandoc 3.6 2 | .\" 3 | .TH "luerl_sandbox" "3" "2023" "" 4 | .SH Name 5 | luerl_sandbox \- Fuctions for sandboxing Luerl evaluation 6 | .SH Interface Functions 7 | The \f[B]Lua State\f[R] parameter is the state of a Lua VM instance. 8 | It must be created with the \f[B]luerl:init()\f[R] call and be carried 9 | from one call to the next. 10 | .PP 11 | As it is possible in Lua to create self\-referencing data structures, 12 | indeed the standard libraries have many instances of this, then using 13 | the functions which decode their return values will generate an error 14 | when they would cause an infinite loop during the decoding. 15 | An simple example is the top level table which contains a key 16 | \f[B]\f[CB]_G\f[B]\f[R] which references the top\-level table. 17 | .PP 18 | Note that Lua \f[B]Chunks\f[R] (see definition below) can travel between 19 | different States. 20 | They are precompiled bits of code, independent of State. 21 | That you can `carry around' this is no unique to Luerl but a low\-level 22 | implementation detail of the standard Lua \c 23 | .UR https://lua.org 24 | language 25 | .UE \c 26 | , for more on chunks \c 27 | .UR https://www.lua.org/manual/5.3/manual.html#3.3.2 28 | read 29 | .UE \c 30 | \ the official Lua 5.3 \c 31 | .UR https://www.lua.org/manual/5.3/manual.html 32 | reference manual 33 | .UE \c 34 | \&. 35 | .SS Spec Definitions 36 | \f[B]Binary\f[R] means an Erlang binary string. 37 | .PD 0 38 | .P 39 | .PD 40 | \f[B]Chunks\f[R] means a portion of precompiled bytecode. 41 | .PD 0 42 | .P 43 | .PD 44 | \f[B]State\f[R] means a Lua State, this \f[I]is\f[R] a Lua VM instance. 45 | .PD 0 46 | .P 47 | .PD 48 | \f[B]Path\f[R] means a file system path and file name. 49 | .PD 0 50 | .P 51 | .PD 52 | \f[B]KeyPath\f[R] means an Erlang list of \f[B]atoms\f[R] representing 53 | nested names, e.g.\ [table,pack] for table.pack. 54 | .PD 0 55 | .P 56 | .PD 57 | \f[B]Keys\f[R] means Lua table keys, the keys of a key\-value structure. 58 | .SS Functions 59 | .SS \f[B]\f[CB]init() \-> State.\f[B]\f[R] 60 | .SS \f[B]\f[CB]init([ State | TablePaths]) \-> State.\f[B]\f[R] 61 | .SS \f[B]\f[CB]init(State, TablePaths) \-> State.\f[B]\f[R] 62 | Create a new sandboxed state. 63 | If a state is given as an argument then that state will be used 64 | otherwise a new default be be generated. 65 | \f[CR]TablePaths\f[R] is a list of paths to functions which will be 66 | blocked. 67 | If none is given then the default list will be used. 68 | .SS \f[B]\f[CB]run(String | Binary) \-> {Result, State} | {error, Reason}.\f[B]\f[R] 69 | .SS \f[B]\f[CB]run(String | Binary, State) \-> {Result, State} | {error, Reason}.\f[B]\f[R] 70 | .SS \f[B]\f[CB]run(String | Binary, Flags, State) \-> {Result, State} | {error, Reason}.\f[B]\f[R] 71 | Spawn a new process which runs the string \f[CR]String\f[R] in 72 | \f[CR]State\f[R] where the default sandbox state will be used if none is 73 | given. 74 | \f[CR]Flags\f[R] is a map or keyword list which can contain the 75 | following fields 76 | .IP 77 | .EX 78 | #{max_time => MaxTime, 79 | max_reductions => MaxReds, 80 | spawn_opts => SpawnOpts} 81 | .EE 82 | .PP 83 | \f[CR]MaxReds\f[R] limits the number of reductions and 84 | \f[CR]MaxTime\f[R] (default 100 msecs) the time to run the string, 85 | \f[CR]SpawnOpts\f[R] are spawn options to the process running the 86 | evaluation. 87 | .SS \f[B]\f[CB]run(String | Binary) \-> {Result, State} | {error, Reason}.\f[B]\f[R] 88 | .SS \f[B]\f[CB]run(String | Binary, State) \-> {Result, State} | {error, Reason}.\f[B]\f[R] 89 | .SS \f[B]\f[CB]run(String | Binary, State, [ MaxReds | Flags ]) \-> {Result, State} | {error, Reason}.\f[B]\f[R] 90 | .SS \f[B]\f[CB]run(String | Binary, State, MaxReds, Flags) \-> {Result, State} | {error, Reason}.\f[B]\f[R] 91 | .SS \f[B]\f[CB]run(String | Binary, State, MaxReds, Flags, Timeout) \-> {Result, State} | {error, Reason}.\f[B]\f[R] 92 | This is the old interface to run. 93 | It still works but the new interface is recommended. 94 | Spawn a new process which runs the string \f[CR]String\f[R] in 95 | \f[CR]State\f[R] where the default sandbox state will be used if none is 96 | given. 97 | \f[CR]MaxReds\f[R] limits the number of reductions and 98 | \f[CR]TimeOut\f[R] (default 100 msecs) the time to run the string, 99 | \f[CR]Flags\f[R] are spawn options to the process running the 100 | evaluation. 101 | .SH AUTHORS 102 | Robert Virding. 103 | -------------------------------------------------------------------------------- /doc_legacy/pdf/luerl.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rvirding/luerl/f1077a818d2d89cc67265b47b9d1a6d639dd4728/doc_legacy/pdf/luerl.pdf -------------------------------------------------------------------------------- /doc_legacy/pdf/luerl_old.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rvirding/luerl/f1077a818d2d89cc67265b47b9d1a6d639dd4728/doc_legacy/pdf/luerl_old.pdf -------------------------------------------------------------------------------- /doc_legacy/pdf/luerl_sandbox.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rvirding/luerl/f1077a818d2d89cc67265b47b9d1a6d639dd4728/doc_legacy/pdf/luerl_sandbox.pdf -------------------------------------------------------------------------------- /doc_legacy/src/luerl_old.3.md: -------------------------------------------------------------------------------- 1 | % luerl_old(3) 2 | % Jean Chassoul, Robert Virding 3 | % 2018-2024 4 | 5 | # Name 6 | luerl - The old original interface to the Luerl system 7 | 8 | # Interface functions 9 | The **Lua State** parameter is the state of a Lua VM instance. It must be created with the **luerl:init()** call and be carried from one call to the next. 10 | 11 | As it is possible in Lua to create self-referencing data structures, indeed the standard libraries have many instances of this, then using the functions which decode their return values will generate an error when they would cause an infinite loop during the decoding. An simple example is the top level table which contains a key **`_G`** which references the top-level table. 12 | 13 | Note that Lua **Chunks** (see definition below) can travel between different States. They are precompiled bits of code, independent of State. That you can 'carry around' this is no unique to Luerl but a low-level implementation detail of the standard Lua [language](https://lua.org), for more on chunks [read](https://www.lua.org/manual/5.3/manual.html#3.3.2) the official Lua 5.3 [reference manual](https://www.lua.org/manual/5.3/manual.html). 14 | 15 | ## Spec Definitions 16 | **Binary** means an Erlang binary string. 17 | **Chunks** means a portion of precompiled bytecode. 18 | **State** means a Lua State, this *is* a Lua VM instance. 19 | **Path** means a file system path and file name. 20 | **KeyPath** means an Erlang list of **atoms** representing nested names, e.g. [table,pack] for table.pack. 21 | **Keys** means Lua table keys, the keys of a key-value structure. 22 | 23 | ## Functions 24 | **eval** and **do** functions differ only in what they return. The **do** functions return results and a new Lua State, the **eval** functions return a tuple starting on 'ok' or 'error', then the result, or cause of error. 25 | 26 | do --> {Result, State} 27 | 28 | eval --> {ok, Result} | {error, Reason} 29 | 30 | ### **`luerl:eval(String|Binary|Form, State) -> {ok, Result} | {error, Reason, StackTrace}.`** 31 | Evaluate a Lua expression passed in as a string or binary, and return its result. 32 | 33 | ### **`luerl:evalfile(Path, State) -> {ok, Result} | {error, Reason, StackTrace}.`** 34 | Load and execute a file, and return the result. 35 | 36 | ### **`luerl:do(String|Binary|Form, State) -> {Result, NewState}.`** 37 | Evaluate a Lua expression and return its result, and the new Lua State. 38 | 39 | ### **`luerl:dofile(Path, State) -> {Result, NewState}.`** 40 | Load and execute the Lua code in the file and return its result, and the new Lua State. Equivalent to doing luerl:do("return dofile('FileName')"). 41 | 42 | ### **`luerl:load(String|Binary[, CompileOptions], State) -> {ok,Function,NewState} | {error, Reason}.`** 43 | Parse a Lua chunk as string or binary, and return a compiled chunk ('form'). 44 | 45 | ### **`luerl:loadfile(FileName[, CompileOptions], State) -> {ok,Function,NewState} | {error, Reason}.`** 46 | Parse a Lua file, and return a compiled chunk ('form'). 47 | 48 | ### **`luerl:path_loadfile([Path, ], FileName[, CompileOptions], State) -> {ok,Function,FullName,State} | {error, Reason}.`** 49 | Search Path until the file FileName is found. Parse the file and return a compiled chunk ('form'). If Path is not given then the path defined in the environment variable LUA_LOAD_PATH is used. 50 | 51 | ### **`luerl:load_module(KeyPath, ErlangModule, State) -> State.`** 52 | Load `ErlangModule` and install its table at `KeyPath` which is encoded. 53 | 54 | ### **`luerl:load_module1(KeyPath, ErlangModule, State) -> State.`** 55 | Load `ErlangModule` and install its table at `KeyPath` which is **NOT** encoded 56 | 57 | ### **`luerl:init() -> State.`** 58 | Get a new Lua State = a fresh Lua VM instance. 59 | 60 | ### **`luerl:call(Form, Args, State) -> {Result,State}`** 61 | ### **`luerl:call_chunk(Form, Args, State) -> {Result,State}`** 62 | Call a compiled chunk or function. Use the call_chunk, call has been kept for backwards compatibility. 63 | 64 | ### **`luerl:call_function(KeyPath, Args, State) -> {Result,NewState}`** 65 | Call a function already defined in the state. `KeyPath` is a list of names to the function. `KeyPath`, `Args` and `Result` are automatically encoded/decoded. 66 | 67 | ### **`luerl:call_function1(KeyPath, Args, State) -> {Result,NewState}`** 68 | Call a function already defined in the state. `KeyPath` is a list of keys to the function. `KeyPath`, `Args` and `Result` are **NOT** encoded/decoded. 69 | 70 | ### **`luerl:call_method(MethPath, Args, State) -> {Result,NewState}.`** 71 | Call a method already defined in the state. `MethPath` is a list of names to the method. `MethPath`, `Args` and `Result` are automatically encoded/decoded. 72 | 73 | ### **`luerl:call_method1(MethPath, Args, State) -> {Result,NewState}`** 74 | Call a method already defined in the state. `MethPath` is a list of keys to the method. `Keys`, `Args` and `Result` are **NOT** encoded/decoded. 75 | 76 | ### **`luerl:stop(State) -> GCedState.`** 77 | Garbage collects the state and (todo:) does away with it. 78 | 79 | ### **`luerl:gc(State) -> State.`** 80 | Runs the garbage collector on a state and returns the new state. 81 | 82 | ### **`luerl:set_table(KeyPath, Value, State) -> State.`** 83 | Sets a value inside the Lua state. Value is automatically encoded. 84 | 85 | ### **`luerl:set_table1(KeyPath, Value, State) -> State.`** 86 | Sets a value inside the Lua state. `KeyPath` and `Value` are **NOT** encoded. 87 | 88 | ### **`luerl:get_table(KeyPath, State) -> {Result,State}.`** 89 | Gets a value inside the Lua state. `KeyPath` and `Result` are automatically encoded. 90 | 91 | ### **`luerl:get_table1(KeyPath, State) -> {Result,State}.`** 92 | Gets a value inside the Lua state. `KeyPath` and `Result` are **NOT** encoded/decoded. 93 | 94 | You can use this function to expose an function to the Lua code by using this interface: 95 | `fun(Args, State) -> {Results, State}` 96 | 97 | Args and Results must be a list of Luerl compatible Erlang values. 98 | -------------------------------------------------------------------------------- /doc_legacy/src/luerl_sandbox.3.md: -------------------------------------------------------------------------------- 1 | % luerl_sandbox(3) 2 | % Robert Virding 3 | % 2023 4 | 5 | # Name 6 | luerl_sandbox - Fuctions for sandboxing Luerl evaluation 7 | 8 | # Interface Functions 9 | The **Lua State** parameter is the state of a Lua VM instance. It must be created with the **luerl:init()** call and be carried from one call to the next. 10 | 11 | As it is possible in Lua to create self-referencing data structures, indeed the standard libraries have many instances of this, then using the functions which decode their return values will generate an error when they would cause an infinite loop during the decoding. An simple example is the top level table which contains a key **`_G`** which references the top-level table. 12 | 13 | Note that Lua **Chunks** (see definition below) can travel between different States. They are precompiled bits of code, independent of State. That you can 'carry around' this is no unique to Luerl but a low-level implementation detail of the standard Lua [language](https://lua.org), for more on chunks [read](https://www.lua.org/manual/5.3/manual.html#3.3.2) the official Lua 5.3 [reference manual](https://www.lua.org/manual/5.3/manual.html). 14 | 15 | ## Spec Definitions 16 | **Binary** means an Erlang binary string. 17 | **Chunks** means a portion of precompiled bytecode. 18 | **State** means a Lua State, this *is* a Lua VM instance. 19 | **Path** means a file system path and file name. 20 | **KeyPath** means an Erlang list of **atoms** representing nested names, e.g. [table,pack] for table.pack. 21 | **Keys** means Lua table keys, the keys of a key-value structure. 22 | 23 | ## Functions 24 | 25 | ### **` init() -> State.`** 26 | ### **` init([ State | TablePaths]) -> State.`** 27 | ### **` init(State, TablePaths) -> State.`** 28 | 29 | Create a new sandboxed state. If a state is given as an argument then that state will be used otherwise a new default be be generated. ``TablePaths`` is a list of paths to functions which will be blocked. If none is given then the default list will be used. 30 | 31 | ### **`run(String | Binary) -> {Result, State} | {error, Reason}.`** 32 | ### **`run(String | Binary, State) -> {Result, State} | {error, Reason}.`** 33 | ### **`run(String | Binary, Flags, State) -> {Result, State} | {error, Reason}.`** 34 | 35 | Spawn a new process which runs the string `String` in `State` where the default sandbox state will be used if none is given. `Flags` is a map or keyword list which can contain the following fields 36 | 37 | #{max_time => MaxTime, 38 | max_reductions => MaxReds, 39 | spawn_opts => SpawnOpts} 40 | 41 | `MaxReds` limits the number of reductions and `MaxTime` (default 100 msecs) the time to run the string, `SpawnOpts` are spawn options to the process running the evaluation. 42 | 43 | ### **`run(String | Binary) -> {Result, State} | {error, Reason}.`** 44 | ### **`run(String | Binary, State) -> {Result, State} | {error, Reason}.`** 45 | ### **`run(String | Binary, State, [ MaxReds | Flags ]) -> {Result, State} | {error, Reason}.`** 46 | ### **`run(String | Binary, State, MaxReds, Flags) -> {Result, State} | {error, Reason}.`** 47 | ### **`run(String | Binary, State, MaxReds, Flags, Timeout) -> {Result, State} | {error, Reason}.`** 48 | 49 | This is the old interface to run. It still works but the new interface is recommended. Spawn a new process which runs the string `String` in `State` where the default sandbox state will be used if none is given. `MaxReds` limits the number of reductions and `TimeOut` (default 100 msecs) the time to run the string, `Flags` are spawn options to the process running the evaluation. 50 | -------------------------------------------------------------------------------- /ebin/luerl.app: -------------------------------------------------------------------------------- 1 | {application,luerl, 2 | [{description,"Luerl - an implementation of Lua on Erlang"}, 3 | {vsn,"1.4.1"}, 4 | {modules,['Elixir.Luerl.New','Elixir.Luerl',luerl,luerl_anno, 5 | luerl_app,luerl_comp,luerl_comp_cg,luerl_comp_env, 6 | luerl_comp_lint,luerl_comp_locf,luerl_comp_normalise, 7 | luerl_comp_peep,luerl_comp_vars,luerl_emul,luerl_heap, 8 | luerl_lib,luerl_lib_basic,luerl_lib_bit32, 9 | luerl_lib_debug,luerl_lib_io,luerl_lib_math, 10 | luerl_lib_os,luerl_lib_os_date,luerl_lib_package, 11 | luerl_lib_string,luerl_lib_string_format, 12 | luerl_lib_table,luerl_lib_utf8,luerl_new,luerl_old, 13 | luerl_parse,luerl_sandbox,luerl_scan,luerl_sup, 14 | luerl_util,ttdict,ttsets]}, 15 | {registered,[]}, 16 | {applications,[kernel,stdlib]}, 17 | {env,[]}, 18 | {mod,{luerl_app,[]}}, 19 | {licenses,["Apache-2.0"]}, 20 | {links,[{"Github","https://github.com/rvirding/luerl"}]}, 21 | {files,["README.md","LICENSE","VERSION","src","doc", 22 | "ebin/luerl.app","include","rebar.*","*akefile", 23 | "*.escript"]}, 24 | {exclude_files,["priv/images/*"]}]}. 25 | -------------------------------------------------------------------------------- /examples/Makefile: -------------------------------------------------------------------------------- 1 | SUBDIRS ?= hello 2 | 3 | all clean: 4 | @for subdir in $(SUBDIRS); do \ 5 | echo $(MAKE) -C $$subdir $@; \ 6 | $(MAKE) -C $$subdir $@; \ 7 | done 8 | 9 | .PHONY: all clean 10 | -------------------------------------------------------------------------------- /examples/benchmark/Makefile: -------------------------------------------------------------------------------- 1 | EXAMPLES = benchmarks 2 | 3 | ROOTDIR = ../.. 4 | SRCDIR = $(ROOTDIR)/src 5 | BEAMDIR = $(ROOTDIR)/ebin 6 | 7 | all: $(EXAMPLES) 8 | 9 | clean: 10 | rm -f *.beam erl_crash.dump 11 | 12 | .SECONDARY: 13 | 14 | %.beam: %.erl $(SRCDIR)/*.hrl 15 | erlc -I $(SRCDIR) $< 16 | 17 | %: %.beam 18 | erl -pa $(BEAMDIR) -s $@ run -s init stop -noshell 19 | 20 | .PHONY: all clean 21 | -------------------------------------------------------------------------------- /examples/benchmark/benchmarks.erl: -------------------------------------------------------------------------------- 1 | %% File : benchmarks.erl 2 | %% Purpose : Benchmarks runner for luerl 3 | %% Use: erlc -I ../src benchmark.erl && erl -pa ../ebin -s benchmark run -s init stop -noshell 4 | %% Or: make 5 | 6 | -module(benchmarks). 7 | -export([run/0, run/1]). 8 | -export([benchmarks/1, do_benchmark/2, do_iteration/3]). 9 | 10 | -define(DEFAULT_ITER, 1000). 11 | 12 | run() -> 13 | Files = filelib:wildcard("suites/*.lua"), 14 | [ run(File) || File <- Files ] , 15 | ok. 16 | 17 | run(File) -> 18 | Lua0 = luerl:init(), 19 | {ok, _Resp, Lua2} = luerl:dofile(File, Lua0), 20 | report_file(File), 21 | [ do_benchmark(Benchmark, Lua2) || Benchmark <- benchmarks(Lua2) ], 22 | {ok, Lua2}. 23 | 24 | do_benchmark(Benchmark, Lua) -> 25 | Iter = num_iterations(Lua), 26 | report_benchmark(Benchmark), 27 | {Time, _Resp} = timer:tc(?MODULE, do_iteration, 28 | [Iter, Benchmark, Lua]), 29 | report_time(Time), 30 | ok. 31 | 32 | do_iteration(0, _Benchmark, _Lua) -> 33 | ok; 34 | do_iteration(Iter, Benchmark, Lua) -> 35 | luerl:call_method([<<"bench">>], Benchmark, [], Lua), 36 | do_iteration(Iter - 1, Benchmark, Lua). 37 | 38 | num_iterations(Lua) -> 39 | case luerl:do("return NUM_ITERATIONS", Lua) of 40 | {ok, [Iter], _} when is_number(Iter) -> round(Iter); 41 | _Any -> ?DEFAULT_ITER 42 | end. 43 | 44 | benchmarks(Lua0) -> 45 | io:format("benchmarks\n", []), 46 | {ok, [Benchmarks], _} = luerl:dofile_dec("util/extract_bench_keys.lua", Lua0), 47 | [ Key || {_Index, Key} <- Benchmarks ]. 48 | 49 | report_file(File) -> 50 | io:format("~n~s ms~n", [string:left(File, 26)]). 51 | 52 | report_benchmark(Benchmark) -> 53 | io:format(" ~s", 54 | [string:left(binary_to_list(Benchmark), 24, $.)]). 55 | 56 | report_time(Time) -> 57 | io:format(" ~p~n", [Time / 1000]). 58 | -------------------------------------------------------------------------------- /examples/benchmark/suites/COPYRIGHT: -------------------------------------------------------------------------------- 1 | Luamarca is licensed under the terms of the MIT license reproduced below. 2 | This means that Luamarca is free software and can be used for both academic 3 | and commercial purposes at absolutely no cost. 4 | 5 | =============================================================================== 6 | 7 | Copyright (C) 2008-2009 Luamarca authors 8 | 9 | Permission is hereby granted, free of charge, to any person obtaining a copy 10 | of this software and associated documentation files (the "Software"), to deal 11 | in the Software without restriction, including without limitation the rights 12 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | copies of the Software, and to permit persons to whom the Software is 14 | furnished to do so, subject to the following conditions: 15 | 16 | The above copyright notice and this permission notice shall be included in 17 | all copies or substantial portions of the Software. 18 | 19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | THE SOFTWARE. 26 | 27 | =============================================================================== 28 | 29 | (end of COPYRIGHT) 30 | -------------------------------------------------------------------------------- /examples/benchmark/suites/accum.lua: -------------------------------------------------------------------------------- 1 | local assert, loadstring = assert, loadstring 2 | local pairs, ipairs, next = pairs, ipairs, next 3 | local table_concat = table.concat 4 | 5 | -------------------------------------------------------------------------------- 6 | 7 | local DATA = 8 | { 9 | 006.635; 009.210; 011.345; 013.277; 015.086; 016.812; 018.475; 020.090; 10 | 021.666; 023.209; 024.725; 026.217; 027.688; 029.141; 030.578; 032.000; 11 | 033.409; 034.805; 036.191; 037.566; 038.932; 040.289; 041.638; 042.980; 12 | 044.314; 045.642; 046.963; 048.278; 049.588; 050.892; 052.191; 053.486; 13 | 054.776; 056.061; 057.342; 058.619; 059.893; 061.162; 062.428; 063.691; 14 | 064.950; 066.206; 067.459; 068.710; 069.957; 071.201; 072.443; 073.683; 15 | 074.919; 076.154; 077.386; 078.616; 079.843; 081.069; 082.292; 083.513; 16 | 084.733; 085.950; 087.166; 088.379; 089.591; 090.802; 092.010; 093.217; 17 | 094.422; 095.626; 096.828; 098.028; 099.228; 100.425; 101.621; 102.816; 18 | 104.010; 105.202; 106.393; 107.583; 108.771; 109.958; 111.144; 112.329; 19 | 113.512; 114.695; 115.876; 117.057; 118.236; 119.414; 120.591; 121.767; 20 | 122.942; 124.116; 125.289; 126.462; 127.633; 128.803; 129.973; 131.141; 21 | 132.309; 133.476; 134.642; 22 | } 23 | 24 | local DATA_SIZE = #DATA 25 | 26 | -------------------------------------------------------------------------------- 27 | 28 | local accum_unrolled 29 | do 30 | local buf = { "return function(t, c) c = c or 0; " } 31 | for i = 1, DATA_SIZE do 32 | buf[#buf + 1] = "c = c + t["..i.."]; " 33 | end 34 | buf[#buf + 1] = "return c; end" 35 | 36 | local fn = assert(loadstring(table_concat(buf))) 37 | 38 | accum_unrolled = assert(fn()) 39 | end 40 | 41 | local accum_numeric_for = function(t, c) 42 | c = c or 0 43 | for i = 1, #t do 44 | c = c + t[i] 45 | end 46 | return c 47 | end 48 | 49 | local accum_numeric_while = function(t, c) 50 | c = c or 0 51 | local i = 1 52 | local v = t[i] 53 | while v ~= nil do 54 | c = c + v 55 | i = i + 1 56 | v = t[i] 57 | end 58 | return c 59 | end 60 | 61 | local accum_ipairs = function(t, c) 62 | c = c or 0 63 | for _, v in ipairs(t) do 64 | c = c + v 65 | end 66 | return c 67 | end 68 | 69 | local accum_pairs = function(t, c) 70 | c = c or 0 71 | for _, v in pairs(t) do 72 | c = c + v 73 | end 74 | return c 75 | end 76 | 77 | local accum_next = function(t, c) 78 | c = c or 0 79 | local k, v = next(t) 80 | while k ~= nil do 81 | c = c + v 82 | k, v = next(t, k) 83 | end 84 | return c 85 | end 86 | 87 | -------------------------------------------------------------------------------- 88 | 89 | bench = { } 90 | 91 | bench.unrolled = function() 92 | return accum_unrolled(DATA) 93 | end 94 | 95 | bench.numeric_while = function() 96 | return accum_numeric_while(DATA) 97 | end 98 | 99 | bench.numeric_for = function() 100 | return accum_numeric_for(DATA) 101 | end 102 | 103 | bench.ipairs = function() 104 | return accum_ipairs(DATA) 105 | end 106 | 107 | bench.pairs = function() 108 | return accum_pairs(DATA) 109 | end 110 | 111 | bench.next = function() 112 | return accum_next(DATA) 113 | end 114 | 115 | return table.pack(bench) 116 | -------------------------------------------------------------------------------- /examples/benchmark/suites/callmap1.lua: -------------------------------------------------------------------------------- 1 | local noop = function() end 2 | local plain_call = function(a) noop() end 3 | local if_call = function(a) 4 | if a == "a1" then 5 | noop() 6 | elseif a == "a2" then 7 | noop() 8 | elseif a == "a3" then 9 | noop() 10 | elseif a == "a4" then 11 | noop() 12 | elseif a == "a5" then 13 | noop() 14 | elseif a == "a6" then 15 | noop() 16 | elseif a == "a7" then 17 | noop() 18 | elseif a == "a8" then 19 | noop() 20 | elseif a == "a9" then 21 | noop() 22 | elseif a == "a10" then 23 | noop() 24 | end 25 | end 26 | 27 | local map = { } 28 | for i = 1, 10 do 29 | map["a"..i] = noop 30 | end 31 | local map_call = function(a) map[a]() end 32 | 33 | local do_bench = function(fn) 34 | return function() 35 | fn("a"..1) 36 | end 37 | end 38 | 39 | bench = { 40 | noop = do_bench(noop); 41 | plain_call = do_bench(plain_call); 42 | if_call = do_bench(if_call); 43 | map_call = do_bench(map_call); 44 | } 45 | 46 | return bench 47 | -------------------------------------------------------------------------------- /examples/benchmark/suites/callmap10.lua: -------------------------------------------------------------------------------- 1 | local noop = function() end 2 | local plain_call = function(a) noop() end 3 | local if_call = function(a) 4 | if a == "a1" then 5 | noop() 6 | elseif a == "a2" then 7 | noop() 8 | elseif a == "a3" then 9 | noop() 10 | elseif a == "a4" then 11 | noop() 12 | elseif a == "a5" then 13 | noop() 14 | elseif a == "a6" then 15 | noop() 16 | elseif a == "a7" then 17 | noop() 18 | elseif a == "a8" then 19 | noop() 20 | elseif a == "a9" then 21 | noop() 22 | elseif a == "a10" then 23 | noop() 24 | end 25 | end 26 | 27 | local map = { } 28 | for i = 1, 10 do 29 | map["a"..i] = noop 30 | end 31 | local map_call = function(a) map[a]() end 32 | 33 | local do_bench = function(fn) 34 | return function() 35 | for i = 1, 10 do 36 | fn("a"..i) 37 | end 38 | end 39 | end 40 | 41 | bench = { 42 | noop = do_bench(noop); 43 | plain_call = do_bench(plain_call); 44 | if_call = do_bench(if_call); 45 | map_call = do_bench(map_call); 46 | } 47 | 48 | return bench 49 | -------------------------------------------------------------------------------- /examples/benchmark/suites/chaincall.lua: -------------------------------------------------------------------------------- 1 | bench = {} 2 | 3 | local function chain() 4 | return chain 5 | end 6 | 7 | local function plain() 8 | -- No-op 9 | end 10 | 11 | bench.chain_upval = function() 12 | chain () () () () () () () () () () -- 10 calls 13 | end 14 | 15 | bench.plain_upval = function() 16 | plain () 17 | plain () 18 | plain () 19 | plain () 20 | plain () 21 | plain () 22 | plain () 23 | plain () 24 | plain () 25 | plain () -- 10 calls 26 | end 27 | 28 | bench.plain_chain_upval = function() 29 | chain () 30 | chain () 31 | chain () 32 | chain () 33 | chain () 34 | chain () 35 | chain () 36 | chain () 37 | chain () 38 | chain () -- 10 calls 39 | end 40 | 41 | bench.chain_local = function() 42 | local chain = chain 43 | chain () () () () () () () () () () -- 10 calls 44 | end 45 | 46 | bench.plain_local = function() 47 | local plain = plain 48 | plain () 49 | plain () 50 | plain () 51 | plain () 52 | plain () 53 | plain () 54 | plain () 55 | plain () 56 | plain () 57 | plain () -- 10 calls 58 | end 59 | 60 | bench.plain_chain_local = function() 61 | local chain = chain 62 | chain () 63 | chain () 64 | chain () 65 | chain () 66 | chain () 67 | chain () 68 | chain () 69 | chain () 70 | chain () 71 | chain () -- 10 calls 72 | end 73 | 74 | return bench 75 | -------------------------------------------------------------------------------- /examples/benchmark/suites/concat.lua: -------------------------------------------------------------------------------- 1 | local table_concat, table_insert = table.concat, table.insert 2 | 3 | NUM_ITERATIONS = 1 4 | 5 | bench = {} 6 | 7 | bench.raw_concat = function() 8 | local self = "string 0\n" 9 | for i = 1, 1000 do 10 | self = self .. "string " .. i .. "\n" 11 | end 12 | return self 13 | end 14 | 15 | bench.raw_plus_1 = function() 16 | local self = { "string 0\n" } 17 | for i = 1, 1000 do 18 | self[#self + 1] = "string " 19 | self[#self + 1] = i 20 | self[#self + 1] = "\n" 21 | end 22 | return table_concat(self) 23 | end 24 | 25 | bench.raw_insert = function() 26 | local self = { "string 0\n" } 27 | for i = 1, 1000 do 28 | table_insert(self, "string ") 29 | table_insert(self, i ) 30 | table_insert(self, "\n" ) 31 | end 32 | return table_concat(self) 33 | end 34 | 35 | bench.mixed_plus_1 = function() 36 | local self = {"string 0\n"} 37 | for i = 1,1000 do 38 | self[#self + 1] = "string " .. i .. "\n" 39 | end 40 | return table_concat(self) 41 | end 42 | 43 | bench.mixed_insert = function() 44 | local self = {"string 0\n"} 45 | for i = 1, 1000 do 46 | table_insert(self, "string " .. i .. "\n") 47 | end 48 | return table_concat(self) 49 | end 50 | 51 | return bench 52 | -------------------------------------------------------------------------------- /examples/benchmark/suites/elseif_large.lua: -------------------------------------------------------------------------------- 1 | -- TODO: ?! Is this benchmark still relevant? 2 | 3 | local tostring, assert, loadstring, ipairs 4 | = tostring, assert, loadstring, ipairs 5 | 6 | local table_concat = table.concat 7 | local math_floor = math.floor 8 | 9 | local noop = function() end 10 | 11 | local plain_call = function(a) noop() end 12 | 13 | local make_plain_call = function() 14 | return plain_call 15 | end 16 | 17 | local make_elseifs = function(n) 18 | local buf = { } 19 | local _ = function(v) buf[#buf + 1] = tostring(v) end 20 | _ "local noop = function() end " 21 | _ "return function(a)" 22 | _ " if a == 'a" _(1) _"' then noop()" 23 | for i = 2, n do 24 | _ " elseif a == 'a" _(i) _"' then noop()" 25 | end 26 | _ " else error('unknown param') end" 27 | _ " end" 28 | return assert(loadstring(table_concat(buf)))() 29 | end 30 | 31 | local make_callmaps = function(n) 32 | local buf = { } 33 | local _ = function(v) buf[#buf + 1] = tostring(v) end 34 | _ "local noop = function() end " 35 | _ "local map = {" 36 | for i = 1, n do 37 | _ "a" _(i) _ " = noop;" 38 | end 39 | _ "} " 40 | _ "return function(a) assert(map[a])() end" 41 | return assert(loadstring(table_concat(buf)))() 42 | end 43 | 44 | local bench_fn = function(fn, n) 45 | return function() 46 | fn("a" .. math_floor(n / 2 + 0.5)) 47 | end 48 | end 49 | 50 | local mark_fn = function(make, n) 51 | return bench_fn(make(n), n) 52 | end 53 | 54 | bench = { } 55 | 56 | for _, i in ipairs { 1, 5, 10, 15, 20, 100, 250, 500, 1000 } do 57 | bench["plain_"..i] = mark_fn(make_plain_call, i) 58 | bench["callmap_"..i] = mark_fn(make_callmaps, i) 59 | bench["elseif_"..i] = mark_fn(make_elseifs, i) 60 | end 61 | 62 | return bench 63 | -------------------------------------------------------------------------------- /examples/benchmark/suites/factory.lua: -------------------------------------------------------------------------------- 1 | local pairs, setmetatable = pairs, setmetatable 2 | 3 | local clone_table = function(t) 4 | local r = {} 5 | for k, v in pairs(t) do 6 | r[k] = v 7 | end 8 | return r 9 | end 10 | 11 | local inplace = function() 12 | local factory 13 | do 14 | factory = function() 15 | return 16 | { 17 | method1 = function(self) end; 18 | method2 = function(self) end; 19 | method3 = function(self) end; 20 | method4 = function(self) end; 21 | method5 = function(self) end; 22 | method6 = function(self) end; 23 | method7 = function(self) end; 24 | method8 = function(self) end; 25 | method9 = function(self) end; 26 | method10 = function(self) end; 27 | } 28 | end 29 | end 30 | return factory 31 | end 32 | 33 | local plain = function() 34 | local factory 35 | do 36 | local method1 = function(self) end 37 | local method2 = function(self) end 38 | local method3 = function(self) end 39 | local method4 = function(self) end 40 | local method5 = function(self) end 41 | local method6 = function(self) end 42 | local method7 = function(self) end 43 | local method8 = function(self) end 44 | local method9 = function(self) end 45 | local method10 = function(self) end 46 | factory = function() 47 | return 48 | { 49 | method1 = method1; 50 | method2 = method2; 51 | method3 = method3; 52 | method4 = method4; 53 | method5 = method5; 54 | method6 = method6; 55 | method7 = method7; 56 | method8 = method8; 57 | method9 = method9; 58 | method10 = method10; 59 | } 60 | end 61 | end 62 | return factory 63 | end 64 | 65 | local mt = function() 66 | local factory 67 | do 68 | local mt = 69 | { 70 | __index = 71 | { 72 | method1 = function(self) end; 73 | method2 = function(self) end; 74 | method3 = function(self) end; 75 | method4 = function(self) end; 76 | method5 = function(self) end; 77 | method6 = function(self) end; 78 | method7 = function(self) end; 79 | method8 = function(self) end; 80 | method9 = function(self) end; 81 | method10 = function(self) end; 82 | }; 83 | } 84 | factory = function() 85 | return setmetatable({}, mt) 86 | end 87 | end 88 | return factory 89 | end 90 | 91 | local clone = function() 92 | local factory 93 | do 94 | local proto = 95 | { 96 | method1 = function(self) end; 97 | method2 = function(self) end; 98 | method3 = function(self) end; 99 | method4 = function(self) end; 100 | method5 = function(self) end; 101 | method6 = function(self) end; 102 | method7 = function(self) end; 103 | method8 = function(self) end; 104 | method9 = function(self) end; 105 | method10 = function(self) end; 106 | } 107 | factory = function() 108 | return clone_table(proto) 109 | end 110 | end 111 | return factory 112 | end 113 | 114 | local invoker = function(factory) 115 | local obj = factory() 116 | return function() 117 | obj:method1() 118 | obj:method2() 119 | obj:method3() 120 | obj:method4() 121 | obj:method5() 122 | obj:method6() 123 | obj:method7() 124 | obj:method8() 125 | obj:method9() 126 | obj:method10() 127 | end 128 | end 129 | 130 | bench = 131 | { 132 | inplace_init = inplace; 133 | plain_init = plain; 134 | metatable_init = mt; 135 | clone_init = clone; 136 | 137 | inplace_call = inplace(); 138 | plain_call = plain(); 139 | metatable_call = mt(); 140 | clone_call = clone(); 141 | 142 | inplace_method = invoker(inplace()); 143 | plain_method = invoker(plain()); 144 | metatable_method = invoker(mt()); 145 | clone_method = invoker(clone()); 146 | } 147 | 148 | return bench 149 | -------------------------------------------------------------------------------- /examples/benchmark/suites/get.lua: -------------------------------------------------------------------------------- 1 | local rawget = rawget 2 | 3 | local t = { true, a = true } 4 | 5 | local get = function(t, k) 6 | return t[k] 7 | end 8 | 9 | bench = { } 10 | 11 | bench.nonnil_num_plain = function() 12 | return t[1] 13 | end 14 | 15 | bench.nonnil_num_get = function() 16 | return get(t, 1) 17 | end 18 | 19 | bench.nonnil_num_rawget = function() 20 | return rawget(t, 1) 21 | end 22 | 23 | bench.nonnil_str_plain = function() 24 | return t["a"] 25 | end 26 | 27 | bench.nonnil_str_sugar = function() 28 | return t.a 29 | end 30 | 31 | bench.nonnil_str_get = function() 32 | return get(t, "a") 33 | end 34 | 35 | bench.nonnil_str_rawget = function() 36 | return rawget(t, "a") 37 | end 38 | 39 | bench.nil_num_plain = function() 40 | return t[2] 41 | end 42 | 43 | bench.nil_num_get = function() 44 | return get(t, 2) 45 | end 46 | 47 | bench.nil_num_rawget = function() 48 | return rawget(t, 2) 49 | end 50 | 51 | bench.nil_str_plain = function() 52 | return t["b"] 53 | end 54 | 55 | bench.nil_str_sugar = function() 56 | return t.b 57 | end 58 | 59 | bench.nil_str_get = function() 60 | return get(t, "b") 61 | end 62 | 63 | bench.nil_str_rawget = function() 64 | return rawget(t, "b") 65 | end 66 | 67 | return bench 68 | -------------------------------------------------------------------------------- /examples/benchmark/suites/inf.lua: -------------------------------------------------------------------------------- 1 | local tonumber = tonumber 2 | local math_huge = math.huge 3 | 4 | bench = {} 5 | 6 | -- bench.e309 = function() 7 | -- local inf = 1e309 8 | -- end 9 | 10 | bench.huge = function() 11 | local inf = math_huge 12 | end 13 | 14 | -- bench.divide = function() 15 | -- local inf = 1/0 16 | -- end 17 | 18 | -- bench.tonumber = function() 19 | -- local inf = tonumber("inf") 20 | -- end 21 | 22 | return bench 23 | -------------------------------------------------------------------------------- /examples/benchmark/suites/is_integer.lua: -------------------------------------------------------------------------------- 1 | local math_floor = math.floor 2 | 3 | bench = {} 4 | 5 | local integer, noninteger = 12345, 12345.67 6 | 7 | local noop = function(a) 8 | return a 9 | end 10 | 11 | local isint_floor = function(a) 12 | if a == math_floor(a) then 13 | return true 14 | end 15 | return false 16 | end 17 | 18 | local isint_floor_direct = function(a) 19 | return (a == math_floor(a)) 20 | end 21 | 22 | local isint_mod = function(a) 23 | if a % 1 == 0 then 24 | return true 25 | end 26 | return false 27 | end 28 | 29 | local isint_mod_direct = function(a) 30 | return (a % 1 == 0) 31 | end 32 | 33 | local isint_bits = function(a) 34 | if (a + 2^52) - 2^52 == a then 35 | return true 36 | end 37 | return false 38 | end 39 | 40 | local isint_bits_direct = function(a) 41 | return (a + 2^52) - 2^52 == a 42 | end 43 | 44 | bench.noop_int = function() 45 | noop(integer) 46 | end 47 | 48 | bench.noop_nonint = function() 49 | noop(noninteger) 50 | end 51 | 52 | bench.floor_int = function() 53 | isint_floor(integer) 54 | end 55 | 56 | bench.floor_nonint = function() 57 | isint_floor(noninteger) 58 | end 59 | 60 | bench.floor_int_direct = function() 61 | isint_floor_direct(integer) 62 | end 63 | 64 | bench.floor_nonint_direct = function() 65 | isint_floor_direct(noninteger) 66 | end 67 | 68 | bench.mod_int = function() 69 | isint_mod(integer) 70 | end 71 | 72 | bench.mod_nonint = function() 73 | isint_mod(noninteger) 74 | end 75 | 76 | bench.mod_int_direct = function() 77 | isint_mod_direct(integer) 78 | end 79 | 80 | bench.mod_nonint_direct = function() 81 | isint_mod_direct(noninteger) 82 | end 83 | 84 | bench.bits_int = function() 85 | isint_bits(integer) 86 | end 87 | 88 | bench.bits_nonint = function() 89 | isint_bits(noninteger) 90 | end 91 | 92 | bench.bits_int_direct = function() 93 | isint_bits_direct(integer) 94 | end 95 | 96 | bench.bits_nonint_direct = function() 97 | isint_bits_direct(noninteger) 98 | end 99 | 100 | return bench 101 | -------------------------------------------------------------------------------- /examples/benchmark/suites/mtvsclosure.lua: -------------------------------------------------------------------------------- 1 | local setmetatable = setmetatable 2 | 3 | local mt = 4 | { 5 | __call = function(t, v) 6 | t[#t + 1] = v 7 | end 8 | } 9 | 10 | local call_setmetatable = function() 11 | return setmetatable({ }, mt) 12 | end 13 | 14 | local create_closure = function() 15 | local t = {} 16 | return function(v) t[#t + 1] = v end 17 | end 18 | 19 | local mt_obj = call_setmetatable() 20 | local fn_obj = create_closure() 21 | 22 | bench = { } 23 | 24 | bench.call_setmetatable = call_setmetatable 25 | 26 | bench.create_closure = create_closure 27 | 28 | -- bench.use_setmetatable = function() 29 | -- mt_obj("boo!") 30 | -- end 31 | 32 | bench.use_closure = function() 33 | fn_obj("boo!") 34 | end 35 | 36 | return bench 37 | -------------------------------------------------------------------------------- /examples/benchmark/suites/next_vs_pairs.lua: -------------------------------------------------------------------------------- 1 | local pairs, next = pairs, next 2 | 3 | local t = {} 4 | for i = 1, 50 do 5 | t[i] = i -- Array part 6 | t[i * 256] = i -- Hash part 7 | end 8 | 9 | bench = {} 10 | 11 | bench.pairs = function() 12 | local sum = 0 13 | for k, v in pairs(t) do 14 | sum = sum + v 15 | end 16 | end 17 | 18 | bench.next = function() 19 | local sum = 0 20 | local k, v = next(t) 21 | while k ~= nil do 22 | sum = sum + v 23 | k, v = next(t, k) 24 | end 25 | end 26 | 27 | return bench 28 | -------------------------------------------------------------------------------- /examples/benchmark/suites/nloop.lua: -------------------------------------------------------------------------------- 1 | local ipairs = ipairs 2 | 3 | local t = {} 4 | for i = 1, 55 do 5 | t[i] = i 6 | end 7 | t[51] = nil -- A hole 8 | 9 | bench = {} 10 | 11 | -- Bench function does: 12 | -- 1. Set all table elements to 0 until the first hole. 13 | -- 2. Return hole position. 14 | 15 | bench.loop_ipairs = function() 16 | local j 17 | for i, v in ipairs(t) do 18 | t[i] = 0 19 | j = i 20 | end 21 | return j + 1 22 | end 23 | 24 | bench.loop_for = function() 25 | local n = #t 26 | local j = n 27 | for i = 1, n do 28 | local v = t[i] 29 | if v == nil then 30 | j = i 31 | break 32 | end 33 | t[i] = 0 34 | end 35 | return j 36 | end 37 | 38 | bench.loop_while = function() 39 | local i = 1 40 | while t[i] ~= nil do 41 | t[i] = 0 42 | i = i + 1 43 | end 44 | return i 45 | end 46 | 47 | return bench 48 | -------------------------------------------------------------------------------- /examples/benchmark/suites/nloop_simple.lua: -------------------------------------------------------------------------------- 1 | local ipairs = ipairs 2 | 3 | local t5, t25, t50 = {}, {}, {} 4 | 5 | do 6 | for i = 1, 10 do 7 | t5[i] = i 8 | end 9 | t5[6] = nil 10 | 11 | for i = 1, 30 do 12 | t25[i] = i 13 | end 14 | t25[26] = nil 15 | 16 | for i = 1, 55 do 17 | t50[i] = i 18 | end 19 | t50[51] = nil 20 | end 21 | 22 | local do_nothing = function() 23 | end 24 | 25 | bench = {} 26 | 27 | local do_loop_ipairs = function(t) 28 | for i, v in ipairs(t) do 29 | end 30 | end 31 | 32 | local do_loop_numfor = function(t) 33 | for i = 1, #t do 34 | if t[i] == nil then 35 | break 36 | end 37 | end 38 | end 39 | 40 | local do_loop_while = function(t) 41 | local i = 1 42 | while t[i] ~= nil do 43 | i = i + 1 44 | end 45 | end 46 | 47 | bench.loop_ipairs_50 = function() 48 | do_loop_ipairs(t50) 49 | do_nothing(t50) -- Padding to get equivalent number of function calls. 50 | do_nothing(t50) 51 | do_nothing(t50) 52 | do_nothing(t50) 53 | do_nothing(t50) 54 | do_nothing(t50) 55 | do_nothing(t50) 56 | do_nothing(t50) 57 | do_nothing(t50) 58 | end 59 | 60 | bench.loop_numfor_50 = function() 61 | do_loop_numfor(t50) 62 | do_nothing(t50) 63 | do_nothing(t50) 64 | do_nothing(t50) 65 | do_nothing(t50) 66 | do_nothing(t50) 67 | do_nothing(t50) 68 | do_nothing(t50) 69 | do_nothing(t50) 70 | do_nothing(t50) 71 | end 72 | 73 | bench.loop_while_50 = function() 74 | do_loop_while(t50) 75 | do_nothing(t50) 76 | do_nothing(t50) 77 | do_nothing(t50) 78 | do_nothing(t50) 79 | do_nothing(t50) 80 | do_nothing(t50) 81 | do_nothing(t50) 82 | do_nothing(t50) 83 | do_nothing(t50) 84 | end 85 | 86 | bench.loop_ipairs_25 = function() 87 | do_loop_ipairs(t25) 88 | do_loop_ipairs(t25) 89 | do_nothing(t25) 90 | do_nothing(t25) 91 | do_nothing(t25) 92 | do_nothing(t25) 93 | do_nothing(t25) 94 | do_nothing(t25) 95 | do_nothing(t25) 96 | do_nothing(t25) 97 | end 98 | 99 | bench.loop_numfor_25 = function() 100 | do_loop_numfor(t25) 101 | do_loop_numfor(t25) 102 | do_nothing(t25) 103 | do_nothing(t25) 104 | do_nothing(t25) 105 | do_nothing(t25) 106 | do_nothing(t25) 107 | do_nothing(t25) 108 | do_nothing(t25) 109 | do_nothing(t25) 110 | end 111 | 112 | bench.loop_while_25 = function() 113 | do_loop_while(t25) 114 | do_loop_while(t25) 115 | do_nothing(t25) 116 | do_nothing(t25) 117 | do_nothing(t25) 118 | do_nothing(t25) 119 | do_nothing(t25) 120 | do_nothing(t25) 121 | do_nothing(t25) 122 | do_nothing(t25) 123 | end 124 | 125 | bench.loop_ipairs_5 = function() 126 | do_loop_ipairs(t5) 127 | do_loop_ipairs(t5) 128 | do_loop_ipairs(t5) 129 | do_loop_ipairs(t5) 130 | do_loop_ipairs(t5) 131 | do_loop_ipairs(t5) 132 | do_loop_ipairs(t5) 133 | do_loop_ipairs(t5) 134 | do_loop_ipairs(t5) 135 | do_loop_ipairs(t5) 136 | end 137 | 138 | bench.loop_numfor_5 = function() 139 | do_loop_numfor(t5) 140 | do_loop_numfor(t5) 141 | do_loop_numfor(t5) 142 | do_loop_numfor(t5) 143 | do_loop_numfor(t5) 144 | do_loop_numfor(t5) 145 | do_loop_numfor(t5) 146 | do_loop_numfor(t5) 147 | do_loop_numfor(t5) 148 | do_loop_numfor(t5) 149 | end 150 | 151 | bench.loop_while_5 = function() 152 | do_loop_while(t5) 153 | do_loop_while(t5) 154 | do_loop_while(t5) 155 | do_loop_while(t5) 156 | do_loop_while(t5) 157 | do_loop_while(t5) 158 | do_loop_while(t5) 159 | do_loop_while(t5) 160 | do_loop_while(t5) 161 | do_loop_while(t5) 162 | end 163 | 164 | return bench 165 | -------------------------------------------------------------------------------- /examples/benchmark/suites/return.lua: -------------------------------------------------------------------------------- 1 | bench = {} 2 | 3 | local function no_ret() 4 | end 5 | 6 | local function ret_nil() 7 | return nil 8 | end 9 | 10 | local function ret_true() 11 | return true 12 | end 13 | 14 | local function ret_self() 15 | return ret_self 16 | end 17 | 18 | bench.no_ret = function() 19 | local a = no_ret() 20 | end 21 | 22 | bench.ret_nil = function() 23 | local a = ret_nil() 24 | end 25 | 26 | bench.ret_true = function() 27 | local a = ret_true() 28 | end 29 | 30 | bench.ret_self = function() 31 | local a = ret_self() 32 | end 33 | 34 | return bench 35 | -------------------------------------------------------------------------------- /examples/benchmark/suites/selectvstable.lua: -------------------------------------------------------------------------------- 1 | local select = select 2 | 3 | local select_test = function(...) 4 | local nargs = select("#", ...) 5 | local r = { } 6 | for i = 1, nargs do 7 | r[#r + 1] = select(i, ...) * 2 8 | end 9 | return r 10 | end 11 | 12 | local table_test = function(...) 13 | local nargs = select("#", ...) -- Still have to do this in case of nils 14 | local args = { ... } 15 | local r = { } 16 | for i = 1, nargs do 17 | r[#r + 1] = args[i] * 2 18 | end 19 | return r 20 | end 21 | 22 | bench = { } 23 | 24 | bench.select = function() 25 | return select_test(3, 5, 1, 9, 7) 26 | end 27 | 28 | bench.table = function() 29 | return table_test(3, 5, 1, 9, 7) 30 | end 31 | 32 | return bench 33 | -------------------------------------------------------------------------------- /examples/benchmark/suites/sort-simple.lua: -------------------------------------------------------------------------------- 1 | local table_sort = table.sort 2 | local math_random, math_randomseed = math.random, math.randomseed 3 | 4 | -------------------------------------------------------------------------------- 5 | 6 | math_randomseed(12345) 7 | 8 | -------------------------------------------------------------------------------- 9 | 10 | -- TODO: Benchmark some pure-lua qsort 11 | 12 | local DATA_SIZE = 1e1 13 | 14 | local generate_data = function() 15 | local t = { } 16 | for i = 1, DATA_SIZE do 17 | t[i] = math_random() 18 | end 19 | return t 20 | end 21 | 22 | local less = function(lhs, rhs) 23 | return lhs < rhs 24 | end 25 | 26 | local bubble_sort = function(t) 27 | for i = 2, #t do 28 | local switched = false 29 | for j = #t, i, -1 do 30 | if t[j] < t[j - 1] then 31 | t[j], t[j - 1] = t[j - 1], t[j] 32 | switched = true 33 | end 34 | end 35 | if switched == false then 36 | return t 37 | end 38 | end 39 | return t 40 | end 41 | 42 | local bubble_sort_cb = function(t, less) 43 | for i = 2, #t do 44 | local switched = false 45 | for j = #t, i, -1 do 46 | if less(t[j], t[j - 1]) then 47 | t[j], t[j - 1] = t[j - 1], t[j] 48 | switched = true 49 | end 50 | end 51 | if switched == false then 52 | return t 53 | end 54 | end 55 | return t 56 | end 57 | 58 | -------------------------------------------------------------------------------- 59 | 60 | bench = { } 61 | 62 | bench.generate_only = function() 63 | local data = generate_data() 64 | return true 65 | end 66 | 67 | bench.tsort_nocallback = function() 68 | local data = generate_data() 69 | return table_sort(data) 70 | end 71 | 72 | bench.tsort_callback = function() 73 | local data = generate_data() 74 | return table_sort(data, less) 75 | end 76 | 77 | bench.bubble_nocallback = function() 78 | local data = generate_data() 79 | return bubble_sort(data) 80 | end 81 | 82 | bench.bubble_callback = function() 83 | local data = generate_data() 84 | return bubble_sort_cb(data, less) 85 | end 86 | 87 | return bench 88 | -------------------------------------------------------------------------------- /examples/benchmark/suites/str_is_empty.lua: -------------------------------------------------------------------------------- 1 | local empty_string = "" 2 | 3 | bench = { } 4 | 5 | bench.noop = function() 6 | local a = "" 7 | return true 8 | end 9 | 10 | bench.empty_constant = function() 11 | local a = "" 12 | return a == "" 13 | end 14 | 15 | bench.empty_upvalue = function() 16 | local a = "" 17 | return a == empty_string 18 | end 19 | 20 | bench.empty_size = function() 21 | local a = "" 22 | return #a == 0 23 | end 24 | 25 | bench.nonempty_constant = function() 26 | local a = "nonempty" 27 | return a == "" 28 | end 29 | 30 | bench.nonempty_upvalue = function() 31 | local a = "nonempty" 32 | return a == empty_string 33 | end 34 | 35 | bench.nonempty_size = function() 36 | local a = "nonempty" 37 | return #a == 0 38 | end 39 | 40 | return bench 41 | -------------------------------------------------------------------------------- /examples/benchmark/suites/tailcall.lua: -------------------------------------------------------------------------------- 1 | local ret = function(t) 2 | t[1] = true 3 | return t 4 | end 5 | 6 | local noret = function(t) 7 | t[1] = true 8 | end 9 | 10 | bench = { } 11 | 12 | bench.tailcall_local = function() 13 | local t = {} 14 | return ret(t) 15 | end 16 | 17 | bench.tailcall_nolocal = function() 18 | return ret({}) 19 | end 20 | 21 | bench.notailcall_return = function() 22 | local t = {} 23 | ret(t) 24 | return t 25 | end 26 | 27 | bench.notailcall_noreturn = function() 28 | local t = {} 29 | noret(t) 30 | return t 31 | end 32 | 33 | return table.pack(bench) 34 | -------------------------------------------------------------------------------- /examples/benchmark/suites/vararg.lua: -------------------------------------------------------------------------------- 1 | local noop = function() end 2 | local vararg_callback = function(...) end 3 | 4 | local call_noargs = function(fn) fn() end 5 | local call_vararg = function(fn, ...) fn(...) end 6 | local call_3 = function(fn, a, b, c) fn(a, b, c) end 7 | 8 | NUM_ITER = 1000 9 | 10 | bench = { } 11 | 12 | bench.noop = noop 13 | 14 | bench.vararg_callback = vararg_callback 15 | 16 | bench.call_noargs_noop_nil = function() 17 | call_noargs(noop) 18 | end 19 | 20 | bench.call_noargs_vararg_nil = function() 21 | call_noargs(vararg_callback) 22 | end 23 | 24 | bench.call_vararg_noop_nil = function() 25 | call_noargs(noop) 26 | end 27 | 28 | bench.call_vararg_vararg_nil = function() 29 | call_noargs(vararg_callback) 30 | end 31 | 32 | bench.call_3_noop_nil = function() 33 | call_3(noop) 34 | end 35 | 36 | bench.call_3_vararg_nil = function() 37 | call_3(vararg_callback) 38 | end 39 | 40 | bench.call_noargs_noop_3 = function() 41 | call_noargs(noop, 1, 2, 3) 42 | end 43 | 44 | bench.call_noargs_vararg_3 = function() 45 | call_noargs(vararg_callback, 1, 2, 3) 46 | end 47 | 48 | bench.call_vararg_noop_3 = function() 49 | call_noargs(noop, 1, 2, 3) 50 | end 51 | 52 | bench.call_vararg_vararg_3 = function() 53 | call_noargs(vararg_callback, 1, 2, 3) 54 | end 55 | 56 | bench.call_3_noop_3 = function() 57 | call_3(noop, 1, 2, 3) 58 | end 59 | 60 | bench.call_3_vararg_3 = function() 61 | call_3(vararg_callback, 1, 2, 3) 62 | end 63 | 64 | return bench 65 | -------------------------------------------------------------------------------- /examples/benchmark/util/extract_bench_keys.lua: -------------------------------------------------------------------------------- 1 | local list = {} 2 | for key, func in pairs(bench) do 3 | list[#list+1] = key 4 | end 5 | return list 6 | -------------------------------------------------------------------------------- /examples/euler/Makefile: -------------------------------------------------------------------------------- 1 | EXAMPLES = euler 2 | 3 | ROOTDIR = ../.. 4 | SRCDIR = $(ROOTDIR)/src 5 | BEAMDIR = $(ROOTDIR)/ebin 6 | 7 | all: $(EXAMPLES) 8 | 9 | clean: 10 | rm -f *.beam erl_crash.dump 11 | 12 | .SECONDARY: 13 | 14 | %.beam: %.erl $(SRCDIR)/*.hrl 15 | erlc -I $(SRCDIR) $< 16 | 17 | %: %.beam 18 | erl -pa $(BEAMDIR) -s $@ run -s init stop -noshell 19 | 20 | .PHONY: all clean 21 | -------------------------------------------------------------------------------- /examples/euler/euler.erl: -------------------------------------------------------------------------------- 1 | %% File : euler.erl 2 | %% Purpose : Running complex lua with luerl 3 | %% Use: erlc -I ../../src euler.erl && erl -pa ../../ebin -s euler run -s init stop -noshell 4 | %% Or: make 5 | 6 | -module(euler). 7 | -export([run/0, run/2]). 8 | 9 | run() -> 10 | run("./problem_001.lua", 233168), 11 | run("./problem_002.lua", 4613732), 12 | run("./problem_003.lua", 29), 13 | run("./problem_004.lua", 36863), 14 | run("./problem_005.lua", 232792560), 15 | run("./problem_006.lua", 25164150), 16 | run("./problem_007.lua", 617), 17 | run("./problem_008.lua", 32), 18 | run("./problem_009.lua", 31875000), 19 | run("./problem_010.lua", 277050.0), 20 | ok. 21 | 22 | run(File, Solution) -> 23 | Lua0 = luerl:init(), 24 | {ok, Form, Lua1} = luerl:loadfile(File, Lua0), 25 | case timer:tc(luerl, call_chunk, [Form, [], Lua1]) of 26 | {T, {ok, [Return], _}} when Return == Solution -> 27 | io:format("~s (returned ~p in ~pus)~n", [File, Return, T]); 28 | {T, {ok, [Return], _}} -> 29 | io:format("~s (expected ~p but got ~p in ~pus)~n", 30 | [File, Solution, Return, T]); 31 | {_, {lua_error, Error, State}} -> 32 | io:format("luerl error: ~p~n", [{Error,State}]); 33 | {_, {error, Error, State}} -> 34 | io:format("Error: ~p~n", [{Error,State}]) 35 | end. 36 | -------------------------------------------------------------------------------- /examples/euler/problem_001.lua: -------------------------------------------------------------------------------- 1 | -- If we list all the natural numbers below 10 that are multiples of 3 or 5, we get 3, 5, 6 and 9. The sum of these multiples is 23. 2 | -- Find the sum of all the multiples of 3 or 5 below 1000. 3 | 4 | LIMIT = 1000 5 | 6 | sum = 0 7 | for ii = 1, (LIMIT-1) do 8 | if (ii%3 == 0) or (ii%5 == 0) then 9 | sum = sum + ii 10 | end 11 | end 12 | 13 | print(sum) 14 | return sum 15 | -------------------------------------------------------------------------------- /examples/euler/problem_002.lua: -------------------------------------------------------------------------------- 1 | -- Each new term in the Fibonacci sequence is generated by adding the previous two terms. 2 | -- By starting with 1 and 2, the first 10 terms will be: 3 | 4 | -- 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, ... 5 | 6 | -- By considering the terms in the Fibonacci sequence whose values do not exceed four million, 7 | -- find the sum of the even-valued terms. 8 | 9 | LIMIT = 4000000 10 | 11 | a, b, sum = 1, 2, 0 12 | while b <= LIMIT do 13 | if b % 2 == 0 then 14 | sum = sum + b 15 | end 16 | a, b = b, a + b 17 | end 18 | 19 | print(sum) 20 | return sum 21 | 22 | -------------------------------------------------------------------------------- /examples/euler/problem_003.lua: -------------------------------------------------------------------------------- 1 | -- The prime factors of 13195 are 5, 7, 13 and 29. 2 | -- What is the largest prime factor of the number 600851475143 ? 3 | 4 | -- NUMBER_TO_FACTOR = 600851475143 5 | NUMBER_TO_FACTOR = 13195 6 | 7 | limit = math.sqrt(NUMBER_TO_FACTOR) 8 | 9 | primes = { 3 } 10 | 11 | function divisible_by_any(val, array) 12 | for ii, num in ipairs(array) do 13 | if (val % num) == 0 then 14 | return true 15 | end 16 | end 17 | return false 18 | end 19 | 20 | for ii = 5, limit, 2 do 21 | if not divisible_by_any(ii, primes) then 22 | table.insert(primes, ii) 23 | if NUMBER_TO_FACTOR % ii == 0 then 24 | factor = ii 25 | end 26 | end 27 | end 28 | print(factor) 29 | return factor 30 | 31 | 32 | -------------------------------------------------------------------------------- /examples/euler/problem_004.lua: -------------------------------------------------------------------------------- 1 | -- A palindromic number reads the same both ways. The largest palindrome made from the product of two 2-digit numbers is 9009 = 91 99. 2 | -- Find the largest palindrome made from the product of two 3-digit numbers. 3 | 4 | function is_palindrome(number) 5 | local str = number .. '' 6 | for ii = 1, (#str / 2) do 7 | if string.byte(str,ii) ~= string.byte(str, -ii) then 8 | return false 9 | end 10 | end 11 | return true 12 | end 13 | 14 | LOW = 100 15 | --HIGH = 999 16 | HIGH = 199 17 | 18 | highest = 0 19 | for ii = LOW, HIGH do 20 | for jj = LOW, HIGH do 21 | num = ii * jj 22 | if is_palindrome(num) then 23 | highest = (num > highest) and num or highest 24 | end 25 | end 26 | end 27 | print(highest) 28 | return highest 29 | 30 | -------------------------------------------------------------------------------- /examples/euler/problem_005.lua: -------------------------------------------------------------------------------- 1 | -- 2520 is the smallest number that can be divided by each of the numbers from 1 to 10 without any remainder. 2 | 3 | -- What is the smallest positive number that is evenly divisible by all of the numbers from 1 to 20? 4 | 5 | LIMIT = 20 6 | 7 | function prime_factors(n) 8 | 9 | local function factor(_, val) 10 | if n > 1 then 11 | while n % val > 0 do 12 | val = val + ( val == 2 and 1 or 2) 13 | if val * val > n then val = n end 14 | end 15 | n = n / val 16 | return val 17 | end 18 | end 19 | return factor, nil, 2 20 | end 21 | 22 | function factorize(number) 23 | factors = {} 24 | for p in prime_factors(number) do 25 | factors[p] = factors[p] and factors[p] + 1 or 1 26 | end 27 | return factors 28 | end 29 | 30 | function collapse(dict1, dict2) 31 | dict = {} 32 | for key, val in pairs(dict1) do 33 | dict[key] = math.max(val, dict2[key] or 0) 34 | end 35 | for key, val in pairs(dict2) do 36 | dict[key] = math.max(val, dict1[key] or 0) 37 | end 38 | return dict 39 | end 40 | 41 | factors = {} 42 | for ii = 2, LIMIT do 43 | factors = collapse(factors, factorize(ii)) 44 | end 45 | product = 1 46 | for key, val in pairs(factors) do 47 | product = product * (key ^ val) 48 | end 49 | 50 | print(product) 51 | return product 52 | -------------------------------------------------------------------------------- /examples/euler/problem_006.lua: -------------------------------------------------------------------------------- 1 | -- The sum of the squares of the first ten natural numbers is, 2 | 3 | -- 12 + 22 + ... + 102 = 385 4 | 5 | -- The square of the sum of the first ten natural numbers is, 6 | 7 | -- (1 + 2 + ... + 10)2 = 552 = 3025 8 | 9 | -- Hence the difference between the sum of the squares of the first ten natural numbers and the square of the sum is 3025 385 = 2640. 10 | -- Find the difference between the sum of the squares of the first one hundred natural numbers and the square of the sum. 11 | 12 | LIMIT = 100 13 | sum, sum_squares = 0, 0 14 | 15 | for ii = 1, LIMIT do 16 | sum, sum_squares = sum + ii, sum_squares + (ii * ii) 17 | end 18 | 19 | answer = (sum*sum) - sum_squares 20 | print(answer) 21 | return answer 22 | -------------------------------------------------------------------------------- /examples/euler/problem_007.lua: -------------------------------------------------------------------------------- 1 | -- By listing the first six prime numbers: 2, 3, 5, 7, 11, and 13, we can see that the 6th prime is 13. 2 | -- What is the 10 001st prime number? 3 | 4 | -- LIMIT = 10001 5 | LIMIT = 113 6 | 7 | function primes(count) 8 | found_primes = { 2 } 9 | 10 | local function divisible_by_known_primes(num) 11 | for _, v in ipairs(found_primes) do 12 | if num % v == 0 then 13 | return true 14 | end 15 | end 16 | return false 17 | end 18 | 19 | local function next_prime(found_primes, _) 20 | val = found_primes[#found_primes] 21 | repeat 22 | val = (val == 2) and 3 or val + 2 23 | until(not divisible_by_known_primes(val)) 24 | table.insert(found_primes, val) 25 | 26 | if #found_primes > count then 27 | return nil 28 | else 29 | return val 30 | end 31 | end 32 | 33 | return next_prime, found_primes, 3 34 | end 35 | 36 | for ii in primes(LIMIT) do 37 | highest = ii 38 | end 39 | print(highest) 40 | return highest 41 | -------------------------------------------------------------------------------- /examples/euler/problem_008.lua: -------------------------------------------------------------------------------- 1 | -- Find the greatest product of five consecutive digits in the 1000-digit number. 2 | 3 | -- NUMBER = "7316717653133062491922511967442657474235534919493496983520312774506326239578318016984801869478851843858615607891129494954595017379583319528532088055111254069874715852386305071569329096329522744304355766896648950445244523161731856403098711121722383113622298934233803081353362766142828064444866452387493035890729629049156044077239071381051585930796086670172427121883998797908792274921901699720888093776657273330010533678812202354218097512545405947522435258490771167055601360483958644670632441572215539753697817977846174064955149290862569321978468622482839722413756570560574902614079729686524145351004748216637048440319989000889524345065854122758866688116427171479924442928230863465674813919123162824586178664583591245665294765456828489128831426076900422421902267105562632111110937054421750694165896040807198403850962455444362981230987879927244284909188845801561660979191338754992005240636899125607176060588611646710940507754100225698315520005593572972571636269561882670428252483600823257530420752963450" 4 | NUMBER = "11111222221" 5 | 6 | largest = 0 7 | for ii = 1, (#NUMBER-5) do 8 | digits = string.sub(NUMBER, ii, ii+5) 9 | sum = string.sub(digits, 1, 1) * 10 | string.sub(digits, 2, 2) * 11 | string.sub(digits, 3, 3) * 12 | string.sub(digits, 4, 4) * 13 | string.sub(digits, 5, 5) 14 | largest = (sum > largest) and sum or largest 15 | end 16 | print(largest) 17 | return largest 18 | -------------------------------------------------------------------------------- /examples/euler/problem_009.lua: -------------------------------------------------------------------------------- 1 | -- A Pythagorean triplet is a set of three natural numbers, a b c, for which, 2 | 3 | -- a2 + b2 = c2 4 | -- For example, 32 + 42 = 9 + 16 = 25 = 52. 5 | 6 | -- There exists exactly one Pythagorean triplet for which a + b + c = 1000. 7 | -- Find the product abc. 8 | 9 | for a = 2, 499 do 10 | for b = 2, 499 do 11 | c = (1000 - a) - b 12 | if a^2 + b^2 == c^2 then 13 | answer = a*b*c 14 | print(answer) 15 | return answer 16 | end 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /examples/euler/problem_010.lua: -------------------------------------------------------------------------------- 1 | -- The sum of the primes below 10 is 2 + 3 + 5 + 7 = 17. 2 | 3 | -- Find the sum of all the primes below two million. 4 | 5 | -- LIMIT = 2000000 6 | LIMIT = 2000 7 | 8 | function primes_below(limit) 9 | found_primes = { } 10 | 11 | local function divisible_by_known_primes(num) 12 | for _, v in ipairs(found_primes) do 13 | if num % v == 0 then 14 | return true 15 | end 16 | end 17 | return false 18 | end 19 | 20 | local function next_prime(found_primes, last_prime) 21 | if last_prime == nil then 22 | table.insert(found_primes, 2) 23 | return 2 24 | end 25 | 26 | val = found_primes[#found_primes] 27 | repeat 28 | val = (val == 2) and 3 or val + 2 29 | until(not divisible_by_known_primes(val)) 30 | table.insert(found_primes, val) 31 | 32 | if val > limit then 33 | return nil 34 | else 35 | return val 36 | end 37 | end 38 | 39 | return next_prime, found_primes, nil 40 | end 41 | 42 | 43 | sum = 0 44 | for ii in primes_below(LIMIT) do 45 | sum = sum + ii 46 | end 47 | print(sum) 48 | return sum 49 | -------------------------------------------------------------------------------- /examples/hello/Makefile: -------------------------------------------------------------------------------- 1 | EXAMPLES = hello \ 2 | hello2 \ 3 | hello_table \ 4 | hello_userdata \ 5 | hello_sandbox \ 6 | hello_funcalls 7 | 8 | ROOTDIR = ../.. 9 | SRCDIR = $(ROOTDIR)/src 10 | BEAMDIR = $(ROOTDIR)/ebin 11 | 12 | all: $(EXAMPLES) 13 | 14 | clean: 15 | rm -f *.beam erl_crash.dump 16 | 17 | .SECONDARY: 18 | 19 | %.beam: %.erl $(SRCDIR)/*.hrl 20 | erlc -I $(SRCDIR) $< 21 | 22 | %: %.beam 23 | erl -pa $(BEAMDIR) -s $@ run -s init stop -noshell 24 | 25 | .PHONY: all clean 26 | -------------------------------------------------------------------------------- /examples/hello/hello.erl: -------------------------------------------------------------------------------- 1 | %% File : hello.erl 2 | %% Purpose : Brief demonstration of Luerl basics. 3 | %% Use $ erlc hello.erl && erl -pa ./ebin -s hello run -s init stop -noshell 4 | %% Or $ make hello 5 | 6 | -module(hello). 7 | -export([run/0]). 8 | 9 | run() -> 10 | 11 | % execute a string 12 | luerl:do("print(\"Hello, Robert(o)!\")", luerl:init()), 13 | 14 | % execute a file 15 | luerl:dofile("./hello.lua", luerl:init()), 16 | 17 | % separately parse, then execute 18 | State0 = luerl:init(), 19 | {ok, Chunk, State1} = luerl:load("print(\"Hello, Chunk!\")", State0), 20 | {ok,_Ret, _NewState} = luerl:call(Chunk, [], State1), 21 | 22 | done. 23 | -------------------------------------------------------------------------------- /examples/hello/hello.lua: -------------------------------------------------------------------------------- 1 | -- File : hello.lua 2 | -- Purpose : Brief demonstration of Luerl basics - execution of a file. 3 | -- See : ./hello.erl 4 | 5 | print("Hello, File!") -------------------------------------------------------------------------------- /examples/hello/hello2-1.lua: -------------------------------------------------------------------------------- 1 | -- File : hello2-1.lua 2 | -- Purpose : Demonstration of Luerl interface. 3 | -- See : ./examples/hello/hello2.erl 4 | 5 | print("(6) Hello, File 'hello2-1'!") -------------------------------------------------------------------------------- /examples/hello/hello2-10.lua: -------------------------------------------------------------------------------- 1 | -- File : hello2-10.lua 2 | -- Purpose : Returning lua dicts 3 | -- See : ./examples/hello/hello2.erl 4 | 5 | return {1,2,{3,'Hello World!'}} 6 | 7 | -------------------------------------------------------------------------------- /examples/hello/hello2-2.lua: -------------------------------------------------------------------------------- 1 | -- File : hello2-2.lua 2 | -- Purpose : Demonstration of Luerl interface. 3 | -- See : ./examples/hello/hello2.erl 4 | 5 | return 2137 * 42 -------------------------------------------------------------------------------- /examples/hello/hello2-3.lua: -------------------------------------------------------------------------------- 1 | -- File : hello2-3.lua 2 | -- Purpose : Demonstration of Luerl interface. 3 | -- See : ./examples/hello/hello2.erl 4 | 5 | function no() print("(16) No!") end 6 | 7 | print("(15) Maybe ...") 8 | 9 | return "(X) Yes!" -------------------------------------------------------------------------------- /examples/hello/hello2-4.lua: -------------------------------------------------------------------------------- 1 | -- File : hello2-4.lua 2 | -- Purpose : Demonstration of Luerl interface. 3 | -- See : ./examples/hello/hello2.erl 4 | 5 | return "'(18b) Evidently, Mr. Watson.'" -------------------------------------------------------------------------------- /examples/hello/hello2-5.lua: -------------------------------------------------------------------------------- 1 | -- File : hello2-5.lua 2 | -- Purpose : Demonstration of Luerl interface. 3 | -- See : ./examples/hello/hello2.erl 4 | 5 | print ("(26) hello2-5.lua talking. a: " .. a) -------------------------------------------------------------------------------- /examples/hello/hello2-6.lua: -------------------------------------------------------------------------------- 1 | -- File : hello2-6.lua 2 | -- Purpose : Demonstration of Luerl interface. 3 | -- See : ./examples/hello/hello2.erl 4 | 5 | a = 'new contents of a' 6 | print('(27) (a) ' .. a) 7 | return a -------------------------------------------------------------------------------- /examples/hello/hello2-7.lua: -------------------------------------------------------------------------------- 1 | -- File : hello2-7.lua 2 | -- Purpose : Demonstration of Luerl interface. 3 | -- See : ./examples/hello/hello2.erl 4 | 5 | a = "(28a) οἶδα οὐκ εἰδώς, oîda ouk eidōs" 6 | return a 7 | -------------------------------------------------------------------------------- /examples/hello/hello2-8.lua: -------------------------------------------------------------------------------- 1 | -- File : hello2-8.lua 2 | -- Purpose : Demonstration of Luerl interface. 3 | -- See : ./examples/hello/hello2.erl 4 | 5 | function old() print "(33) old" end 6 | 7 | print "(32) News!" -------------------------------------------------------------------------------- /examples/hello/hello2-9.lua: -------------------------------------------------------------------------------- 1 | -- File : hello2-9.lua 2 | -- Purpose : Demonstration of Luerl interface. 3 | -- See : ./examples/hello/hello2.erl 4 | 5 | function confirm(p) 6 | return p .. ' (it really is)' 7 | end 8 | 9 | return confirm(a) -------------------------------------------------------------------------------- /examples/hello/hello2.erl: -------------------------------------------------------------------------------- 1 | %% File : hello2.erl 2 | %% File : luerl/examples/hello/hello2.erl 3 | %% Purpose : Demonstration of the Luerl interface. 4 | %% Author : Henning Diedrich 5 | %% Use : $ cd examples/hello && erlc hello2.erl && erl -pa ../../ebin -s hello2 run -s init stop -noshell 6 | %% Or : $ make examples 7 | 8 | -module(hello2). 9 | -export([run/0]). 10 | 11 | run() -> 12 | 13 | io:format("-------------------------------------------~n"), 14 | io:format("This is an assortment of samples and tests.~n"), 15 | io:format("-------------------------------------------~n"), 16 | io:format("It's a comprehensive demo of the interface.~n"), 17 | io:format("Please check out the source to learn more.~n"), 18 | 19 | St0A = luerl:init(), 20 | % execute a string 21 | luerl:do("print(\"(1) Hello, Robert!\")", St0A), 22 | luerl:do(<<"print(\"(2) Hello, Roberto!\")">>, St0A), 23 | 24 | % execute a string, get a result 25 | {ok,A,_} = luerl:do("return 1 + 1", St0A), 26 | {ok,A,_} = luerl:do(<<"return 1 + 1">>, St0A), 27 | io:format("(5) 1 + 1 = ~p!~n", [A]), 28 | 29 | % execute a file 30 | luerl:dofile("./hello2-1.lua", St0A), 31 | 32 | % execute a file, get a result 33 | {ok,B,_} = luerl:dofile("./hello2-2.lua", St0A), 34 | io:format("(7) 2137 * 42 = ~p?~n", [B]), 35 | 36 | % execute a standard function 37 | luerl:call_function_dec([print], 38 | [<<"(8) Hello, standard print function!">>], St0A), 39 | luerl:call_function_dec([print], 40 | [<<"(9) Hello, standard print function!">>], St0A), 41 | {ok,Result1,_} = luerl:call_function_dec([table,pack], 42 | [<<"a">>,<<"b">>,42], St0A), 43 | {ok,Result1,_} = luerl:call_function_dec([table,pack], 44 | [<<"a">>,<<"b">>,42], St0A), 45 | io:format("(10) ~p?~n", [Result1]), 46 | 47 | % separately parse, then execute (doubles (11) and Chunk1 as assertion) 48 | St1A = luerl:init(), 49 | {ok,Chunk1,St1B} = luerl:load("print(\"(11) Hello, Chunk 1!\")", St1A), 50 | {ok,Chunk1,_} = luerl:load(<<"print(\"(11) Hello, Chunk 1!\")">>, St1A), 51 | luerl:call_chunk(Chunk1, [], St1B), 52 | 53 | % separately parse, then execute (doubles (12) and Chunk2 as assertion) 54 | St2A = luerl:init(), 55 | {ok,Chunk2,St2B} = luerl:load("function chunk2() print(\"(12) Hello, Chunk 2!\") end", St2A), 56 | {ok,Chunk2,_} = luerl:load(<<"function chunk2() print(\"(12) Hello, Chunk 2!\") end">>, St2A), 57 | {ok,Result2,St2C} = luerl:call_chunk(Chunk2, [], St2B), 58 | {ok,Result2,St2D} = luerl:do(<<"function chunk2() print(\"(12) Hello, Chunk 2!\") end">>, St2A), 59 | luerl:call_function_dec([chunk2], [], St2C), 60 | luerl:call_function_dec([chunk2], [], St2D), 61 | 62 | % separately parse, then execute a file. The file defines a function no() 63 | St3A = luerl:init(), 64 | {ok,Chunk3,St3B} = luerl:loadfile("./hello2-3.lua", St3A), 65 | {ok,_Result3,St3C} = luerl:call_chunk(Chunk3, St3B), 66 | {ok,[],_} = luerl:call_function_dec([no], [], St3C), 67 | 68 | % separately parse, then execute, get a result 69 | St4A = luerl:init(), 70 | {ok,Chunk4,St4B} = luerl:load("return '(17b) Marvelous wheater today, isn°t it!'", St4A), 71 | {ok,Chunk4,_} = luerl:load(<<"return '(17b) Marvelous wheater today, isn°t it!'">>, St4A), 72 | {ok,Result4,_} = luerl:call_chunk(Chunk4, [], St4B), 73 | io:format("(17) And I say: ~p~n", [Result4]), 74 | 75 | % separately parse, then execute a file, get a result 76 | St5A = luerl:init(), 77 | {ok,Chunk5,St5B} = luerl:loadfile("./hello2-4.lua", St5A), 78 | {ok,Result5,_} = luerl:call_chunk(Chunk5, St5B), 79 | io:format("(18) And he says: ~p~n", [Result5]), 80 | 81 | 82 | % Same as above, passing State in all times. 83 | 84 | % create state 85 | New = luerl:init(), 86 | {ok,_,_New2} = luerl:do("print '(19) hello generix'", New), 87 | 88 | % change state 89 | {ok,_,State0} = luerl:do("a = 1000", New), 90 | {ok,_,State01} = luerl:do("a = 1000", New), 91 | 92 | % execute a string, using passed in State0 93 | luerl:do("print('(22) ' .. a+2)", State0), 94 | luerl:do(<<"print('(23) ' .. a+3)">>, State0), 95 | 96 | % execute a string, get a result from passed in State0 97 | {ok,E,_} = luerl:do("return 4 * a", State0), 98 | {ok,E,_} = luerl:do(<<"return 4 * a">>, State0), 99 | io:format("(24) 4 x a = ~p!~n", [E]), 100 | 101 | % execute a string, get a result, change State0 102 | {ok,Z,State02} = luerl:do("a = 123; return a * 3", State01), 103 | {ok,Z,State03} = luerl:do(<<"return (3 * a)">>, State02), 104 | io:format("(25) a = ~p~n", [Z]), 105 | 106 | % execute a file using passed in state 107 | luerl:dofile("./hello2-5.lua", State03), 108 | 109 | % execute a file that changes the State0 110 | {ok,_,State04} = luerl:dofile("./hello2-6.lua", State03), 111 | luerl:do("print('(27) (b) ' .. a)", State04), 112 | 113 | % execute a file, get a result 114 | {ok,F,State05} = luerl:dofile("./hello2-7.lua", State04), 115 | io:format("(28) F: ~ts~n", [F]), 116 | 117 | % execute a file that changes the State0, and get a value back 118 | {ok,F,State06} = luerl:dofile("./hello2-7.lua", State05), 119 | io:format("(29) F: ~ts = ", [F]), 120 | luerl:do("print('(30) F: ' .. a)", State06), 121 | 122 | % separately parse, then execute 123 | {ok,Chunk11,_} = luerl:load("print(\"(31) Hello, \" .. a .. \"!\")", State06), 124 | {ok,Chunk11,State07} = luerl:load(<<"print(\"(31) Hello, \" .. a .. \"!\")">>, State06), 125 | luerl:call_chunk(Chunk11,State07), 126 | 127 | % separately parse, then execute a file. The file defines a function old() 128 | {ok,Chunk12,St7} = luerl:loadfile("./hello2-8.lua", State07), 129 | {ok,_Result12,State07A} = luerl:call_chunk(Chunk12,St7), 130 | luerl:call_function_dec([old],[],State07A), 131 | 132 | % separately parse, then execute, get a result 133 | {ok,Chunk13,St8} = luerl:load("a = '(30a)' .. a .. ' (this is Greek)'; return a", State07), 134 | {ok,Chunk13,_} = luerl:load(<<"a = '(30a)' .. a .. ' (this is Greek)'; return a">>, State07), 135 | {ok,Result07,State08} = luerl:call_chunk(Chunk13, St8), 136 | io:format("(34) And again I said: ~ts~n", [Result07]), 137 | 138 | % separately parse, then execute a file, get a result. The file defines confirm(p) 139 | {ok,Chunk14,St9} = luerl:loadfile("./hello2-9.lua", State08), 140 | {ok,Result14,State14} = luerl:call_chunk(Chunk14, St9), 141 | io:format("(35) And twice: ~ts~n", [Result14]), 142 | {ok,Result14A,_} = luerl:call_function_dec([confirm], 143 | [<<"Is it?">>], State14), 144 | io:format("(36) Well: ~ts~n", [Result14A]), 145 | 146 | % execute a file, get the decoded result of a table 147 | {ok,Result15,_} = luerl:dofile("./hello2-10.lua", State14), 148 | io:format("(37) Decoded table: ~p~n", [Result15]), 149 | 150 | io:format("done~n"). 151 | -------------------------------------------------------------------------------- /examples/hello/hello_funcalls.erl: -------------------------------------------------------------------------------- 1 | %% File : hello_funcalls.erl 2 | %% Purpose : Demonstration of various ways to expose Erlang functions to Luerl. 3 | %% Use $ erlc hello_funcalls.erl && erl -pa ../../ebin -s hello_funcalls run -s init stop -noshell 4 | 5 | -module(hello_funcalls). 6 | 7 | -include("luerl.hrl"). 8 | 9 | -export([run/0,mfa_function/3]). 10 | 11 | regular_function(Args, St) -> 12 | io:format("regular_function(~p)\n", [Args]), 13 | {[42], St}. 14 | 15 | mfa_function(StaticArgs, DynamicArgs, St) -> 16 | io:format("mfa_function(~p, ~p)\n", [StaticArgs, DynamicArgs]), 17 | {[42], St}. 18 | 19 | run() -> 20 | LuaScript = <<"return hello_funcall(4, 5, 6)">>, 21 | Lua = luerl:init(), 22 | {ok,Lua1} = luerl:set_table_keys_dec([<<"hello_funcall">>], 23 | fun regular_function/2, Lua), 24 | 25 | % The argument part of {M,F,A} won't get encoded, so that is why 26 | % we can get away with passing a tuple here. 27 | {ok,Lua2} = luerl:set_table_keys_dec([<<"hello_funcall">>], 28 | {hello_funcalls,mfa_function,{1,2}}, 29 | Lua), 30 | 31 | {ok,Res1, _} = luerl:do(LuaScript, Lua1), 32 | io:format("regular_function got ~p~n", Res1), 33 | 34 | {ok,Res2, _} = luerl:do(LuaScript, Lua2), 35 | io:format("mfa_function got ~p~n", Res2). 36 | -------------------------------------------------------------------------------- /examples/hello/hello_sandbox.erl: -------------------------------------------------------------------------------- 1 | %% File : hello_sandbox.erl 2 | %% Purpose : Brief demonstration of Luerl sandbox basics. 3 | %% Use $ erlc hello_sandbox.erl && erl -pa ./ebin -s hello_sandbox run -s init stop -noshell 4 | %% Or $ make hello_sandbox 5 | 6 | -module(hello_sandbox). 7 | -export([run/0]). 8 | 9 | run() -> 10 | %% Default sandboxed state. 11 | SbSt = luerl_sandbox:init(), 12 | 13 | io:format("inited\n"), 14 | 15 | %% Sandboxing globals 16 | %% {error, {lua_error, Reason, _}} = 17 | {lua_error, Reason, _} = 18 | luerl_sandbox:run("return os.getenv(\"HOME\")", [], SbSt), 19 | io:format("os.getenv with sandbox: ~p~n",[Reason]), 20 | 21 | %% Customizing sandbox 22 | %% first with default Luerl state. 23 | {ok,[<<"number">>], _} = luerl_sandbox:run("return type(1)", luerl:init()), 24 | 25 | %% then with sandboxed type function 26 | {lua_error, _, _} = 27 | luerl_sandbox:run("return type(1)", luerl_sandbox:init([['_G', type]])), 28 | 29 | %% Using sandboxed state outside of runner 30 | try 31 | luerl:do("return os.getenv(\"HOME\")", SbSt) 32 | catch 33 | _:_ -> 34 | io:format("catch error with os.getenv(\"HOME\") with sandbox~n", []) 35 | end, 36 | 37 | %% Setting values. 38 | MaxReductions = 100, 39 | SpawnOpts = [{priority, low}], 40 | Timeout = 1000, 41 | 42 | %% Script runner with reduction counting. 43 | Flags0 = #{max_reductions => MaxReductions}, 44 | {error, {reductions, R0}} = 45 | luerl_sandbox:run("a={}; for i=1,1000000 do a[i] = 5 end", 46 | Flags0, SbSt), 47 | io:format("killed process with reductions ~p > 100~n",[R0]), 48 | 49 | %% Sandboxed run with default Luerl state. 50 | Flags1 = #{max_reductions => MaxReductions, 51 | spawn_opts => SpawnOpts, 52 | max_time => Timeout}, 53 | {error, {reductions, R1}} = 54 | luerl_sandbox:run("x = 'a'; while true do x = x .. x end", 55 | Flags1, luerl:init()), 56 | io:format("killed process with reductions ~p > 100~n",[R1]), 57 | 58 | %% Unlimited reductions 59 | Flags3 = #{max_reductions => none}, 60 | {ok, [], _} = luerl_sandbox:run("a={}; for i=1,10 do a[i] = 5 end", 61 | Flags3, SbSt), 62 | io:format("Finished running with unlimited reductions ~n",[]), 63 | 64 | done. 65 | -------------------------------------------------------------------------------- /examples/hello/hello_table.erl: -------------------------------------------------------------------------------- 1 | %% File : hello_table.erl 2 | %% Purpose : Brief demonstration of Luerl table access. 3 | %% Use $ erlc hello_table_new.erl && erl -pa ../../ebin -s hello_table_new run -s init stop -noshell 4 | 5 | -module(hello_table). 6 | -export([run/0]). 7 | 8 | run() -> 9 | LuaScript = <<"hello_table = { hello=\"world\" }; return hello_table">>, 10 | {ok, [_Table], Lua0} = luerl:do(LuaScript, luerl:init()), 11 | 12 | {ok,World,Lua1} = luerl:get_table_keys_dec([hello_table, hello], Lua0), 13 | {ok,Lua2} = luerl:set_table_keys_dec([hello_table, hello], there, Lua1), 14 | {ok,HelloDict,Lua3} = luerl:get_table_keys_dec([hello_table], Lua2), 15 | {ok,There, Lua4} = luerl:get_table_keys_dec([hello_table, hello], Lua3), 16 | io:format("(1) hello ~s ~s - ~p~n", [There, World, HelloDict]), 17 | 18 | {ok,Lua5} = 19 | luerl:set_table_keys([<<"hello_table">>, <<"goodbye">>], 20 | <<"bye">>, Lua4), 21 | {ok, Bye, Lua6} = 22 | luerl:get_table_keys([<<"hello_table">>, <<"goodbye">>], Lua5), 23 | {ok, HelloTab, _Lua7} = luerl:get_table_keys([<<"hello_table">>], Lua6), 24 | io:format("(2) ~s - ~p~n", [Bye, HelloTab]), 25 | 26 | done. 27 | -------------------------------------------------------------------------------- /examples/hello/hello_userdata.erl: -------------------------------------------------------------------------------- 1 | %% File : hello_userdata.erl 2 | %% Purpose : Brief demonstration of Luerl userdata access. 3 | %% Use $ erlc hello_userdata_new.erl && erl -pa ../../ebin -s hello_userdata_new run -s init stop -noshell 4 | 5 | -module(hello_userdata). 6 | -export([run/0]). 7 | 8 | run() -> 9 | St0 = luerl:init(), 10 | U42 = {userdata,42}, %The original decoded data 11 | {Uref,St1} = luerl:encode(U42, St0), 12 | {ok,St2} = luerl:set_table_keys([<<"u1">>], Uref, St1), 13 | {ok,St3} = luerl:set_table_keys([<<"u2">>], Uref, St2), 14 | %% This call wraps the actual data for us. 15 | St4 = luerl_heap:set_userdata_data(Uref, 84, St3), 16 | {ok,Uref,St5} = luerl:get_table_keys([<<"u1">>], St4), 17 | {ok,Uref,St6} = luerl:get_table_keys([<<"u2">>], St5), 18 | U84 = {userdata,84}, %New decoded data 19 | U84 = luerl:decode(Uref, St6), 20 | St6. 21 | -------------------------------------------------------------------------------- /examples/minibench/Makefile: -------------------------------------------------------------------------------- 1 | EXAMPLES = minibench \ 2 | minibench2 3 | 4 | ROOTDIR = ../.. 5 | SRCDIR = $(ROOTDIR)/src 6 | BEAMDIR = $(ROOTDIR)/ebin 7 | 8 | all: $(EXAMPLES) 9 | 10 | clean: 11 | rm -f *.beam erl_crash.dump 12 | 13 | .SECONDARY: 14 | 15 | %.beam: %.erl $(SRCDIR)/*.hrl 16 | erlc -I $(SRCDIR) $< 17 | 18 | %: %.beam 19 | erl -pa $(BEAMDIR) -s $@ run -s init stop -noshell 20 | 21 | .PHONY: all clean 22 | -------------------------------------------------------------------------------- /examples/minibench/minibench.erl: -------------------------------------------------------------------------------- 1 | %% File : mini.erl 2 | %% Author : Henning Diedrich 3 | %% File : luerl/examples/minibench/minibench.erl 4 | %% Purpose : Benchmark for frequent calls to small Luerl scripts 5 | %% Author : Henning Diedrich 6 | %% Use $ cd ./examples/minibench 7 | %% $ erlc minibench.erl 8 | %% $ erl -pa ../../ebin -s minibench run -s init stop -noshell 9 | %% Or $ make minibench 10 | 11 | -module(minibench). 12 | -export([run/0]). 13 | 14 | run() -> 15 | 16 | io:format("----------------------------------------------------------~n"), 17 | io:format("This is a benchmark of frequent fast calls into Luerl.~n"), 18 | 19 | % I. eval and execute 20 | io:format("----------------------------------------------------------~n"), 21 | io:format("Init state, parse and execute '1 + 1'~n"), 22 | I1 = 100000, 23 | {T1,_State} = timer:tc(fun() -> do_loop(I1, "return 1 + 1") end), 24 | 25 | io:format("Adding Up: ~p microseconds for ~p x calling Lua and returning the result of 1 + 1.~n", [T1,I1]), 26 | io:format("Per call: ~p microseconds.~n", [T1/I1]), 27 | 28 | 29 | % II. eval once, then only execute 30 | io:format("----------------------------------------------------------~n"), 31 | io:format("Init state, and execute pre-parsed '1 + 1'~n"), 32 | I2 = 100000, 33 | {ok, Chunk2, State2} = luerl:load("return 1 + 1", luerl:init()), 34 | 35 | {T2,_State21} = timer:tc(fun() -> do_loop_state(I2, Chunk2, State2) end), 36 | 37 | io:format("Adding Up: ~p microseconds for ~p x calling Lua and returning the result of 1 + 1.~n", [T2,I2]), 38 | io:format("Per call: ~p microseconds.~n", [T2/I2]), 39 | 40 | 41 | % III. eval once, then only execute 42 | io:format("----------------------------------------------------------~n"), 43 | io:format("Execute pre-parse execute '1 + 1', re-using same state~n"), 44 | I3 = 100000, 45 | State3 = luerl:init(), 46 | {ok, Chunk3, State31} = luerl:load("return 1 + 1", State3), 47 | 48 | {T3,_State31} = timer:tc(fun() -> do_loop_state(I3, Chunk3, State31) end), 49 | 50 | io:format("Adding Up: ~p microseconds for ~p x calling Lua and returning the result of 1 + 1.~n", [T3,I3]), 51 | io:format("Per call: ~p microseconds.~n", [T3/I3]), 52 | 53 | 54 | % IV. measure but state initialization 55 | io:format("----------------------------------------------------------~n"), 56 | io:format("Pure initialization of Lua state~n"), 57 | I4 = 100000, 58 | 59 | {T4,_State41} = timer:tc(fun() -> [luerl:init() || _ <- lists:seq(1,I4)] end), 60 | 61 | io:format("Adding Up: ~p microseconds for ~p x initializing Lua state.~n", [T4,I4]), 62 | io:format("Per call: ~p microseconds.~n", [T4/I4]), 63 | 64 | 65 | % V. eval once, then only execute, re-use previous state 66 | io:format("----------------------------------------------------------~n"), 67 | io:format("Execute pre-parsed '1 + 1', re-using state from last result~n"), 68 | I5 = 100000, 69 | State5 = luerl:init(), 70 | {ok, Chunk5, State51} = luerl:load("return 1 + 1", State5), 71 | 72 | {T5,_State51} = timer:tc(fun() -> do_loop_chain(I5, Chunk5, State51) end), 73 | 74 | io:format("Adding Up: ~p microseconds for ~p x calling Lua and returning the result of 1 + 1.~n", [T5,I5]), 75 | io:format("Per call: ~p microseconds.~n", [T5/I5]), 76 | 77 | 78 | % VI. measure but parsing 79 | io:format("----------------------------------------------------------~n"), 80 | io:format("Pure parsing~n"), 81 | I6 = 100000, 82 | {T6,_State61} = timer:tc(fun() -> [luerl:load("return 1 + 1", 83 | luerl:init()) || 84 | _ <- lists:seq(1,I6)] end), 85 | 86 | io:format("Adding Up: ~p microseconds for ~p x calling Lua and returning the result of 1 + 1.~n", [T6,I6]), 87 | io:format("Per call: ~p microseconds.~n", [T6/I6]), 88 | 89 | 90 | % VII. Parse and execute 91 | io:format("----------------------------------------------------------~n"), 92 | io:format("Parse and execute '1 + 1', re-using state~n"), 93 | I7 = 100000, 94 | State7 = luerl:init(), 95 | {T7,_State71} = timer:tc(fun() -> do_loop_do(I7, "return 1 + 1", State7) end), 96 | 97 | io:format("Adding Up: ~p microseconds for ~p x calling Lua and returning the result of 1 + 1.~n", [T7,I7]), 98 | io:format("Per call: ~p microseconds.~n", [T7/I7]), 99 | 100 | done. 101 | 102 | % helper 103 | do_loop(N, Chunk) when N > 0 -> 104 | luerl:do(Chunk, luerl:init()), 105 | do_loop(N-1, Chunk); 106 | do_loop(0, _) -> ok. 107 | 108 | do_loop_state(N, Chunk, State) when N > 0 -> 109 | luerl:call_chunk(Chunk, [], State), 110 | do_loop_state(N-1, Chunk, State); 111 | do_loop_state(0, _, _) -> ok. 112 | 113 | do_loop_do(N, String, State) when N > 0 -> 114 | luerl:do(String, State), 115 | do_loop_do(N-1, String, State); 116 | do_loop_do(0, _, _) -> ok. 117 | 118 | do_loop_chain(N, Chunk, State0) when N > 0 -> 119 | {ok,_,State1} = luerl:call_chunk(Chunk, [], State0), 120 | do_loop_chain(N-1, Chunk, State1); 121 | do_loop_chain(0, _, _) -> ok. 122 | -------------------------------------------------------------------------------- /examples/minibench/minibench2.erl: -------------------------------------------------------------------------------- 1 | %% File : minibench2.erl 2 | %% Author : Henning Diedrich 3 | %% File : luerl/examples/minibench/minibench.erl 4 | %% Purpose : Benchmark for frequent calls to small Luerl scripts 5 | %% Author : Henning Diedrich 6 | %% Use $ cd ./examples/minibench 7 | %% $ erlc minibench.erl 8 | %% $ erl -pa ../../ebin -s minibench run -s init stop -noshell 9 | %% Or $ make minibench 10 | 11 | -module(minibench2). 12 | -export([run/0]). 13 | 14 | run() -> 15 | 16 | io:format("----------------------------------------------------------~n"), 17 | io:format("This is a benchmark of frequent fast calls into Luerl.~n"), 18 | 19 | % I. eval and execute 20 | DoStr1 = "a = 7.33; b = 9000; c = (33 * a / b) ^ 15 * a + b; return c", 21 | io:format("----------------------------------------------------------~n"), 22 | io:format("Init state, parse and execute '~s'~n", [DoStr1]), 23 | I1 = 10000, 24 | {T1,_State} = timer:tc(fun() -> do_loop(I1, DoStr1) end), 25 | 26 | io:format("Adding Up: ~p microseconds for ~p x calling Lua and returning the result of '~s'.~n", 27 | [T1,I1,DoStr1]), 28 | io:format("Per call: ~p microseconds.~n", [T1/I1]), 29 | 30 | 31 | % II. eval once, then only execute 32 | DoStr2 = "a = 7.33; b = 9000; c = (33 * a / b) ^ 15 * a + b; return c", 33 | io:format("----------------------------------------------------------~n"), 34 | io:format("Init state, and execute pre-parsed '~s'~n", [DoStr2]), 35 | I2 = 10000, 36 | {ok, Chunk2, State2} = luerl:load(DoStr2, luerl:init()), 37 | 38 | {T2,_State21} = timer:tc(fun() -> do_loop_state(I2, Chunk2, State2) end), 39 | 40 | io:format("Adding Up: ~p microseconds for ~p x calling Lua and returning the result of '~s'.~n", 41 | [T2,I2,DoStr2]), 42 | io:format("Per call: ~p microseconds.~n", [T2/I2]), 43 | 44 | 45 | % III. eval once, then only execute 46 | DoStr3 = "a = 7.33; b = 9000; c = (33 * a / b) ^ 15 * a + b; return c", 47 | io:format("----------------------------------------------------------~n"), 48 | io:format("Execute pre-parse execute '~s', re-using same state~n", 49 | [DoStr3]), 50 | I3 = 10000, 51 | {ok, Chunk3, State3} = luerl:load(DoStr3, luerl:init()), 52 | 53 | {T3,_State31} = timer:tc(fun() -> do_loop_state(I3, Chunk3, State3) end), 54 | 55 | io:format("Adding Up: ~p microseconds for ~p x calling Lua and returning the result of '~s'.~n", 56 | [T3,I3,DoStr3]), 57 | io:format("Per call: ~p microseconds.~n", [T3/I3]), 58 | 59 | 60 | % IV. measure but state initialization 61 | io:format("----------------------------------------------------------~n"), 62 | io:format("Pure initialization of Lua state~n"), 63 | I4 = 10000, 64 | 65 | {T4,_State41} = timer:tc(fun() -> [luerl:init() || _ <- lists:seq(1,I4)] end), 66 | 67 | io:format("Adding Up: ~p microseconds for ~p x initializing a Lua state.~n", [T4,I4]), 68 | io:format("Per call: ~p microseconds.~n", [T4/I4]), 69 | 70 | 71 | % V. eval once, then only execute, re-use previous state 72 | DoStr5 = "a = 7.33; b = 9000; c = (33 * a / b) ^ 15 * a + b; return c", 73 | io:format("----------------------------------------------------------~n"), 74 | io:format("Execute pre-parsed '~s', re-using state from last result~n", 75 | [DoStr5]), 76 | I5 = 10000, 77 | {ok, Chunk5, State5} = luerl:load(DoStr5, luerl:init()), 78 | 79 | {T5,_State51} = timer:tc(fun() -> do_loop_chain(I5, Chunk5, State5) end), 80 | 81 | io:format("Adding Up: ~p microseconds for ~p x calling Lua and returning the result of '~s'.~n", [T5,I5,DoStr5]), 82 | io:format("Per call: ~p microseconds.~n", [T5/I5]), 83 | 84 | % Vb. function call, re-use previous state 85 | DoStr5b = "function OneAndOne() a = 7.33; b = 9000; c = (33 * a / b) ^ 15 * a + b; return c end", 86 | io:format("----------------------------------------------------------~n"), 87 | io:format("Execute pre-parsed function '~s', re-using state from last result~n", 88 | [DoStr5b]), 89 | I5b = 10000, 90 | State5b = luerl:init(), 91 | {ok,[],State5b1} = luerl:do(DoStr5b, State5b), 92 | io:format("-"), 93 | {T5b,_State5b1} = timer:tc(fun() -> do_loop_do(I5b, "return OneAndOne()", State5b1) end), 94 | 95 | io:format("Adding Up: ~p microseconds for ~p x calling Lua and returning the result of '~s'.~n", 96 | [T5b,I5b,DoStr5b]), 97 | io:format("Per call: ~p microseconds.~n", [T5b/I5b]), 98 | 99 | % Vc. empty function call, re-use previous state 100 | DoStr5c = "function EmptyFunc() end", 101 | io:format("----------------------------------------------------------~n"), 102 | io:format("Execute empty function, re-using state from last result~n"), 103 | I5c = 10000, 104 | State5c = luerl:init(), 105 | {ok,[],State5c1} = luerl:do(DoStr5c, State5c), 106 | io:format("-"), 107 | {T5c,_State5c1} = timer:tc(fun() -> do_loop_do(I5c, "EmptyFunc()", State5c1) end), 108 | 109 | io:format("Adding Up: ~p microseconds for ~p x calling empty function.~n", 110 | [T5c,I5c]), 111 | io:format("Per call: ~p microseconds.~n", [T5c/I5c]), 112 | 113 | % VI. measure but parsing 114 | DoStr6 = "a = 7.33; b = 9000; c = (33 * a / b) ^ 15 * a + b; return c", 115 | io:format("----------------------------------------------------------~n"), 116 | io:format("Pure parsing~n"), 117 | I6 = 10000, 118 | {T6,_State61} = timer:tc(fun() -> [luerl:load(DoStr6, luerl:init()) || 119 | _ <- lists:seq(1,I6)] end), 120 | 121 | io:format("Adding Up: ~p microseconds for ~p x calling Lua and returning the result of '~s'.~n", [T6,I6,DoStr6]), 122 | io:format("Per call: ~p microseconds.~n", [T6/I6]), 123 | 124 | 125 | % VII. Parse and execute 126 | DoStr7 = "a = 7.33; b = 9000; c = (33 * a / b) ^ 15 * a + b; return c", 127 | io:format("----------------------------------------------------------~n"), 128 | io:format("Parse and execute 'a = 7.33; b = 9000; c = (33 * a / b) ^ 15 * a + b', re-using state~n"), 129 | I7 = 10000, 130 | State7 = luerl:init(), 131 | {T7,_State71} = timer:tc(fun() -> do_loop_do(I7, DoStr7, State7) end), 132 | 133 | io:format("Adding Up: ~p microseconds for ~p x calling Lua and returning the result of '~s'.~n", [T7,I7,DoStr7]), 134 | io:format("Per call: ~p microseconds.~n", [T7/I7]), 135 | 136 | 137 | done. 138 | 139 | % helper 140 | do_loop(N, Chunk) when N > 0 -> 141 | luerl:do(Chunk, luerl:init()), 142 | do_loop(N-1, Chunk); 143 | do_loop(0, _) -> ok. 144 | 145 | do_loop_state(N, Chunk, State) when N > 0 -> 146 | luerl:call_chunk(Chunk, [], State), 147 | do_loop_state(N-1, Chunk, State); 148 | do_loop_state(0, _, _) -> ok. 149 | 150 | do_loop_do(N, String, State) when N > 0 -> 151 | {ok,_,State1} = luerl:do(String, State), 152 | do_loop_do(N-1, String, State1); 153 | do_loop_do(0, _, _) -> ok. 154 | 155 | do_loop_chain(N, Chunk, State0) when N > 0 -> 156 | {ok,_,State1} = luerl:call_chunk(Chunk, State0), 157 | do_loop_chain(N-1, Chunk, State1); 158 | do_loop_chain(0, _, _) -> ok. 159 | -------------------------------------------------------------------------------- /get_comp_opts.escript: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env escript 2 | %% -*- mode: erlang; indent-tabs-mode: nil -*- 3 | %% Define a number of compiler options. We first work out the current 4 | %% Erlang version and from the we can define the various options. 5 | 6 | %% Define the makefile variables HAS_MAPS, HAS_FULL_KEYS, 7 | %% NEW_REC_CORE, NEW_RAND, HAS_FLOOR, HAS_CEIL and NEW_STACKTRACE 8 | %% depending on version of Erlang. 9 | 10 | main(_) -> 11 | Version = otp_release(), 12 | CompOpts = comp_opts(Version), 13 | file:write_file("comp_opts.mk", "COMP_OPTS = " ++ CompOpts ++ "\n"). 14 | 15 | %% Get the release number. 16 | %% We have stolen the idea and most of the code from rebar3. 17 | 18 | otp_release() -> 19 | case erlang:system_info(otp_release) of 20 | [$R,N1|Rest] when is_integer(N1) -> 21 | %% If OTP <= R16, take the digits. 22 | [N1|Rest]; 23 | Rel -> 24 | %% If OTP >= 17.x, erlang:system_info(otp_release) returns 25 | %% just the major version number. 26 | File = filename:join([code:root_dir(),"releases",Rel,"OTP_VERSION"]), 27 | case file:read_file(File) of 28 | {error, _} -> Rel; 29 | {ok, Vsn} -> 30 | Size = byte_size(Vsn), 31 | %% The shortest vsn string consists of at least 32 | %% two digits followed by "\n". Therefore, it's 33 | %% safe to assume Size >= 3. 34 | case binary:part(Vsn, {Size, -3}) of 35 | <<"**\n">> -> 36 | binary:bin_to_list(Vsn, {0, Size - 3}); 37 | _ -> 38 | binary:bin_to_list(Vsn, {0, Size - 1}) 39 | end 40 | end 41 | end. 42 | 43 | comp_opts(Version) -> 44 | Copts0 = "-DERLANG_VERSION=\\\"" ++ Version ++ "\\\"" ++ " ", 45 | Copts0 ++ append_copts(Version, [{"17","HAS_MAPS"}, 46 | {"18","HAS_FULL_KEYS"}, 47 | {"19","NEW_REC_CORE"}, 48 | {"19","NEW_RAND"}, 49 | {"20","NEW_BOOL_GUARD"}, 50 | {"20","HAS_FLOOR"}, 51 | {"20","HAS_CEIL"}, 52 | {"21","NEW_STACKTRACE"}, 53 | {"23","EEP48"}, 54 | {"27","OTP27_MAYBE"}]). 55 | 56 | append_copts(Version, [{Ver,Opt}|Opts]) -> 57 | Rest = append_copts(Version, Opts), 58 | if Version >= Ver -> 59 | "-D" ++ Opt ++ "=true" ++ " " ++ Rest; 60 | true -> Rest 61 | end; 62 | append_copts(_Version, []) -> []. 63 | -------------------------------------------------------------------------------- /priv/images/logo-large.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rvirding/luerl/f1077a818d2d89cc67265b47b9d1a6d639dd4728/priv/images/logo-large.png -------------------------------------------------------------------------------- /priv/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rvirding/luerl/f1077a818d2d89cc67265b47b9d1a6d639dd4728/priv/images/logo.png -------------------------------------------------------------------------------- /rebar.config: -------------------------------------------------------------------------------- 1 | %% -*- mode: erlang; indent-tabs-mode: nil -*- 2 | {erl_opts, [{debug_info, true}]}. 3 | {deps, []}. 4 | 5 | {shell, [ 6 | % {config, "config/sys.config"}, 7 | {apps, [luerl]} 8 | ]}. 9 | 10 | {relx, [{release, {luerl, "1.4.1"}, 11 | [kernel, 12 | stdlib, 13 | sasl, 14 | inets, 15 | luerl]}, 16 | {dev_mode, false}, 17 | {include_erts, true}, 18 | {extended_start_script, true}]}. 19 | 20 | {profiles, [ 21 | {test, [ 22 | {dist_node, [ 23 | {setcookie, 'ct'}, 24 | {sname, 'ct'} 25 | ]}, 26 | {ct_opts, [{logdir, "logs"}]} 27 | ]}, 28 | {otp_24, [ 29 | {xrl_opts, []}, 30 | {yrl_opts, []} 31 | ]}, 32 | {default, [ 33 | {xrl_opts, [{deterministic, true}]}, 34 | {yrl_opts, [{deterministic, true}]} 35 | ]} 36 | ]}. 37 | 38 | {project_plugins, [rebar3_hex, rebar3_ex_doc]}. 39 | 40 | {hex, [ 41 | {doc, #{provider => ex_doc}} 42 | ]}. 43 | 44 | {ex_doc, [ 45 | {source_url, <<"https://github.com/rvirding/luerl">>}, 46 | {extras, ["README.md", "CHANGELOG.md", "LICENSE"]}, 47 | {main, "readme"}, 48 | {skip_undefined_reference_warnings_on, ["luerl_parse", "luerl_scan"]}, 49 | {groups_for_modules, [ 50 | {"Core API", [ 51 | luerl, 52 | luerl_old, 53 | luerl_sandbox 54 | ]}, 55 | {"Elixir API", [ 56 | 'Elixir.Luerl', 57 | 'Elixir.Luerl.Old' 58 | ]} 59 | ]} 60 | ]}. 61 | -------------------------------------------------------------------------------- /rebar.config.script: -------------------------------------------------------------------------------- 1 | %% -*- mode: erlang; indent-tabs-mode: nil -*- 2 | 3 | Conf0 = CONFIG, %The original config 4 | 5 | %% Do a deep set stepping down a list of keys replacing/adding last 6 | %% with value. Named funs would be nicer but not always available. 7 | 8 | SetConf = fun ([K], Val, Ps, _F) -> 9 | %% Replace the whole K field with Val. 10 | [Val|proplists:delete(K, Ps)]; 11 | ([K|Ks], Val, Ps, F) -> 12 | %% Step down and build coming up. 13 | case lists:keyfind(K, 1, Ps) of 14 | {K,Kps} -> 15 | lists:keyreplace(K, 1, Ps, {K,F(Ks, Val, Kps, F)}); 16 | false -> Ps ++ [{K,F(Ks, Val, [], F)}] 17 | end 18 | end, 19 | 20 | %% Get the release number. 21 | %% We have stolen the idea and most of the code from rebar3. 22 | 23 | OTPRelease = 24 | fun () -> 25 | case erlang:system_info(otp_release) of 26 | [$R,N1|Rest] when is_integer(N1) -> 27 | %% If OTP <= R16, take the digits. 28 | [N1|Rest]; 29 | Rel -> 30 | %% If OTP >= 17.x, erlang:system_info(otp_release) returns 31 | %% just the major version number. 32 | File = filename:join([code:root_dir(),"releases",Rel,"OTP_VERSION"]), 33 | case file:read_file(File) of 34 | {error, _} -> Rel; 35 | {ok, Vsn} -> 36 | Size = byte_size(Vsn), 37 | %% The shortest vsn string consists of at least 38 | %% two digits followed by "\n". Therefore, it's 39 | %% safe to assume Size >= 3. 40 | case binary:part(Vsn, {Size, -3}) of 41 | <<"**\n">> -> 42 | binary:bin_to_list(Vsn, {0, Size - 3}); 43 | _ -> 44 | binary:bin_to_list(Vsn, {0, Size - 1}) 45 | end 46 | end 47 | end 48 | end, 49 | 50 | Version = OTPRelease(), 51 | 52 | %% Collect the macro definitions we will add to the compiler options. 53 | %% Named funs would be nicer but not always available. 54 | 55 | AppendCopts = fun (Version, [{Ver,Opt}|Opts], F) -> 56 | Rest = F(Version, Opts, F), 57 | if Version >= Ver -> 58 | [{d,Opt,true}|Rest]; 59 | true -> 60 | Rest 61 | end; 62 | (_Version, [], _F) -> [] 63 | end, 64 | 65 | Copts0 = [{d,'ERLANG_VERSION',Version}], 66 | Copts = Copts0 ++ AppendCopts(Version, 67 | [{"17",'HAS_MAPS'}, 68 | {"18",'HAS_FULL_KEYS'}, 69 | {"19",'NEW_REC_CORE'}, 70 | {"19",'NEW_RAND'}, 71 | {"20",'NEW_BOOL_GUARD'}, 72 | {"20",'HAS_FLOOR'}, 73 | {"20",'HAS_CEIL'}, 74 | {"21",'NEW_STACKTRACE'}, 75 | {"23",'EEP48'}, 76 | {"27",'OTP27_MAYBE'}], 77 | AppendCopts), 78 | 79 | %% Ensure they are in erl_opts. 80 | 81 | %% Make sure debug_info is always included for documentation 82 | AllOpts = [{debug_info, true} | Copts], 83 | 84 | Conf1 = case lists:keyfind(erl_opts, 1, Conf0) of 85 | {erl_opts,Opts} -> %Existing erl_opts 86 | %% Make sure debug_info is included in the final options 87 | OptsWithDebug = case lists:member(debug_info, Opts) of 88 | true -> lists:delete(debug_info, Opts) ++ [{debug_info, true}]; 89 | false -> Opts ++ [{debug_info, true}] 90 | end, 91 | NewOpts = {erl_opts, OptsWithDebug ++ Copts}, 92 | lists:keyreplace(erl_opts, 1, Conf0, NewOpts); 93 | false -> %No erl_opts 94 | Conf0 ++ [{erl_opts, AllOpts}] 95 | end, 96 | 97 | %% TestConfig = [{cover_enabled, true}, {cover_opts, [verbose]}], 98 | 99 | %% Aliases = [{alias, [ 100 | %% {test, [eunit, {ct, "--cover"}, cover]} 101 | %% ]}], 102 | 103 | %% Conf1 ++ TestConfig ++ Aliases. 104 | %% 105 | 106 | Conf1. 107 | -------------------------------------------------------------------------------- /rebar.lock: -------------------------------------------------------------------------------- 1 | []. 2 | -------------------------------------------------------------------------------- /src/Elixir.Luerl.Old.erl: -------------------------------------------------------------------------------- 1 | %% Copyright (c) 2013-2024 Robert Virding 2 | %% 3 | %% Licensed under the Apache License, Version 2.0 (the "License"); 4 | %% you may not use this file except in compliance with the License. 5 | %% You may obtain a copy of the License at 6 | %% 7 | %% http://www.apache.org/licenses/LICENSE-2.0 8 | %% 9 | %% Unless required by applicable law or agreed to in writing, software 10 | %% distributed under the License is distributed on an "AS IS" BASIS, 11 | %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | %% See the License for the specific language governing permissions and 13 | %% limitations under the License. 14 | 15 | %% File : luerl_ex.erl 16 | %% Authors : Cees de Groot 17 | %% Purpose : Elixir-style wrappers for luerl_old.erl 18 | 19 | %% This module just contains functions that forward to luerl_old.erl, 20 | %% but place the VM State arguments in the first position rather than 21 | %% the last. This better matches Elixir conventions and allows for 22 | %% using the Elixir pipe operator '|>' to chain Luerl function calls. 23 | 24 | -module('Elixir.Luerl.Old'). 25 | 26 | -include("luerl.hrl"). 27 | 28 | ?MODULEDOC(""" 29 | Legacy Elixir API for Luerl 30 | 31 | This module provides Elixir-friendly wrappers for the luerl_old module 32 | with state as the first argument for better pipe operator usage. 33 | New code should use the Luerl module instead. 34 | """). 35 | 36 | -export([eval/2,evalfile/2, 37 | do/2,dofile/2, 38 | load/2,load/3, 39 | loadfile/2,loadfile/3, 40 | path_loadfile/2,path_loadfile/3,path_loadfile/4, 41 | load_module/3,load_module1/3, 42 | call/3,call_chunk/3, 43 | call_function/3,call_function1/3,function_list/2, 44 | call_method/3,call_method1/3,method_list/2, 45 | get_table/2,get_table1/2,set_table/3,set_table1/3,set_table1/4, 46 | init/0,stop/1,gc/1, 47 | set_trace_func/2,clear_trace_func/1, 48 | set_trace_data/2,get_trace_data/1, 49 | get_stacktrace/1, 50 | externalize/1,internalize/1 51 | ]). 52 | 53 | %% Encoding and decoding. 54 | -export([encode/2,encode_list/2,decode/2,decode_list/2]). 55 | 56 | eval(St, Chunk) -> 57 | luerl_old:eval(Chunk, St). 58 | 59 | evalfile(St, Path) -> 60 | luerl_old:evalfile(Path, St). 61 | 62 | do(St, S) -> 63 | luerl_old:do(S, St). 64 | 65 | dofile(St, Path) -> 66 | luerl_old:dofile(Path, St). 67 | 68 | load(St, Bin) -> 69 | luerl_old:load(Bin, St). 70 | 71 | load(St, Bin, Opts) -> 72 | luerl_old:load(Bin, Opts, St). 73 | 74 | loadfile(St, Name) -> 75 | luerl_old:loadfile(Name, St). 76 | 77 | loadfile(St, Name, Opts) -> 78 | luerl_old:loadfile(Name, Opts, St). 79 | 80 | path_loadfile(St, Name) -> 81 | luerl_old:path_loadfile(Name, St). 82 | 83 | path_loadfile(St, Dirs, Name) -> 84 | luerl_old:path_loadfile(Dirs, Name, St). 85 | 86 | path_loadfile(St, Dir, Name, Opts) -> 87 | luerl_old:path_loadfile(Dir, Name, Opts, St). 88 | 89 | load_module(St, Fp, Mod) -> 90 | luerl_old:load_module(Fp, Mod, St). 91 | 92 | load_module1(St, Fp, Mod) -> 93 | luerl_old:load_module1(Fp, Mod, St). 94 | 95 | init() -> 96 | luerl_old:init(). 97 | 98 | call(St, C, As) -> 99 | luerl_old:call(C, As, St). 100 | 101 | call_chunk(St, C, As) -> 102 | luerl_old:call_chunk(C, As, St). 103 | 104 | call_function(St, Fp, As) -> 105 | luerl_old:call_function(Fp, As, St). 106 | 107 | call_function1(St, Lfp, Las) -> 108 | luerl_old:call_function1(Lfp, Las, St). 109 | 110 | function_list(St, Ks) -> 111 | luerl_old:function_list(Ks, St). 112 | 113 | call_method(St, Fp, As) -> 114 | luerl_old:call_method(Fp, As, St). 115 | 116 | call_method1(St, Fp, Las) -> 117 | luerl_old:call_method1(Fp, Las, St). 118 | 119 | method_list(St, Ks) -> 120 | luerl_old:method_list(Ks, St). 121 | 122 | get_table(St, Fp) -> 123 | luerl_old:get_table(Fp, St). 124 | 125 | get_table1(St, Fp) -> 126 | luerl_old:get_table1(Fp, St). 127 | 128 | set_table(St, Fp, V) -> 129 | luerl_old:set_table(Fp, V, St). 130 | 131 | set_table1(St, Lfp, Lv) -> 132 | luerl_old:set_table1(Lfp, Lv, St). 133 | 134 | set_table1(St, Tab, Key, Lv) -> 135 | luerl_old:set_table1(Tab, Key, Lv, St). 136 | 137 | stop(St) -> 138 | luerl_old:stop(St). 139 | 140 | gc(St) -> 141 | luerl_old:gc(St). 142 | 143 | set_trace_func(St, Func) -> 144 | luerl_old:set_trace_func(Func, St). 145 | 146 | clear_trace_func(St) -> 147 | luerl_old:clear_trace_func(St). 148 | 149 | get_trace_data(St) -> 150 | luerl_old:get_trace_data(St). 151 | 152 | set_trace_data(St, Tdata) -> 153 | luerl_old:set_trace_data(Tdata, St). 154 | 155 | get_stacktrace(St) -> 156 | luerl_old:get_stacktrace(St). 157 | 158 | encode_list(St, Ts) -> 159 | luerl_old:encode_list(Ts, St). 160 | 161 | encode(St, V) -> 162 | luerl_old:encode(V, St). 163 | 164 | decode_list(St, Lts) -> 165 | luerl_old:decode_list(Lts, St). 166 | 167 | decode(St, V) -> 168 | luerl_old:decode(V, St). 169 | 170 | externalize(St) -> 171 | luerl_old:externalize(St). 172 | 173 | internalize(St) -> 174 | luerl_old:internalize(St). 175 | -------------------------------------------------------------------------------- /src/Elixir.Luerl.erl: -------------------------------------------------------------------------------- 1 | %% Copyright (c) 2013-2024 Robert Virding 2 | %% 3 | %% Licensed under the Apache License, Version 2.0 (the "License"); 4 | %% you may not use this file except in compliance with the License. 5 | %% You may obtain a copy of the License at 6 | %% 7 | %% http://www.apache.org/licenses/LICENSE-2.0 8 | %% 9 | %% Unless required by applicable law or agreed to in writing, software 10 | %% distributed under the License is distributed on an "AS IS" BASIS, 11 | %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | %% See the License for the specific language governing permissions and 13 | %% limitations under the License. 14 | 15 | %% File : luerl_ex.erl 16 | %% Authors : Cees de Groot 17 | %% Purpose : Elixir-style wrappers for luerl.erl 18 | 19 | %% This module just contains functions that forward to luerl.erl, but place 20 | %% the VM State arguments in the first position rather than the last. This 21 | %% better matches Elixir conventions and allows for using the Elixir pipe 22 | %% operator '|>' to chain Luerl function calls. 23 | 24 | -module('Elixir.Luerl'). 25 | 26 | -include("luerl.hrl"). 27 | 28 | ?MODULEDOC(""" 29 | Elixir API for Luerl, an implementation of Lua 5.3 written in Erlang. 30 | 31 | This module provides an idiomatic Elixir interface to the Luerl Lua 32 | interpreter with state as the first argument for better pipe operator usage. 33 | """). 34 | 35 | %% Basic user API to luerl. 36 | -export([init/0,gc/1, 37 | load/2,load/3,loadfile/2,loadfile/3, 38 | path_loadfile/2,path_loadfile/3,path_loadfile/4, 39 | load_module/3,load_module_dec/3, 40 | do/2,do_dec/2,do/3,do_dec/3, 41 | dofile/2,dofile/3,dofile_dec/2,dofile_dec/3, 42 | call/3,call_chunk/2,call_chunk/3, 43 | call_function/3,call_function_enc/3,call_function_dec/3, 44 | call_method/4,call_method_dec/4, 45 | get_table_keys/2,get_table_keys_dec/2, 46 | set_table_keys/3,set_table_keys_dec/3, 47 | get_table_key/3,set_table_key/4, 48 | get_stacktrace/1 49 | ]). 50 | 51 | %% Tracing. 52 | -export([set_trace_func/2,clear_trace_func/1, 53 | set_trace_data/2,get_trace_data/1]). 54 | 55 | %% Encoding and decoding. 56 | -export([encode/2,encode_list/2,decode/2,decode_list/2]). 57 | 58 | %%Helping with storing VM state 59 | -export([externalize/1,internalize/1]). 60 | 61 | %% Storing and retrieving private data 62 | -export([put_private/3,get_private/2,delete_private/2]). 63 | 64 | init() -> 65 | luerl:init(). 66 | 67 | gc(St) -> 68 | luerl:gc(St). 69 | 70 | set_trace_func(St, Func) -> 71 | luerl:set_trace_func(Func, St). 72 | 73 | clear_trace_func(St) -> 74 | luerl:clear_trace_func(St). 75 | 76 | get_trace_data(St) -> 77 | luerl:get_trace_data(St). 78 | 79 | set_trace_data(St, Tdata) -> 80 | luerl:set_trace_data(Tdata, St). 81 | 82 | load(St, Bin) -> 83 | luerl:load(Bin, St). 84 | 85 | load(St, Bin, Opts) -> 86 | luerl:load(Bin, Opts, St). 87 | 88 | loadfile(St, Name) -> 89 | luerl:loadfile(Name, St). 90 | 91 | loadfile(St, Name, Opts) -> 92 | luerl:loadfile(Name, Opts, St). 93 | 94 | path_loadfile(St, Path, Name) -> 95 | luerl:path_loadfile(Path, Name, St). 96 | 97 | path_loadfile(St, Path, Name, Options) -> 98 | luerl:path_loadfile(Path, Name, Options, St). 99 | 100 | path_loadfile(St, Name) -> 101 | luerl:path_loadfile(Name, St). 102 | 103 | load_module(St, Lfp, Mod) -> 104 | luerl:load_module(Lfp, Mod, St). 105 | 106 | load_module_dec(St, Dfp, Mod) -> 107 | luerl:load_module_dec(Dfp, Mod, St). 108 | 109 | do(St, S) -> 110 | luerl:do(S, St). 111 | 112 | do(St, S, Opts) -> 113 | luerl:do(S, Opts, St). 114 | 115 | do_dec(St, S) -> 116 | luerl:do_dec(S, St). 117 | 118 | do_dec(St, S, Opts) -> 119 | luerl:do_dec(S, Opts, St). 120 | 121 | dofile(St, Path) -> 122 | luerl:dofile(Path, St). 123 | 124 | dofile(St, Path, Opts) -> 125 | luerl:dofile(Path, Opts, St). 126 | 127 | dofile_dec(St, Path) -> 128 | luerl:dofile_dec(Path, St). 129 | 130 | dofile_dec(St, Path, Opts) -> 131 | luerl:dofile_dec(Path, Opts, St). 132 | 133 | call(St, C, Args) -> 134 | luerl:call(C, Args, St). 135 | 136 | call_chunk(St, C) -> 137 | luerl:call_chunk(C, St). 138 | 139 | call_chunk(St, C, Args) -> 140 | luerl:call_chunk(C, Args, St). 141 | 142 | call_function(St, Fp, Args) -> 143 | luerl:call_function(Fp, Args, St). 144 | 145 | call_function_enc(St, Dfunc, Dargs) -> 146 | luerl:call_function_enc(Dfunc, Dargs, St). 147 | 148 | call_function_dec(St, Dfunc, Dargs) -> 149 | luerl:call_function_dec(Dfunc, Dargs, St). 150 | 151 | call_method(St, Obj, Meth, Args) -> 152 | luerl:call_method(Obj, Meth, Args, St). 153 | 154 | call_method_dec(St, Dobj, Dmeth, Dargs) -> 155 | luerl:call_method_dec(Dobj, Dmeth, Dargs, St). 156 | 157 | get_table_keys(St, Keys) -> 158 | luerl:get_table_keys(Keys, St). 159 | 160 | get_table_keys_dec(St, Dkeys) -> 161 | luerl:get_table_keys_dec(Dkeys, St). 162 | 163 | set_table_keys(St, Keys, Val) -> 164 | luerl:set_table_keys(Keys, Val, St). 165 | 166 | set_table_keys_dec(St, Dkeys, Dval) -> 167 | luerl:set_table_keys_dec(Dkeys, Dval, St). 168 | 169 | get_table_key(St, Tab, Key) -> 170 | luerl:get_table_key(Tab, Key, St). 171 | 172 | set_table_key(St, Tab, Key, Val) -> 173 | luerl:set_table_key(Tab, Key, Val, St). 174 | 175 | get_stacktrace(St) -> 176 | luerl:get_stacktrace(St). 177 | 178 | encode(St, V) -> 179 | luerl:encode(V, St). 180 | 181 | encode_list(St, Ts) -> 182 | luerl:encode_list(Ts, St). 183 | 184 | decode(St, V) -> 185 | luerl:decode(V, St). 186 | 187 | decode_list(St, Lts) -> 188 | luerl:decode_list(Lts, St). 189 | 190 | externalize(St) -> 191 | luerl:externalize(St). 192 | 193 | internalize(St) -> 194 | luerl:internalize(St). 195 | 196 | put_private(St, K, V) -> 197 | luerl:put_private(K, V, St). 198 | 199 | get_private(St, Key) -> 200 | try 201 | {ok, maps:get(Key, St#luerl.private)} 202 | catch 203 | error:{badkey, _} -> 204 | error 205 | end. 206 | 207 | 208 | delete_private(St, K) -> 209 | try 210 | luerl:delete_private(K, St) 211 | catch 212 | error:{badkey, _} -> 213 | St 214 | end. 215 | -------------------------------------------------------------------------------- /src/NOTES: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013 Robert Virding 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | 15 | 16 | Implementation Notes 17 | -------------------- 18 | 19 | Syntax 20 | ------ 21 | 22 | We are almost able to represent the Lua syntax as an LALR(1) 23 | grammar. The only place this doesn't work is when a statement can be a 24 | function call as it clashes when it can also be a varlist. We get 25 | around this by using the more general prefixexp and doing a post-test 26 | to check that it is a functioncall. This works together with the 27 | varlist. 28 | 29 | Data 30 | ---- 31 | 32 | For the Lua data types we internally use the corresponding Erlang: 33 | 34 | nil - nil 35 | true/false - true/false 36 | strings - binaries 37 | numbers - floats 38 | tables - #table{} with array for keys 1..n, ordict for rest 39 | userdata - #userdata{} 40 | function - #function{} or {function,Fun} 41 | thread - #thread{} 42 | 43 | See luerl.hrl for the field names of the records. 44 | 45 | All tables are combinations of ttdicts and arrays. In each table an 46 | array is used for integer keys >= 1 while an ttdict is used for all 47 | other keys. We use this information when building/processing 48 | tables. Seems like Lua internally stores elements as a (unordered) 49 | sequence of key-value elements, except maybe for the table part. Some 50 | tests show that using ttdicts for all elements results the system 51 | being 10-20% slower and using more memory. 52 | 53 | So using the array module for positive integer keys seems a reasonable 54 | choice. Direct read/write access is fast, but "shifting" access for 55 | example in table.insert/remove is not that efficient. Most table 56 | functions work after a fashion even in the "undefined" case all the 57 | elements don't have keys only in 1..n, but it seems like the order in 58 | which elements were added affects the outcome. We don't have any such 59 | information available. We do try to do something reasonable that sort 60 | of mirrors the Lua functions. Should we or should we be strict? 61 | 62 | The table table can be either an ordict, an array, use the process 63 | dictionary, or an ETS table; these are accessed through macros. To use 64 | ETS would need a bigger change as copying the whole table for each 65 | access would be very inefficient. Either use bags and have one per 66 | table or use sets and have the ETS key as {Tab,Key}. 67 | 68 | Machine 69 | ------- 70 | 71 | The VM is a hybrid. It uses normal Erlang function calls for Luerl 72 | calls and blocks and has a small instruction set for operations inside 73 | a block. This should make it not too difficult to compile down to 74 | straight Erlang in the future. 75 | 76 | Blocks keep variables in tuples. There are two variable types 77 | depending on how they are defined: 78 | 79 | - Local variables that are used in this block and sub-blocks, but not 80 | used in any functions defined in the blocks. These are kept in a 81 | stack of tuples, the LocalVars or Lvs, and referenced by offset in stack 82 | and offset in tuple. 83 | 84 | - Environment variables that are defined in functions which are 85 | defined in this block or in sub-blocks. This mean they must be kept 86 | around as long as the functions are alive and are stored in the 87 | global heap as each invocation can modify them. They are kept in a 88 | stack of references, the EnvironmentVars or Evs, to tuples in the 89 | global heap and referenced by offset in stack and offset in tuple. 90 | 91 | A function contains a reference to the stack of environment variables 92 | which existed when it was created. Note that the mutable nature of Lua 93 | data means that these can be modified and the changes must be visible 94 | to every function which references them. 95 | 96 | There is also a stack containing arguments and temporary values. This 97 | is stack is "global" in the sense that it is passed through all calls 98 | and blocks. It is also passed as an argument into functions 99 | implemented in Erlang. This is so that event of a Lua/Luerl GC the 100 | collector uses the stack to determine which data in the global heap is 101 | to be saved. 102 | 103 | The VM is a pure stack machine. 104 | 105 | To handle multiple return values we always return a list of values. 106 | The only place this is not done is in luerl_eval.erl when getting 107 | values from the environment where we can only have one value. This 108 | means a lot of calls to first_value/1 in luerl_emul.erl, but the 109 | consistency is worth it. 110 | 111 | Similarly all the arguments in a function call are passed in a list. 112 | The function then unpacks the list into its arguments, including 113 | '...'. 114 | 115 | All of the predefined libraries have an install/1 function. This is 116 | called when initialising Luerl; it does any library specific 117 | initialisation necessary and returns a table containing the functions 118 | in the library. 119 | 120 | We create a unique tag which is saved in the environment. This is used 121 | so we can implement 'break' with a simple throw. The thrown value 122 | includes the tag so we can uniquely catch it and not get confused with 123 | a throw/error/exit from the erlang code. 124 | 125 | Compiler 126 | -------- 127 | 128 | The compiler has state at different levels: 129 | 130 | - In luerl_comp there is #comp{} containing code, options and errors. 131 | - In the #cst{} between the compiler modules for data outside the 132 | code. This empty so far. 133 | - Inside and local to the compiler modules. 134 | 135 | All the compiler modules are written so that they chain a status 136 | argument through their code, even if it not used. When they are not 137 | used we just send the atom 'nil' through and check it comes out "the 138 | other end". 139 | 140 | Lua implementation "features" 141 | ----------------------------- 142 | 143 | When "integers" are wanted then float input values are often "rounded" 144 | to the correct float value. So 1.3 --> 1.0 and 3.7 --> 4.0. 145 | -------------------------------------------------------------------------------- /src/luerl.app.src: -------------------------------------------------------------------------------- 1 | %% -*- mode: erlang; indent-tabs-mode: nil -*- 2 | %% Copyright (c) 2013-2024 Robert Virding 3 | %% 4 | %% Licensed under the Apache License, Version 2.0 (the "License"); 5 | %% you may not use this file except in compliance with the License. 6 | %% You may obtain a copy of the License at 7 | %% 8 | %% http://www.apache.org/licenses/LICENSE-2.0 9 | %% 10 | %% Unless required by applicable law or agreed to in writing, software 11 | %% distributed under the License is distributed on an "AS IS" BASIS, 12 | %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | %% See the License for the specific language governing permissions and 14 | %% limitations under the License. 15 | 16 | {application, luerl, 17 | [{description, "Luerl - an implementation of Lua on Erlang"}, 18 | {vsn, "1.4.1"}, 19 | {modules, []}, 20 | {registered, []}, 21 | {applications, [kernel, stdlib]}, 22 | {env, []}, 23 | {mod, {luerl_app, []}}, 24 | 25 | %% Project metadata 26 | {licenses, ["Apache-2.0"]}, 27 | {links, [{"Github", "https://github.com/rvirding/luerl"}]}, 28 | %% This is used for hex packages. 29 | {files, ["README.md", "LICENSE", "VERSION", "src", "doc", "ebin/luerl.app", 30 | "include", "rebar.*", "*akefile", 31 | "*.escript"]}, 32 | {exclude_files, ["priv/images/*"]} 33 | ]}. 34 | -------------------------------------------------------------------------------- /src/luerl_anno.erl: -------------------------------------------------------------------------------- 1 | %% Copyright (c) 2019 Robert Virding 2 | %% 3 | %% Licensed under the Apache License, Version 2.0 (the "License"); 4 | %% you may not use this file except in compliance with the License. 5 | %% You may obtain a copy of the License at 6 | %% 7 | %% http://www.apache.org/licenses/LICENSE-2.0 8 | %% 9 | %% Unless required by applicable law or agreed to in writing, software 10 | %% distributed under the License is distributed on an "AS IS" BASIS, 11 | %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | %% See the License for the specific language governing permissions and 13 | %% limitations under the License. 14 | 15 | %% File : luerl_anno.erl 16 | %% Author : Robert Virding 17 | %% Purpose : Handle annotations in the Luerl abstract code. 18 | 19 | %% We keep the same standard as in the Erlang AST: 20 | %% 21 | %% - annotations with just the line number can be just the integer. 22 | %% - in an aonnotation list the line number is just an integer while 23 | %% all other annotations have the format {Key,Value}. 24 | 25 | -module(luerl_anno). 26 | 27 | -include("luerl.hrl"). 28 | 29 | ?MODULEDOC(false). 30 | 31 | -export([new/0,new/1,new/2,set_line/2,line/1,set/3,get/2]). 32 | 33 | %% new() -> Anno. 34 | %% new(Line) -> Anno. 35 | %% new(Key, Val) -> Anno. 36 | %% Create an empty annotation, one containing Line and one containing 37 | %% a general Key/Val. 38 | 39 | new() -> []. 40 | 41 | new(Line) -> Line. 42 | 43 | new(Key, Val) -> set(Key, Val, new()). 44 | 45 | %% set_line(Line, Anno) -> Anno. 46 | %% line(Anno) -> Line | undefined. 47 | %% Specific functions for accessing line numbers in the anno. 48 | 49 | set_line(Line, Anno) when is_integer(Anno) -> Line; 50 | set_line(Line, Anno) -> set_line1(Line, Anno). 51 | 52 | set_line1(Line, [Old|Anno]) when is_integer(Old) -> [Line|Anno]; 53 | set_line1(Line, [A|Anno]) -> 54 | [A|set_line1(Line, Anno)]; 55 | set_line1(Line, []) -> [Line]. 56 | 57 | line(Anno) when is_integer(Anno) -> Anno; 58 | line(Anno) -> line1(Anno). 59 | 60 | line1([Line|_]) when is_integer(Line) -> Line; 61 | line1([_|Anno]) -> line1(Anno); 62 | line1([]) -> undefined. 63 | 64 | %% set(Key, Value, Anno) -> Anno. 65 | %% get(Key, Anno) -> Value | undefined. 66 | %% Generic accessing functions for the anno. 67 | 68 | set(line, Val, Anno) -> set_line(Val, Anno); 69 | set(Key, Val, Anno) when is_integer(Anno) -> 70 | [Anno,{Key,Val}]; 71 | set(Key, Val, Anno) -> set1(Key, Val, Anno). 72 | 73 | set1(Key, Val, [{Key,_Old}|Anno]) -> 74 | [{Key,Val}|Anno]; 75 | set1(Key, Val, [A|Anno]) -> 76 | [A|set1(Key, Val, Anno)]; 77 | set1(Key, Val, []) -> 78 | [{Key,Val}]. 79 | 80 | get(line, Anno) -> line(Anno); %This is untagged 81 | get(_Key, Anno) when is_integer(Anno) -> %This is untagged so not Key 82 | undefined; 83 | get(Key, Anno) -> get1(Key, Anno). 84 | 85 | get1(Key, [{Key,Val}|_Anno]) -> Val; 86 | get1(Key, [_|Anno]) -> get1(Key, Anno); 87 | get1(_Key, []) -> undefined. 88 | -------------------------------------------------------------------------------- /src/luerl_app.erl: -------------------------------------------------------------------------------- 1 | %% Copyright (c) 2013-2021 Robert Virding 2 | %% 3 | %% Licensed under the Apache License, Version 2.0 (the "License"); 4 | %% you may not use this file except in compliance with the License. 5 | %% You may obtain a copy of the License at 6 | %% 7 | %% http://www.apache.org/licenses/LICENSE-2.0 8 | %% 9 | %% Unless required by applicable law or agreed to in writing, software 10 | %% distributed under the License is distributed on an "AS IS" BASIS, 11 | %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | %% See the License for the specific language governing permissions and 13 | %% limitations under the License. 14 | 15 | -module(luerl_app). 16 | 17 | -include("luerl.hrl"). 18 | 19 | ?MODULEDOC(false). 20 | 21 | -behaviour(application). 22 | 23 | %% Application callbacks 24 | -export([start/2, stop/1]). 25 | 26 | %% =================================================================== 27 | %% Application callbacks 28 | %% =================================================================== 29 | 30 | start(_StartType, _StartArgs) -> 31 | luerl_sup:start_link(). 32 | 33 | stop(_State) -> 34 | ok. 35 | -------------------------------------------------------------------------------- /src/luerl_comp.hrl: -------------------------------------------------------------------------------- 1 | %% Copyright (c) 2013-2019 Robert Virding 2 | %% 3 | %% Licensed under the Apache License, Version 2.0 (the "License"); 4 | %% you may not use this file except in compliance with the License. 5 | %% You may obtain a copy of the License at 6 | %% 7 | %% http://www.apache.org/licenses/LICENSE-2.0 8 | %% 9 | %% Unless required by applicable law or agreed to in writing, software 10 | %% distributed under the License is distributed on an "AS IS" BASIS, 11 | %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | %% See the License for the specific language governing permissions and 13 | %% limitations under the License. 14 | 15 | %% File : luerl_comp.hrl 16 | %% Author : Robert Virding 17 | %% Purpose : Internal LUA 5.2 compiler definitions. 18 | 19 | %% Common compiler information 20 | 21 | -record(cinfo, {lfile=[], %Lua file name 22 | vfile=[], %Virtual file name 23 | opts=[] %Compiler options 24 | }). 25 | 26 | %% Some useful macros. 27 | -define(IF(Test,True,False), case Test of true -> True; false -> False end). 28 | -define(WHEN_OPT(Opt,Opts,Fun), ?IF(member(Opt, Opts), Fun(), ok)). 29 | -define(DEBUG_PRINT(Format,Args,Opts), 30 | ?WHEN_OPT(debug_print, Opts, 31 | fun () -> io:fwrite(Format, Args) end)). 32 | 33 | %% Variable data. 34 | -record(vars, {local=[], %Local variables 35 | free=[], %Free variables 36 | used=[], %Used in sub blocks 37 | fused=[] %Used in sub-functions 38 | }). 39 | 40 | %% Define internal data macros. 41 | 42 | %% Statements. 43 | %% The line number here, 'l', can be a line number or annotation list. 44 | 45 | -record(assign_stmt, {l,vars,exps}). 46 | 47 | -record(call_stmt, {l,call}). 48 | 49 | -record(return_stmt, {l,exps}). 50 | 51 | -record(break_stmt, {l}). 52 | 53 | -record(block_stmt, {l, 54 | body=[], %Block body statements 55 | vars=none, %Variable info 56 | lsz=none, %Local frame size 57 | loc=not_used, %Local var block template 58 | esz=none, %Env frame size 59 | env=not_used, %Local env block template 60 | %%local=none, %Local variables 61 | locf=false}). %Local functions 62 | 63 | -record(while_stmt, {l,exp,body=[]}). 64 | 65 | -record(repeat_stmt, {l,body=[]}). 66 | 67 | -record(nfor_stmt, {l, 68 | var, %Loop variable 69 | init,limit,step, %The init, limit, step values 70 | body=[]}). %Loop body 71 | 72 | -record(gfor_stmt, {l, 73 | vars, %Loop variables 74 | gens, %Generators 75 | body=[]}). %Loop body 76 | 77 | -record(if_stmt, {l,tests=[],else_block}). 78 | 79 | -record(local_assign_stmt, {l,vars,exps}). 80 | 81 | -record(local_fdef_stmt, {l,var,func}). 82 | 83 | -record(expr_stmt, {l,exp}). %Pseudo stmt for expressions 84 | 85 | -record(block, {l, 86 | body=[], %Block body statements 87 | vars=none, %Variable info 88 | lsz=none, %Local frame size 89 | loc=not_used, %Local var block template 90 | esz=none, %Env frame size 91 | env=not_used, %Local env block template 92 | locf=false}). 93 | 94 | %% Expressions. 95 | %% The line number here, 'l', can be a line number or annotation list. 96 | 97 | -record(fdef, {l, 98 | pars=[], %Parameters 99 | body=[], %Function body statements 100 | vars=none, %Variable info 101 | lsz=none, %Local frame size 102 | loc=not_used, %Local var block template 103 | esz=none, %Env frame size 104 | env=not_used, %Local env block template 105 | %%local=none, %Local variables 106 | locf=false}). %Local function 107 | 108 | -record(lit, {l,val}). %Literal value 109 | 110 | -record(op, {l,op,args=[]}). 111 | 112 | -record(dot, {l,exp,rest}). 113 | 114 | -record(single, {l,exp}). 115 | 116 | -record(var, {l,name}). 117 | 118 | -record(fcall, {l,args=[]}). %Function call 119 | 120 | -record(mcall, {l,meth,args=[]}). %Method call 121 | 122 | -record(key, {l,key}). 123 | 124 | -record(tabcon, {l,fields=[]}). %Table constructor 125 | 126 | -record(efield, {l,val}). 127 | 128 | -record(kfield, {l,key,val}). 129 | 130 | %% Variable types. 131 | %% The line number here, 'l', can be a line number or annotation list. 132 | 133 | -record(lvar, {l,n,d,i}). %Local name, depth, index 134 | -record(evar, {l,n,d,i}). %Environment name, depth, index 135 | -record(gvar, {l,n}). %Global name 136 | -------------------------------------------------------------------------------- /src/luerl_comp_peep.erl: -------------------------------------------------------------------------------- 1 | %% Copyright (c) 2013-2019 Robert Virding 2 | %% 3 | %% Licensed under the Apache License, Version 2.0 (the "License"); 4 | %% you may not use this file except in compliance with the License. 5 | %% You may obtain a copy of the License at 6 | %% 7 | %% http://www.apache.org/licenses/LICENSE-2.0 8 | %% 9 | %% Unless required by applicable law or agreed to in writing, software 10 | %% distributed under the License is distributed on an "AS IS" BASIS, 11 | %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | %% See the License for the specific language governing permissions and 13 | %% limitations under the License. 14 | 15 | %% File : luerl_comp_peep.erl 16 | %% Author : Robert Virding 17 | %% Purpose : A basic LUA 5.3 compiler for Luerl. 18 | 19 | %% Does peep-hole optimisation in the compiler. 20 | 21 | -module(luerl_comp_peep). 22 | 23 | 24 | -include("luerl.hrl"). 25 | -include("luerl_comp.hrl"). 26 | -include("luerl_instrs.hrl"). 27 | 28 | ?MODULEDOC(false). 29 | 30 | -export([chunk/2]). 31 | 32 | %% chunk(Code, CompInfo) -> {ok,Code}. 33 | %% A chunk is now a list of instructions to define the function. 34 | 35 | chunk(Is0, #cinfo{opts=Opts}=_Ci) -> 36 | Is1 = instrs(Is0, nil), %No local state 37 | luerl_comp:debug_print(Opts, "cp: ~p\n", [Is1]), 38 | {ok,Is1}. 39 | 40 | %% Combining instructions. 41 | instrs([?PUSH_LIT(L),?GET_KEY|Is], St) -> 42 | instrs([?GET_LIT_KEY(L)|Is], St); 43 | instrs([?PUSH_LIT(L),?SET_KEY|Is], St) -> 44 | instrs([?SET_LIT_KEY(L)|Is], St); 45 | 46 | %% Must check these properly, probably seldom used anyway. 47 | %% instrs([?STORE_EVAR(D, I),?PUSH_EVAR(D, I)|Is], St) -> 48 | %% instrs([?DUP,?STORE_EVAR(D, I)|Is], St); 49 | %% instrs([?STORE_LVAR(D, I),?PUSH_LVAR(D, I)|Is], St) -> 50 | %% instrs([?DUP,?STORE_LVAR(D, I)|Is], St); 51 | %% instrs([?STORE_GVAR(K),?PUSH_GVAR(K)|Is], St) -> 52 | %% instrs([?DUP,?STORE_EVAR(D, I)|Is], St); 53 | 54 | instrs([?PUSH_LIT(L),?MULTIPLE|Is], St) -> 55 | instrs([?PUSH_LAST_LIT(L)|Is], St); 56 | instrs([?PUSH_LVAR(D, I),?MULTIPLE|Is], St) -> 57 | instrs([?PUSH_LAST_LVAR(D, I)|Is], St); 58 | instrs([?PUSH_EVAR(D, I),?MULTIPLE|Is], St) -> 59 | instrs([?PUSH_LAST_EVAR(D, I)|Is], St); 60 | instrs([?PUSH_GVAR(K),?MULTIPLE|Is], St) -> 61 | instrs([?PUSH_LAST_GVAR(K)|Is], St); 62 | 63 | instrs([?POP,?POP|Is], St) -> 64 | instrs([?POP2|Is], St); 65 | 66 | %% Doing sub instructions. 67 | instrs([?PUSH_FDEF(Anno,Lsz,Esz,Pars,Fis0)|Is], St) -> 68 | Fis1 = instrs(Fis0, St), 69 | [?PUSH_FDEF(Anno,Lsz,Esz,Pars,Fis1)|instrs(Is, St)]; 70 | instrs([?BLOCK(Lsz,Esz,Bis0)|Is], St) -> 71 | Bis1 = instrs(Bis0, St), 72 | [?BLOCK(Lsz,Esz,Bis1)|instrs(Is, St)]; 73 | instrs([?REPEAT(Ris0)|Is], St) -> 74 | Ris1 = instrs(Ris0, St), 75 | [?REPEAT(Ris1)|instrs(Is, St)]; 76 | instrs([?WHILE(Eis0, Wis0)|Is], St) -> 77 | Eis1 = instrs(Eis0, St), 78 | Wis1 = instrs(Wis0, St), 79 | [?WHILE(Eis1, Wis1)|instrs(Is, St)]; 80 | instrs([?AND_THEN(Tis0)|Is], St) -> 81 | Tis1 = instrs(Tis0, St), 82 | [?AND_THEN(Tis1)|instrs(Is, St)]; 83 | instrs([?OR_ELSE(Fis0)|Is], St) -> 84 | Fis1 = instrs(Fis0, St), 85 | [?OR_ELSE(Fis1)|instrs(Is, St)]; 86 | instrs([?IF_TRUE(Tis0)|Is], St) -> 87 | Tis1 = instrs(Tis0, St), 88 | [?IF_TRUE(Tis1)|instrs(Is, St)]; 89 | instrs([?IF(Tis, [])|Is], St) -> 90 | instrs([?IF_TRUE(Tis)|Is], St); 91 | instrs([?IF(Tis0, Fis0)|Is], St) -> 92 | Tis1 = instrs(Tis0, St), 93 | Fis1 = instrs(Fis0, St), 94 | [?IF(Tis1, Fis1)|instrs(Is, St)]; 95 | instrs([?NFOR(V, Fis0)|Is], St) -> 96 | Fis1 = instrs(Fis0, St), 97 | [?NFOR(V, Fis1)|instrs(Is, St)]; 98 | instrs([?GFOR(Vs, Fis0)|Is], St) -> 99 | Fis1 = instrs(Fis0, St), 100 | [?GFOR(Vs, Fis1)|instrs(Is, St)]; 101 | 102 | %% Tail calls for when they are implemented in the VM. 103 | %% instrs([?FCALL,?POP], _St) -> [?TAIL_FCALL]; 104 | %% instrs([?FCALL,?RETURN(_)|_], _St) -> [?TAIL_FCALL]; 105 | %% instrs([?MCALL(M),?POP], _St) -> [?TAIL_MCALL(M)]; 106 | %% instrs([?MCALL(M),?RETURN(_)|_], _St) -> [?TAIL_MCALL(M)]; 107 | 108 | %% Nothing to do. 109 | instrs([I|Is], St) -> [I|instrs(Is, St)]; 110 | instrs([], _) -> []. 111 | -------------------------------------------------------------------------------- /src/luerl_instrs.hrl: -------------------------------------------------------------------------------- 1 | %% Copyright (c) 2019 Robert Virding 2 | %% 3 | %% Licensed under the Apache License, Version 2.0 (the "License"); 4 | %% you may not use this file except in compliance with the License. 5 | %% You may obtain a copy of the License at 6 | %% 7 | %% http://www.apache.org/licenses/LICENSE-2.0 8 | %% 9 | %% Unless required by applicable law or agreed to in writing, software 10 | %% distributed under the License is distributed on an "AS IS" BASIS, 11 | %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | %% See the License for the specific language governing permissions and 13 | %% limitations under the License. 14 | 15 | %% File : luerl_instrs.hrl 16 | %% Author : Robert Virding 17 | %% Purpose : Internal LUA 5.3 instructions. 18 | 19 | %% Expression instructions. 20 | -define(PUSH_LIT(L), {push_lit,L}). 21 | -define(PUSH_LVAR(D,I), {push_lvar,D,I}). 22 | -define(PUSH_EVAR(D, I), {push_evar,D,I}). 23 | -define(PUSH_GVAR(K), {push_gvar,K}). 24 | 25 | -define(PUSH_LAST_LIT(L), {push_last_lit,L}). %[?PUSH_LIT,?MULTIPLE] 26 | -define(PUSH_LAST_LVAR(D,I), {push_last_lvar,D,I}). 27 | -define(PUSH_LAST_EVAR(D, I), {push_last_evar,D,I}). 28 | -define(PUSH_LAST_GVAR(K), {push_last_gvar,K}). 29 | 30 | -define(STORE_LVAR(D, I), {store_lvar,D,I}). 31 | -define(STORE_EVAR(D, I), {store_evar,D,I}). 32 | -define(STORE_GVAR(K), {store_gvar,K}). 33 | 34 | -define(GET_KEY, get_key). %Acc = Stk[Acc] 35 | -define(GET_LIT_KEY(K), {get_lit_key,K}). %[?PUSH_LIT(K),?GET_KEY] 36 | -define(SET_KEY, set_key). %Stk[ 37 | -define(SET_LIT_KEY(K), {set_lit_key,K}). %[?PUSH_LIT(K),?SET_KEY] 38 | 39 | -define(SINGLE, single). %Ensure single value 40 | -define(MULTIPLE, multiple). %Ensure multiple value 41 | 42 | -define(BUILD_TAB(Fc, I), {build_tab,Fc,I}). 43 | -define(FCALL, fcall). 44 | -define(TAIL_FCALL, tail_fcall). 45 | -define(MCALL(M), {mcall,M}). 46 | -define(TAIL_MCALL(M), {tail_mcall,M}). 47 | -define(OP(Op,Ac), {op,Op,Ac}). 48 | -define(PUSH_FDEF(Anno, Lsz, Esz, Pars, Is), 49 | {push_fdef,Anno,Lsz,Esz,Pars,Is}). 50 | -define(PUSH_FDEF(FnRef), {push_fdef,FnRef}). 51 | 52 | %% Control instructions. 53 | -define(BLOCK(Lsz, Esz, Is), {block,Lsz,Esz,Is}). 54 | -define(BLOCK_OPEN(Lsz, Esz), {block_open,Lsz,Esz}). 55 | -define(BLOCK_CLOSE, block_close). 56 | -define(WHILE(E, B), {while,E,B}). 57 | -define(WHILE_LOOP(Eis, Wis), {while_loop,Eis,Wis}). 58 | -define(REPEAT(B), {repeat,B}). 59 | -define(REPEAT_LOOP(B), {repeat_loop,B}). 60 | -define(AND_THEN(T), {and_then,T}). 61 | -define(OR_ELSE(T), {or_else,T}). 62 | -define(IF_TRUE(T), {if_true,T}). 63 | -define(IF(T, F), {'if',T,F}). 64 | -define(NFOR(V, B), {nfor,V,B}). 65 | -define(NFOR_LOOP(N, Limit, Step, Fis), {nfor_loop,N,Limit,Step,Fis}). 66 | -define(GFOR(Vs, B), {gfor,Vs,B}). 67 | -define(GFOR_CALL(Func, Data, Val, Fis), {gfor_call,Func,Data,Val,Fis}). 68 | -define(GFOR_LOOP(Func, Data, Fis), {gfor_loop,Func,Data,Fis}). 69 | -define(BREAK, break). 70 | -define(RETURN(Ac), {return,Ac}). 71 | 72 | %% Stack instructions. 73 | -define(PUSH, push). 74 | -define(POP, pop). 75 | -define(POP2, pop2). 76 | -define(SWAP, swap). 77 | -define(DUP, dup). 78 | -define(PUSH_VALS(Vc), {push_vals,Vc}). 79 | -define(POP_VALS(Vc), {pop_vals,Vc}). 80 | -define(POP_ARGS(Ac), {pop_args,Ac}). 81 | -define(PUSH_ARGS(Al), {push_args,Al}). 82 | 83 | %% Comment and line instructions. 84 | -define(COMMENT(C), {comment,C}). 85 | -define(CURRENT_LINE(L, File), {current_line,L,File}). 86 | -------------------------------------------------------------------------------- /src/luerl_lib_bit32.erl: -------------------------------------------------------------------------------- 1 | %% Copyright (c) 2014-2018 Łukasz Biedrycki 2 | %% 3 | %% Licensed under the Apache License, Version 2.0 (the "License"); 4 | %% you may not use this file except in compliance with the License. 5 | %% You may obtain a copy of the License at 6 | %% 7 | %% http://www.apache.org/licenses/LICENSE-2.0 8 | %% 9 | %% Unless required by applicable law or agreed to in writing, software 10 | %% distributed under the License is distributed on an "AS IS" BASIS, 11 | %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | %% See the License for the specific language governing permissions and 13 | %% limitations under the License. 14 | 15 | %% File : luerl_lib_bit32.erl 16 | %% Author : Łukasz Biedrycki 17 | %% Purpose : The bit32 library for Luerl. 18 | 19 | %% This library has been deprecated in 5.3 but we still keep it. 20 | 21 | -module(luerl_lib_bit32). 22 | 23 | -include("luerl.hrl"). 24 | 25 | ?MODULEDOC(false). 26 | 27 | -export([install/1,fband/3,fbnot/3,fbor/3,fbtest/3,fbxor/3,flshift/3,frshift/3, 28 | farshift/3,flrotate/3,frrotate/3,fextract/3,freplace/3]). 29 | 30 | -import(luerl_lib, [badarg_error/3]). %Shorten this 31 | 32 | -define(MOST_SIGNIFICANT, 16#80000000). 33 | -define(LEAST_SIGNIFICANT, 16#00000001). 34 | -define(DEFAULT_BAND, 4294967295). 35 | -define(DEFAULT_BOR, 0). 36 | -define(DEFAULT_BXOR, 0). 37 | 38 | install(St) -> 39 | luerl_heap:alloc_table(table(), St). 40 | 41 | table() -> 42 | [{<<"band">>,#erl_mfa{m=?MODULE,f=fband}}, 43 | {<<"bnot">>,#erl_mfa{m=?MODULE,f=fbnot}}, 44 | {<<"bor">>,#erl_mfa{m=?MODULE,f=fbor}}, 45 | {<<"btest">>,#erl_mfa{m=?MODULE,f=fbtest}}, 46 | {<<"bxor">>,#erl_mfa{m=?MODULE,f=fbxor}}, 47 | {<<"lshift">>,#erl_mfa{m=?MODULE,f=flshift}}, 48 | {<<"rshift">>,#erl_mfa{m=?MODULE,f=frshift}}, 49 | {<<"arshift">>,#erl_mfa{m=?MODULE,f=farshift}}, 50 | {<<"lrotate">>,#erl_mfa{m=?MODULE,f=flrotate}}, 51 | {<<"rrotate">>,#erl_mfa{m=?MODULE,f=frrotate}}, 52 | {<<"extract">>,#erl_mfa{m=?MODULE,f=fextract}}, 53 | {<<"replace">>,#erl_mfa{m=?MODULE,f=freplace}} 54 | ]. 55 | 56 | fband(_, As, St) -> 57 | case luerl_lib:args_to_integers(As) of 58 | L when is_list(L) -> {[aband(L)], St}; 59 | error -> badarg_error('band', As, St) 60 | end. 61 | 62 | aband([]) -> ?DEFAULT_BAND; 63 | aband([X|T]) -> aband(T, checkint32(X)). 64 | 65 | aband([], A) -> float(A); 66 | aband([X|T], A) -> aband(T, checkint32(X) band A). 67 | 68 | fbnot(_, As, St) -> 69 | case luerl_lib:args_to_integers(As) of 70 | [N|_] -> 71 | NotN = bnot checkint32(N), 72 | {[float(NotN)], St}; 73 | error -> badarg_error('bnot', As, St) 74 | end. 75 | 76 | fbor(_, As, St) -> 77 | case luerl_lib:args_to_integers(As) of 78 | L when is_list(L) -> {[abor(L)], St}; 79 | error -> badarg_error('bor', As, St) 80 | end. 81 | 82 | abor([]) -> ?DEFAULT_BOR; 83 | abor([X|T]) -> abor(T, checkint32(X)). 84 | 85 | abor([], A) -> float(A); 86 | abor([X|T], A) -> abor(T, checkint32(X) bor A). 87 | 88 | fbtest(_, As, St) -> 89 | case luerl_lib:args_to_integers(As) of 90 | L when is_list(L) -> {[aband(L) /= 0], St}; 91 | error -> badarg_error('btest', As, St) 92 | end. 93 | 94 | fbxor(_, As, St) -> 95 | case luerl_lib:args_to_integers(As) of 96 | L when is_list(L) -> {[abxor(L)], St}; 97 | error -> badarg_error('bxor', As, St) 98 | end. 99 | 100 | abxor([]) -> ?DEFAULT_BXOR; 101 | abxor([X|T]) -> abxor(T, checkint32(X)). 102 | 103 | abxor([], A) -> float(A); 104 | abxor([X|T], A) -> abxor(T, checkint32(X) bxor A). 105 | 106 | flshift(_, As, St) -> 107 | case luerl_lib:args_to_integers(As) of 108 | [X,Y|_] -> {[float(checkint32(X) bsl trunc(Y))], St}; 109 | _ -> badarg_error('lshift', As, St) 110 | end. 111 | 112 | frshift(_, As, St) -> 113 | case luerl_lib:args_to_integers(As) of 114 | [X,Y|_] -> {[float(checkint32(X) bsr trunc(Y))], St}; 115 | _ -> badarg_error('rshift', As, St) 116 | end. 117 | 118 | farshift(_, As, St) -> 119 | case luerl_lib:args_to_integers(As) of 120 | [X,Y|_] -> 121 | Disp = trunc(Y), 122 | case Disp > 0 of 123 | true -> {[float(checkint32(X) bsr trunc(Y))], St}; 124 | false -> {[float(checkint32(X) bsl abs(trunc(Y)))], St} 125 | end; 126 | _ -> badarg_error('arshift', As, St) 127 | end. 128 | 129 | flrotate(_, As, St) -> 130 | case luerl_lib:args_to_integers(As) of 131 | [X,Y|_] -> {[float(lrotate(checkint32(X), trunc(Y)))], St}; 132 | _ -> badarg_error('lrotate', As, St) 133 | end. 134 | 135 | frrotate(_, As, St) -> 136 | case luerl_lib:args_to_integers(As) of 137 | [X,Y|_] -> {[float(rrotate(checkint32(X), trunc(Y)))], St}; 138 | _ -> badarg_error('rrotate', As, St) 139 | end. 140 | 141 | fextract(_, As, St) -> 142 | case luerl_lib:args_to_integers(As) of 143 | [N,Field,Width|_] -> 144 | {[float(extract(N, Field, Width, As, St))], St}; 145 | [N,Field|_] -> 146 | {[float(extract(N, Field, 1, As, St))], St}; 147 | _ -> badarg_error('extract', As, St) 148 | end. 149 | 150 | freplace(_, As, St) -> 151 | case luerl_lib:args_to_integers(As) of 152 | [N,V,Field,Width|_] -> 153 | {[float(replace(N, V, Field, Width, As, St))], St}; 154 | [N,V,Field|_] -> 155 | {[float(replace(N, V, Field, 1, As, St))], St}; 156 | _ -> badarg_error('replace', As, St) 157 | end. 158 | 159 | 160 | %% Internal 161 | lrotate(X, Y) when Y < 0 -> 162 | rrotate(X, abs(Y)); 163 | lrotate(X, Y) when Y == 0 -> 164 | X; 165 | lrotate(X1, Y) -> 166 | Most = X1 band ?MOST_SIGNIFICANT, 167 | X2 = uint32(X1 bsl 1), 168 | X3 = X2 bor (Most bsr 31), 169 | lrotate(X3, Y - 1). 170 | 171 | rrotate(X, Y) when Y < 0 -> 172 | lrotate(X, abs(Y)); 173 | rrotate(X, Y) when Y == 0 -> 174 | X; 175 | rrotate(X1, Y) -> 176 | Least = X1 band ?LEAST_SIGNIFICANT, 177 | X2 = X1 bsr 1, 178 | X3 = X2 bor (Least bsl 31), 179 | rrotate(X3, Y - 1). 180 | 181 | uint32(N) -> 182 | <> = <>, 183 | Res. 184 | 185 | checkint32(N) -> 186 | uint32(trunc(N)). 187 | 188 | ge0(N, Where, As, St) -> 189 | case N >= 0 of 190 | true -> N; 191 | false -> badarg_error(Where, As, St) 192 | end. 193 | 194 | gt0(N, Where, As, St) -> 195 | case N > 0 of 196 | true -> N; 197 | false -> badarg_error(Where, As, St) 198 | end. 199 | 200 | le(N, V, Where, As, St) -> 201 | case N =< V of 202 | true -> N; 203 | false -> badarg_error(Where, As, St) 204 | end. 205 | 206 | extract(N1, Field1, Width1, As, St) -> 207 | N2 = checkint32(N1), 208 | Field2 = trunc(Field1), 209 | Width2 = trunc(Width1), 210 | _ = ge0(Field2, 'extract', As, St), 211 | _ = gt0(Width2, 'extract', As, St), 212 | _ = le(Field2 + Width2, 32, 'extract', As, St), 213 | trunc(N2 / math:pow(2, Field2)) rem trunc(math:pow(2, Width2)). 214 | 215 | replace(N1, V1, Field1, Width1, As, St) -> 216 | N2 = checkint32(N1), 217 | V2 = checkint32(V1), 218 | Field2 = trunc(Field1), 219 | Width2 = trunc(Width1), 220 | _ = ge0(Field2, 'replace', As, St), 221 | _ = gt0(Width2, 'replace', As, St), 222 | _ = le(Field2 + Width2, 32, 'extract', As, St), 223 | Field3 = trunc(math:pow(2, Field2)), 224 | Width3 = trunc(math:pow(2, Width2)), 225 | FW = Field3 * Width3, 226 | (N2 rem Field3) + 227 | (V2 rem Width3) * Field3 + trunc(N2 div FW) * FW. 228 | -------------------------------------------------------------------------------- /src/luerl_lib_debug.erl: -------------------------------------------------------------------------------- 1 | %% Copyright (c) 2015-2020 Robert Virding 2 | %% 3 | %% Licensed under the Apache License, Version 2.0 (the "License"); 4 | %% you may not use this file except in compliance with the License. 5 | %% You may obtain a copy of the License at 6 | %% 7 | %% http://www.apache.org/licenses/LICENSE-2.0 8 | %% 9 | %% Unless required by applicable law or agreed to in writing, software 10 | %% distributed under the License is distributed on an "AS IS" BASIS, 11 | %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | %% See the License for the specific language governing permissions and 13 | %% limitations under the License. 14 | 15 | %% File : luerl_lib_debug.erl 16 | %% Author : Robert Virding 17 | %% Purpose : The debug library for Luerl. 18 | 19 | %% This is a very rudimentary debug module which contains those 20 | %% functions which need no detailed information about the internals. 21 | 22 | -module(luerl_lib_debug). 23 | 24 | -include("luerl.hrl"). 25 | 26 | ?MODULEDOC(false). 27 | 28 | %% The basic entry point to set up the function table. 29 | -export([install/1,getmetatable/3,getuservalue/3,setmetatable/3,setuservalue/3]). 30 | 31 | -import(luerl_lib, [lua_error/2,badarg_error/3]). %Shorten this 32 | 33 | install(St) -> 34 | luerl_heap:alloc_table(table(), St). 35 | 36 | %% table() -> [{FuncName,Function}]. 37 | 38 | table() -> 39 | [{<<"getmetatable">>,#erl_mfa{m=?MODULE,f=getmetatable}}, 40 | {<<"getuservalue">>,#erl_mfa{m=?MODULE,f=getuservalue}}, 41 | {<<"setmetatable">>,#erl_mfa{m=?MODULE,f=setmetatable}}, 42 | {<<"setuservalue">>,#erl_mfa{m=?MODULE,f=setuservalue}} 43 | ]. 44 | 45 | %% getmetatable([Value|_], State) -> {[Table],State}. 46 | %% setmetatable([Table,Table|nil|_], State) -> {[Table],State}. 47 | %% Can set the metatable of all types here. Return tables for all 48 | %% values, for tables and userdata it is the table of the object, 49 | %% else the metatable for the type. 50 | 51 | getmetatable(_, [O|_], St) -> 52 | {[luerl_heap:get_metatable(O, St)],St}; 53 | getmetatable(_, As, St) -> badarg_error(getmetatable, As, St). 54 | 55 | setmetatable(_, [T,M|_], St0) -> 56 | St1 = luerl_heap:set_metatable(T, M, St0), 57 | {[T],St1}; 58 | setmetatable(_, As, St) -> badarg_error(setmetatable, As, St). 59 | 60 | %% getuservalue([User|_], State) -> {[Value],State}. 61 | %% setuservalue([User,Value|_], State) -> {[User],State}. 62 | %% These are basically no-ops. 63 | 64 | getuservalue(_, [_|_], St) -> {[nil],St}; 65 | getuservalue(_, As, St) -> badarg_error(getuservalue, As, St). 66 | 67 | setuservalue(_, [U,_|_], St) -> {[U],St}; 68 | setuservalue(_, As, St) -> badarg_error(setuservalue, As, St). 69 | -------------------------------------------------------------------------------- /src/luerl_lib_io.erl: -------------------------------------------------------------------------------- 1 | %% Copyright (c) 2013-2020 Robert Virding 2 | %% 3 | %% Licensed under the Apache License, Version 2.0 (the "License"); 4 | %% you may not use this file except in compliance with the License. 5 | %% You may obtain a copy of the License at 6 | %% 7 | %% http://www.apache.org/licenses/LICENSE-2.0 8 | %% 9 | %% Unless required by applicable law or agreed to in writing, software 10 | %% distributed under the License is distributed on an "AS IS" BASIS, 11 | %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | %% See the License for the specific language governing permissions and 13 | %% limitations under the License. 14 | 15 | %% File : luerl_lib_io.erl 16 | %% Author : Robert Virding 17 | %% Purpose : The io library for Luerl. 18 | 19 | %% This is a quick hack to get io working. It will be improved in time. 20 | 21 | -module(luerl_lib_io). 22 | 23 | -include("luerl.hrl"). 24 | 25 | ?MODULEDOC(false). 26 | 27 | -export([install/1,flush/3,write/3]). 28 | 29 | -import(luerl_lib, [lua_error/2,badarg_error/3]). %Shorten this 30 | 31 | install(St) -> 32 | luerl_heap:alloc_table(table(), St). 33 | 34 | %% table() -> [{FuncName,Function}]. 35 | 36 | table() -> 37 | [{<<"flush">>,#erl_mfa{m=?MODULE,f=flush}}, 38 | {<<"write">>,#erl_mfa{m=?MODULE,f=write}} 39 | ]. 40 | 41 | flush(_, _, St) -> {[true],St}. 42 | 43 | write(_, As, St) -> 44 | case luerl_lib:args_to_strings(As) of 45 | error -> badarg_error(write, As, St); 46 | Ss -> 47 | lists:foreach(fun (S) -> io:format("~s", [S]) end, Ss), 48 | {[#userdata{d=standard_io}],St} 49 | end. 50 | -------------------------------------------------------------------------------- /src/luerl_lib_utf8.erl: -------------------------------------------------------------------------------- 1 | %% Copyright (c) 2013-2020 Robert Virding 2 | %% 3 | %% Licensed under the Apache License, Version 2.0 (the "License"); 4 | %% you may not use this file except in compliance with the License. 5 | %% You may obtain a copy of the License at 6 | %% 7 | %% http://www.apache.org/licenses/LICENSE-2.0 8 | %% 9 | %% Unless required by applicable law or agreed to in writing, software 10 | %% distributed under the License is distributed on an "AS IS" BASIS, 11 | %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | %% See the License for the specific language governing permissions and 13 | %% limitations under the License. 14 | 15 | %% File : luerl_lib_utf8.erl 16 | %% Author : Robert Virding 17 | %% Purpose : The utf8 library for Luerl. 18 | 19 | -module(luerl_lib_utf8). 20 | 21 | -include("luerl.hrl"). 22 | 23 | ?MODULEDOC(false). 24 | 25 | -export([install/1,utf8_char/3,codes/3,codepoint/3,utf8_len/3,offset/3]). 26 | 27 | -import(luerl_lib, [lua_error/2,badarg_error/3]). %Shorten these 28 | 29 | install(St) -> 30 | luerl_heap:alloc_table(table(), St). 31 | 32 | table() -> 33 | [{<<"char">>,#erl_mfa{m=?MODULE,f=utf8_char}}, 34 | {<<"charpattern">>,<<"[\0-\x7F\xC2-\xF4][\x80-\xBF]*">>}, 35 | {<<"codes">>,#erl_mfa{m=?MODULE,f=codes}}, 36 | {<<"codepoint">>,#erl_mfa{m=?MODULE,f=codepoint}}, 37 | {<<"len">>,#erl_mfa{m=?MODULE,f=utf8_len}}, 38 | {<<"offset">>,#erl_mfa{m=?MODULE,f=offset}} 39 | ]. 40 | 41 | %% char(...) -> String. 42 | %% Receives zero or more integers, converts each one to its 43 | %% corresponding UTF-8 byte sequence and returns a string with the 44 | %% concatenation of all these sequences. 45 | 46 | utf8_char(_, As, St) -> 47 | case luerl_lib:args_to_integers(As) of 48 | Is when is_list(Is) -> 49 | Ss = << <> || I <- Is >>, 50 | {[Ss],St}; 51 | error -> badarg_error(char, As, St) 52 | end. 53 | 54 | %% len(...) -> Integer. 55 | %% Returns the number of UTF-8 characters in string s that start 56 | %% between positions i and j (both inclusive). The default for i is 1 57 | %% and for j is -1. If it finds any invalid byte sequence, returns a 58 | %% false value plus the position of the first invalid byte. 59 | 60 | utf8_len(_, As, St) -> 61 | {Str,I,J} = string_args(As, len, St), 62 | StrLen = byte_size(Str), 63 | Ret = if I > J -> [0]; %Do the same as Lua 64 | true -> 65 | Bin = binary_part(Str, I - 1, StrLen - I + 1), 66 | case bin_len(Bin, StrLen - J, 0) of 67 | {ok,Size} -> [Size]; 68 | {error,Rest} -> [nil,StrLen - byte_size(Rest) + 1] 69 | end 70 | end, 71 | {Ret,St}. 72 | 73 | bin_len(Bin, Last, N) when byte_size(Bin) =< Last -> {ok,N}; 74 | bin_len(Bin0, Last, N) -> 75 | try 76 | <<_/utf8,Bin1/binary>> = Bin0, 77 | bin_len(Bin1, Last, N+1) 78 | catch 79 | _:_ -> {error,Bin0} 80 | end. 81 | 82 | %% codepoint(...) -> [Integer]. 83 | %% Returns the codepoints (as integers) from all characters in s that 84 | %% start between byte position i and j (both included). The default 85 | %% for i is 1 and for j is i. It raises an error if it meets any 86 | %% invalid byte sequence. 87 | 88 | codepoint(_, As, St) -> 89 | {Str,I,J} = string_args(As, codepoint, St), 90 | StrLen = byte_size(Str), 91 | Ret = if I > J -> []; %Do the same as Lua 92 | true -> 93 | Bin = binary_part(Str, I - 1, StrLen - I + 1), 94 | case bin_codepoint(Bin, StrLen - J, []) of 95 | {ok,Cps} -> Cps; 96 | {error,_} -> badarg_error(codepoint, As, St) 97 | end 98 | end, 99 | {Ret,St}. 100 | 101 | bin_codepoint(Bin, Last, Cps) when byte_size(Bin) =< Last -> 102 | {ok,lists:reverse(Cps)}; 103 | bin_codepoint(Bin0, Last, Cps) -> 104 | try 105 | <> = Bin0, 106 | bin_codepoint(Bin1, Last, [C|Cps]) 107 | catch 108 | _:_ -> {error,Bin0} 109 | end. 110 | 111 | %% codes(String) -> [Fun,String,P]. 112 | 113 | codes(_, As, St) -> 114 | case luerl_lib:conv_list(As, [lua_string]) of 115 | error -> badarg_error(codes, As, St); 116 | [Str|_] -> {[#erl_func{code=fun codes_next/2},Str,0],St} 117 | end. 118 | 119 | codes_next([A], St) -> codes_next([A,0], St); 120 | codes_next([Str,P|_], St) when byte_size(Str) =< P -> {[nil],St}; 121 | codes_next([Str,P|_], St) when is_binary(Str) -> 122 | <<_:P/binary,C/utf8,Rest/binary>> = Str, 123 | P1 = byte_size(Str) - byte_size(Rest), 124 | {[P1,C],St}. 125 | 126 | %% offset(String, N, ...) -> Integer. 127 | -spec offset(_, [_], any()) -> no_return(). 128 | 129 | offset(_, As, St) -> 130 | _ = string_args(As, offset, St), 131 | %% We don't do anything yet. 132 | lua_error({'NYI',offset}, St). 133 | 134 | %% string_args(Args, Op, St) -> {String,I,J}. 135 | %% Return the string, i and j values from the arguments. Generate a 136 | %% badarg error on bad values. 137 | 138 | string_args(As, Op, St) -> 139 | %% Get the args. 140 | Args = luerl_lib:conv_list(As, [lua_string,lua_integer,lua_integer]), 141 | case Args of %Cunning here, export A1,A2,A3 142 | [A1,A2,A3|_] -> ok; 143 | [A1,A2] -> A3 = byte_size(A1); 144 | [A1] -> A2 = 1, A3 = byte_size(A1); 145 | error -> A1 = A2 = A3 = ok, badarg_error(Op, As, St) 146 | end, 147 | StrLen = byte_size(A1), 148 | %% Check args and return Str, I, J. 149 | Str = A1, 150 | I = if A2 > 0, A2 =< StrLen -> A2; 151 | A2 < 0, A2 >= -StrLen -> StrLen + A2 + 1; 152 | true -> badarg_error(Op, As, St) 153 | end, 154 | J = if A3 > 0, A3 =< StrLen -> A3; 155 | A3 < 0, A3 >= -StrLen -> StrLen + A3 + 1; 156 | true -> badarg_error(Op, As, St) 157 | end, 158 | {Str,I,J}. 159 | -------------------------------------------------------------------------------- /src/luerl_sandbox.erl: -------------------------------------------------------------------------------- 1 | %% Copyright (c) 2013-2017 Robert Virding 2 | %% 3 | %% Licensed under the Apache License, Version 2.0 (the "License"); 4 | %% you may not use this file except in compliance with the License. 5 | %% You may obtain a copy of the License at 6 | %% 7 | %% http://www.apache.org/licenses/LICENSE-2.0 8 | %% 9 | %% Unless required by applicable law or agreed to in writing, software 10 | %% distributed under the License is distributed on an "AS IS" BASIS, 11 | %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | %% See the License for the specific language governing permissions and 13 | %% limitations under the License. 14 | 15 | %% File : luerl_sandbox.erl 16 | %% Authors : Tyler Butchart 17 | %% Purpose : Reduction limiting luerl sandbox. 18 | 19 | 20 | -module(luerl_sandbox). 21 | 22 | %% @doc Sandboxed execution environment for Luerl with reduction counting. 23 | %% This module provides a way to run Lua code with controlled execution, 24 | %% limiting the number of reductions (computational steps) to prevent 25 | %% excessive resource consumption. 26 | 27 | -include("luerl.hrl"). 28 | 29 | -export([init/0,init/1,init/2, 30 | run/1,run/2,run/3,run/4,run/5]). 31 | 32 | -define(LUERL_GLOBAL, '_G'). 33 | -define(SANDBOXED_VALUE, sandboxed). 34 | -define(SANDBOXED_GLOBALS, [ 35 | [?LUERL_GLOBAL, io], 36 | [?LUERL_GLOBAL, file], 37 | [?LUERL_GLOBAL, os, execute], 38 | [?LUERL_GLOBAL, os, exit], 39 | [?LUERL_GLOBAL, os, getenv], 40 | [?LUERL_GLOBAL, os, remove], 41 | [?LUERL_GLOBAL, os, rename], 42 | [?LUERL_GLOBAL, os, tmpname], 43 | [?LUERL_GLOBAL, package], 44 | [?LUERL_GLOBAL, load], 45 | [?LUERL_GLOBAL, loadfile], 46 | [?LUERL_GLOBAL, require], 47 | [?LUERL_GLOBAL, dofile], 48 | [?LUERL_GLOBAL, load], 49 | [?LUERL_GLOBAL, loadfile], 50 | [?LUERL_GLOBAL, loadstring] 51 | ]). 52 | 53 | -define(MAX_TIME, 100). 54 | 55 | %% Define IS_MAP/1 macro for is_map/1 bif. 56 | -ifdef(HAS_MAPS). 57 | -define(IS_MAP(T), is_map(T)). 58 | -else. 59 | -define(IS_MAP(T), false). 60 | -endif. 61 | 62 | 63 | %% init([, State|TablePaths[, TablePaths]]) -> State 64 | init() -> 65 | init(luerl:init()). 66 | 67 | init(TablePaths) when is_list(TablePaths) -> 68 | init(luerl:init(), TablePaths); 69 | init(St) -> 70 | init(St, ?SANDBOXED_GLOBALS). 71 | 72 | 73 | init(St, []) -> luerl:gc(St); 74 | init(St0, [Path|Tail]) -> 75 | {ok,St1} = luerl:set_table_keys_dec(Path, ?SANDBOXED_VALUE, St0), 76 | init(St1, Tail). 77 | 78 | %% The default flags for running the sandboxed process. 79 | default_flags() -> 80 | [{max_time, ?MAX_TIME}, 81 | {max_reductions, none}, 82 | {spawn_opts, []}]. 83 | 84 | %% run(String|Binary) -> {Term,State} | {error,Term}. 85 | %% run(String|Binary, State) -> {Term,State} | {error,Term}. 86 | %% run(String|Binary, Flags, State) -> {Term,State} | {error,Term}. 87 | %% The new interface where Flags is a map which can contain: 88 | %% 89 | %% #{max_time => Time, (100 msec) 90 | %% max_reductions => Reductions, (none) 91 | %% spawn_opts => Spawn_Options} ([]) 92 | %% 93 | %% Any other fields are ignored. The default values are shown above. 94 | %% This can also be given as a keyword list. 95 | 96 | %% run(String|Binary|Form[, State[, MaxReductions|Flags[, Flags[, Timeout]]]]) -> {Term,State}|{error,Term} 97 | %% This is the old interface which still works. 98 | 99 | run(S) -> 100 | run(S, init()). 101 | 102 | run(S, St) -> 103 | do_run(S, default_flags(), St). 104 | 105 | %% The new interface. 106 | run(S, Flags, St) when ?IS_MAP(Flags) -> 107 | run(S, maps:to_list(Flags), St); 108 | run(S, Flags, #luerl{}=St) when is_list(Flags) -> 109 | do_run(S, Flags ++ default_flags(), St); 110 | 111 | %% The old interface. 112 | run(S, St, MaxR) when is_integer(MaxR) -> 113 | run(S, St, MaxR, []); 114 | run(S, St, Flags) when is_list(Flags) -> 115 | run(S, St, 0, Flags). 116 | 117 | run(S, St, MaxR, Flags) -> 118 | run(S, St, MaxR, Flags, ?MAX_TIME). 119 | 120 | run(S, St, 0, Opts, MaxT) -> 121 | %% Need to get the old no reductions to the new no reductions. 122 | run(S, St, none, Opts, MaxT); 123 | run(S, St, MaxR, Opts, MaxT) -> 124 | Flags = [{max_time,MaxT},{max_reductions,MaxR},{spawn_opts,Opts}], 125 | do_run(S, Flags, St). 126 | 127 | do_run(S, Flags, St) -> 128 | MaxT = proplists:get_value(max_time, Flags), 129 | Opts = proplists:get_value(spawn_opts, Flags), 130 | Runner = start(self(), S, Opts, St), 131 | case proplists:get_value(max_reductions, Flags) of 132 | none -> 133 | receive_response(Runner, MaxT); 134 | MaxR when is_integer(MaxR), MaxR > 0 -> 135 | case wait_reductions(Runner, MaxR) of 136 | {killed, R} -> 137 | {error, {reductions, R}}; 138 | ok -> 139 | receive_response(Runner, MaxT) 140 | end; 141 | _Other -> 142 | exit(badarg) 143 | end. 144 | 145 | start(Parent, S, Opts, St) -> 146 | spawn_opt(fun() -> 147 | try 148 | Reply = luerl:do(S, St), 149 | erlang:send(Parent, {self(), Reply}) 150 | catch 151 | error:Reason -> 152 | erlang:send(Parent, {self(), {error, Reason}}) 153 | end 154 | end, Opts). 155 | 156 | wait_reductions(Runner, MaxR) -> 157 | case process_info(Runner, reductions) of 158 | undefined -> 159 | %% The process has died. 160 | ok; 161 | {reductions, R} when R >= MaxR -> 162 | exit(Runner, kill), 163 | {killed, R}; 164 | {reductions, _} -> 165 | %% We only check every default MAX_TIME so we don't 166 | %% overload the runner process too much. 167 | receive after ?MAX_TIME -> ok end, 168 | wait_reductions(Runner, MaxR) 169 | end. 170 | 171 | receive_response(Runner, Timeout) -> 172 | receive 173 | {Runner, Reply} -> 174 | %% The runner has terminated. 175 | Reply; 176 | {error, Error} -> Error 177 | after 178 | Timeout -> 179 | %% Kill the runner as its time is up. 180 | exit(Runner, kill), 181 | {error, timeout} 182 | end. 183 | -------------------------------------------------------------------------------- /src/luerl_sup.erl: -------------------------------------------------------------------------------- 1 | %% Copyright (c) 2013-2021 Robert Virding 2 | %% 3 | %% Licensed under the Apache License, Version 2.0 (the "License"); 4 | %% you may not use this file except in compliance with the License. 5 | %% You may obtain a copy of the License at 6 | %% 7 | %% http://www.apache.org/licenses/LICENSE-2.0 8 | %% 9 | %% Unless required by applicable law or agreed to in writing, software 10 | %% distributed under the License is distributed on an "AS IS" BASIS, 11 | %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | %% See the License for the specific language governing permissions and 13 | %% limitations under the License. 14 | 15 | -module(luerl_sup). 16 | 17 | -include("luerl.hrl"). 18 | 19 | ?MODULEDOC(false). 20 | 21 | -behaviour(supervisor). 22 | 23 | %% API 24 | -export([start_link/0]). 25 | 26 | %% Supervisor callbacks 27 | -export([init/1]). 28 | 29 | -define(SERVER, ?MODULE). 30 | 31 | %% =================================================================== 32 | %% API functions 33 | %% =================================================================== 34 | 35 | start_link() -> 36 | supervisor:start_link({local, ?SERVER}, ?MODULE, []). 37 | 38 | %% =================================================================== 39 | %% Supervisor callbacks 40 | %% =================================================================== 41 | 42 | %% sup_flags() = #{strategy => strategy(), % optional 43 | %% intensity => non_neg_integer(), % optional 44 | %% period => pos_integer()} % optional 45 | %% child_spec() = #{id => child_id(), % mandatory 46 | %% start => mfargs(), % mandatory 47 | %% restart => restart(), % optional 48 | %% shutdown => shutdown(), % optional 49 | %% type => worker(), % optional 50 | %% modules => modules()} % optional 51 | init([]) -> 52 | SupFlags = #{strategy => one_for_one, 53 | intensity => 5, 54 | period => 10}, 55 | ChildSpecs = [], 56 | {ok, {SupFlags, ChildSpecs}}. 57 | 58 | %% internal functions 59 | -------------------------------------------------------------------------------- /src/luerl_util.erl: -------------------------------------------------------------------------------- 1 | %% Copyright (c) 2019 Robert Virding 2 | %% 3 | %% Licensed under the Apache License, Version 2.0 (the "License"); 4 | %% you may not use this file except in compliance with the License. 5 | %% You may obtain a copy of the License at 6 | %% 7 | %% http://www.apache.org/licenses/LICENSE-2.0 8 | %% 9 | %% Unless required by applicable law or agreed to in writing, software 10 | %% distributed under the License is distributed on an "AS IS" BASIS, 11 | %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | %% See the License for the specific language governing permissions and 13 | %% limitations under the License. 14 | 15 | %% File : luerl_util.erl 16 | %% Purpose : Utility functions for Luer. 17 | 18 | -module(luerl_util). 19 | 20 | -include("luerl.hrl"). 21 | 22 | ?MODULEDOC(false). 23 | 24 | -export([errname_info/1]). 25 | 26 | %% Convert error names to errnos and strings. 27 | errname_info(Name) -> 28 | #{errno => get_errno(Name), 29 | errstr => erl_posix_msg:message(Name)}. 30 | 31 | %% Made using the following command (`errno' from the moreutils package): 32 | %% `errno -l | sort -k2 -n | awk '{print "get_errno("tolower($1)") -> "$2";"}'' 33 | get_errno(eperm) -> 1; 34 | get_errno(enoent) -> 2; 35 | get_errno(esrch) -> 3; 36 | get_errno(eintr) -> 4; 37 | get_errno(eio) -> 5; 38 | get_errno(enxio) -> 6; 39 | get_errno(e2big) -> 7; 40 | get_errno(enoexec) -> 8; 41 | get_errno(ebadf) -> 9; 42 | get_errno(echild) -> 10; 43 | get_errno(eagain) -> 11; 44 | get_errno(ewouldblock) -> 11; 45 | get_errno(enomem) -> 12; 46 | get_errno(eacces) -> 13; 47 | get_errno(efault) -> 14; 48 | get_errno(enotblk) -> 15; 49 | get_errno(ebusy) -> 16; 50 | get_errno(eexist) -> 17; 51 | get_errno(exdev) -> 18; 52 | get_errno(enodev) -> 19; 53 | get_errno(enotdir) -> 20; 54 | get_errno(eisdir) -> 21; 55 | get_errno(einval) -> 22; 56 | get_errno(enfile) -> 23; 57 | get_errno(emfile) -> 24; 58 | get_errno(enotty) -> 25; 59 | get_errno(etxtbsy) -> 26; 60 | get_errno(efbig) -> 27; 61 | get_errno(enospc) -> 28; 62 | get_errno(espipe) -> 29; 63 | get_errno(erofs) -> 30; 64 | get_errno(emlink) -> 31; 65 | get_errno(epipe) -> 32; 66 | get_errno(edom) -> 33; 67 | get_errno(erange) -> 34; 68 | get_errno(edeadlk) -> 35; 69 | get_errno(edeadlock) -> 35; 70 | get_errno(enametoolong) -> 36; 71 | get_errno(enolck) -> 37; 72 | get_errno(enosys) -> 38; 73 | get_errno(enotempty) -> 39; 74 | get_errno(eloop) -> 40; 75 | get_errno(enomsg) -> 42; 76 | get_errno(eidrm) -> 43; 77 | get_errno(echrng) -> 44; 78 | get_errno(el2nsync) -> 45; 79 | get_errno(el3hlt) -> 46; 80 | get_errno(el3rst) -> 47; 81 | get_errno(elnrng) -> 48; 82 | get_errno(eunatch) -> 49; 83 | get_errno(enocsi) -> 50; 84 | get_errno(el2hlt) -> 51; 85 | get_errno(ebade) -> 52; 86 | get_errno(ebadr) -> 53; 87 | get_errno(exfull) -> 54; 88 | get_errno(enoano) -> 55; 89 | get_errno(ebadrqc) -> 56; 90 | get_errno(ebadslt) -> 57; 91 | get_errno(ebfont) -> 59; 92 | get_errno(enostr) -> 60; 93 | get_errno(enodata) -> 61; 94 | get_errno(etime) -> 62; 95 | get_errno(enosr) -> 63; 96 | get_errno(enonet) -> 64; 97 | get_errno(enopkg) -> 65; 98 | get_errno(eremote) -> 66; 99 | get_errno(enolink) -> 67; 100 | get_errno(eadv) -> 68; 101 | get_errno(esrmnt) -> 69; 102 | get_errno(ecomm) -> 70; 103 | get_errno(eproto) -> 71; 104 | get_errno(emultihop) -> 72; 105 | get_errno(edotdot) -> 73; 106 | get_errno(ebadmsg) -> 74; 107 | get_errno(eoverflow) -> 75; 108 | get_errno(enotuniq) -> 76; 109 | get_errno(ebadfd) -> 77; 110 | get_errno(eremchg) -> 78; 111 | get_errno(elibacc) -> 79; 112 | get_errno(elibbad) -> 80; 113 | get_errno(elibscn) -> 81; 114 | get_errno(elibmax) -> 82; 115 | get_errno(elibexec) -> 83; 116 | get_errno(eilseq) -> 84; 117 | get_errno(erestart) -> 85; 118 | get_errno(estrpipe) -> 86; 119 | get_errno(eusers) -> 87; 120 | get_errno(enotsock) -> 88; 121 | get_errno(edestaddrreq) -> 89; 122 | get_errno(emsgsize) -> 90; 123 | get_errno(eprototype) -> 91; 124 | get_errno(enoprotoopt) -> 92; 125 | get_errno(eprotonosupport) -> 93; 126 | get_errno(esocktnosupport) -> 94; 127 | get_errno(enotsup) -> 95; 128 | get_errno(eopnotsupp) -> 95; 129 | get_errno(epfnosupport) -> 96; 130 | get_errno(eafnosupport) -> 97; 131 | get_errno(eaddrinuse) -> 98; 132 | get_errno(eaddrnotavail) -> 99; 133 | get_errno(enetdown) -> 100; 134 | get_errno(enetunreach) -> 101; 135 | get_errno(enetreset) -> 102; 136 | get_errno(econnaborted) -> 103; 137 | get_errno(econnreset) -> 104; 138 | get_errno(enobufs) -> 105; 139 | get_errno(eisconn) -> 106; 140 | get_errno(enotconn) -> 107; 141 | get_errno(eshutdown) -> 108; 142 | get_errno(etoomanyrefs) -> 109; 143 | get_errno(etimedout) -> 110; 144 | get_errno(econnrefused) -> 111; 145 | get_errno(ehostdown) -> 112; 146 | get_errno(ehostunreach) -> 113; 147 | get_errno(ealready) -> 114; 148 | get_errno(einprogress) -> 115; 149 | get_errno(estale) -> 116; 150 | get_errno(euclean) -> 117; 151 | get_errno(enotnam) -> 118; 152 | get_errno(enavail) -> 119; 153 | get_errno(eisnam) -> 120; 154 | get_errno(eremoteio) -> 121; 155 | get_errno(edquot) -> 122; 156 | get_errno(enomedium) -> 123; 157 | get_errno(emediumtype) -> 124; 158 | get_errno(ecanceled) -> 125; 159 | get_errno(enokey) -> 126; 160 | get_errno(ekeyexpired) -> 127; 161 | get_errno(ekeyrevoked) -> 128; 162 | get_errno(ekeyrejected) -> 129; 163 | get_errno(eownerdead) -> 130; 164 | get_errno(enotrecoverable) -> 131; 165 | get_errno(erfkill) -> 132; 166 | get_errno(ehwpoison) -> 133; 167 | get_errno(_) -> 0. 168 | -------------------------------------------------------------------------------- /test/Elixir.Luerl_tests.erl: -------------------------------------------------------------------------------- 1 | %% Copyright (C) 2024 Robert Virding 2 | %% 3 | %% Licensed under the Apache License, Version 2.0 (the "License"); 4 | %% you may not use this file except in compliance with the License. 5 | %% You may obtain a copy of the License at 6 | %% 7 | %% http://www.apache.org/licenses/LICENSE-2.0 8 | %% 9 | %% Unless required by applicable law or agreed to in writing, software 10 | %% distributed under the License is distributed on an "AS IS" BASIS, 11 | %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | %% See the License for the specific language governing permissions and 13 | %% limitations under the License. 14 | 15 | -module('Elixir.Luerl_tests'). 16 | 17 | -include_lib("eunit/include/eunit.hrl"). 18 | 19 | private_test() -> 20 | State1 = 'Elixir.Luerl':init(), 21 | State2 = 'Elixir.Luerl':put_private(State1, secret, <<"mysecret">>), 22 | ?assertMatch({ok, <<"mysecret">>}, 'Elixir.Luerl':get_private(State2, secret)), 23 | ?assertMatch(error, 'Elixir.Luerl':get_private(State2, missing)), 24 | State3 = 'Elixir.Luerl':delete_private(State2, secret), 25 | ?assertMatch(error, 'Elixir.Luerl':get_private(State3, secret)). 26 | -------------------------------------------------------------------------------- /test/lib_os_SUITE.erl: -------------------------------------------------------------------------------- 1 | %% Copyright (c) 2023 Mark Meeus 2 | %% 3 | %% Licensed under the Apache License, Version 2.0 (the "License"); 4 | %% you may not use this file except in compliance with the License. 5 | %% You may obtain a copy of the License at 6 | %% 7 | %% http://www.apache.org/licenses/LICENSE-2.0 8 | %% 9 | %% Unless required by applicable law or agreed to in writing, software 10 | %% distributed under the License is distributed on an "AS IS" BASIS, 11 | %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | %% See the License for the specific language governing permissions and 13 | %% limitations under the License. 14 | 15 | -module(lib_os_SUITE). 16 | 17 | -include_lib("common_test/include/ct.hrl"). 18 | -include_lib("eunit/include/eunit.hrl"). 19 | 20 | -export([all/0, groups/0]). 21 | -export([os_date_formatting/1, os_date_table/1, 22 | os_date_integrated/1, os_date_integrated_table/1]). 23 | 24 | all() -> 25 | [ 26 | {group, date_support}, 27 | {group, integrated} 28 | ]. 29 | 30 | groups() -> 31 | [ 32 | {date_support, [parallel], [os_date_formatting, os_date_table]}, 33 | {integrated, [parallel], [os_date_integrated, os_date_integrated_table]} 34 | ]. 35 | 36 | os_date_formatting(_) -> 37 | Date = {{2023, 1, 2}, {3, 4, 5}}, 38 | ?assertEqual(<<"2023">>, luerl_lib_os_date:format(Date, <<"%Y">>)), 39 | ?assertEqual(<<"23">>, luerl_lib_os_date:format(Date, <<"%y">>)), 40 | ?assertEqual(<<"02">>, luerl_lib_os_date:format(Date, <<"%d">>)), 41 | ?assertEqual(<<"01">>, luerl_lib_os_date:format(Date, <<"%m">>)), 42 | ?assertEqual(<<"03">>, luerl_lib_os_date:format(Date, <<"%H">>)), 43 | ?assertEqual(<<"04">>, luerl_lib_os_date:format(Date, <<"%M">>)), 44 | ?assertEqual(<<"05">>, luerl_lib_os_date:format(Date, <<"%S">>)), 45 | ?assertEqual(<<"01/02/23">>, luerl_lib_os_date:format(Date, <<"%x">>)), 46 | ?assertEqual(<<"03:04:05">>, luerl_lib_os_date:format(Date, <<"%X">>)), 47 | ?assertEqual(<<"1">>, luerl_lib_os_date:format(Date, <<"%w">>)), %% Day of week 48 | 49 | %% Hour in 12H 50 | ?assertEqual(<<"12">>, luerl_lib_os_date:format({{2023, 1, 2},{0,0,0}}, <<"%I">>)), 51 | ?assertEqual(<<"12">>, luerl_lib_os_date:format({{2023, 1, 2},{0,59,0}}, <<"%I">>)), 52 | ?assertEqual(<<"01">>, luerl_lib_os_date:format({{2023, 1, 2},{1,00,0}}, <<"%I">>)), 53 | ?assertEqual(<<"12">>, luerl_lib_os_date:format({{2023, 1, 2},{12,00,0}}, <<"%I">>)), 54 | ?assertEqual(<<"11">>, luerl_lib_os_date:format({{2023, 1, 2},{23,00,0}}, <<"%I">>)), 55 | 56 | ?assertEqual(<<"AM">>, luerl_lib_os_date:format({{2023, 1, 2},{0,0,0}}, <<"%p">>)), 57 | ?assertEqual(<<"AM">>, luerl_lib_os_date:format({{2023, 1, 2},{11, 59,0}}, <<"%p">>)), 58 | ?assertEqual(<<"PM">>, luerl_lib_os_date:format({{2023, 1, 2},{12,0,0}}, <<"%p">>)), 59 | ?assertEqual(<<"PM">>, luerl_lib_os_date:format({{2023, 1, 2},{23,59,0}}, <<"%p">>)), 60 | 61 | ?assertEqual(<<"09">>, luerl_lib_os_date:format({{2023, 1, 2},{9,0,0}}, <<"%I">>)), %% Hour in 12H 62 | ?assertEqual(<<"01">>, luerl_lib_os_date:format(Date, <<"%W">>)), %% ISO Week number 63 | ?assertEqual(<<"Jan">>, luerl_lib_os_date:format(Date, <<"%b">>)), 64 | ?assertEqual(<<"January">>, luerl_lib_os_date:format(Date, <<"%B">>)), 65 | ?assertEqual(<<"Mon">>, luerl_lib_os_date:format(Date, <<"%a">>)), 66 | ?assertEqual(<<"Monday">>, luerl_lib_os_date:format(Date, <<"%A">>)), 67 | 68 | ?assertEqual(<<"%">>, luerl_lib_os_date:format(Date, <<"%%">>)), 69 | ?assertEqual(<<"2023-01-02 03:04:05">>, luerl_lib_os_date:format(Date, <<"%Y-%m-%d %H:%M:%S">>)). 70 | 71 | os_date_table(_) -> 72 | Date = {{2023, 1, 2}, {3, 4, 5}}, 73 | ?assertEqual([ 74 | {<<"year">>, 2023}, 75 | {<<"month">>, 1}, 76 | {<<"day">>, 2}, 77 | {<<"hour">>, 3}, 78 | {<<"min">>, 4}, 79 | {<<"sec">>, 5}, 80 | {<<"wday">>, 2} 81 | ], luerl_lib_os_date:format(Date, <<"*t">>)). 82 | 83 | os_date_integrated(_) -> 84 | State = luerl:init(), 85 | Chunk = <<"return os.date('noformat'), os.date(), os.date('%c', 1683371767)">>, 86 | {ok, [NoFormat, _, FromTimeStamp], _State1} = luerl:do(Chunk, State), 87 | ?assertEqual(<<"noformat">>, NoFormat), 88 | %% Date is "Sat May 6 13:16:07 2023", 89 | %% Just check year to avoid test flakiness 90 | ?assert(re:run(FromTimeStamp, <<"2023">>) =/= nomatch). 91 | 92 | os_date_integrated_table(_) -> 93 | State = luerl:init(), 94 | Chunk = <<"return os.date('*t').year">>, 95 | {ok, [Result], _State1} = luerl:do(Chunk, State), 96 | {{Year, _, _}, _} = calendar:local_time(), 97 | ?assertEqual(Year, Result). 98 | -------------------------------------------------------------------------------- /test/luerl_old_funcall_tests.erl: -------------------------------------------------------------------------------- 1 | %%% @author Hans-Christian Esperer 2 | %%% @copyright (C) 2015, Hans-Christian Esperer 3 | %%% Licensed under the Apache License, Version 2.0 (the "License"); 4 | %%% you may not use this file except in compliance with the License. 5 | %%% You may obtain a copy of the License at 6 | %%% 7 | %%% http://www.apache.org/licenses/LICENSE-2.0 8 | %%% 9 | %%% Unless required by applicable law or agreed to in writing, 10 | %%% software distributed under the License is distributed on an "AS 11 | %%% IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | %%% express or implied. See the License for the specific language 13 | %%% governing permissions and limitations under the License. 14 | %%% 15 | %%% @doc 16 | %% 17 | %%% @end 18 | %%% Created : 11 Jan 2015 by Hans-Christian Esperer 19 | 20 | -module(luerl_old_funcall_tests). 21 | 22 | -include_lib("eunit/include/eunit.hrl"). 23 | 24 | external_fun_test() -> 25 | State = luerl_old:init(), 26 | F = fun([A], S) -> 27 | {[A + 2, [A + 3, A + 4]], S} 28 | end, 29 | State1 = luerl_old:set_table([<<"testFun">>], F, State), 30 | {_, State2} = luerl_old:do(<<"function test(i)\n local a, b = testFun(i)\n return (a == i + 2), (b[1] == i + 3), (b[2] == i + 4) end">>, State1), 31 | {Res, _State3} = luerl_old:call_function([test], [2], State2), 32 | [BoolVal, BoolVal2, BoolVal3] = Res, 33 | ?assertEqual(true, BoolVal), 34 | ?assertEqual(true, BoolVal2), 35 | ?assertEqual(true, BoolVal3). 36 | 37 | external_nostate_fun_test() -> 38 | State = luerl_old:init(), 39 | F = fun([A]) -> 40 | [A + 2, [A + 3, A + 4]] 41 | end, 42 | State1 = luerl_old:set_table([<<"testFun">>], F, State), 43 | Chunk = <<"function test(i)\n" 44 | " local a, b = testFun(i)\n" 45 | " return (a == i + 2), (b[1] == i + 3), (b[2] == i + 4)\n" 46 | "end">>, 47 | {_, State2} = luerl_old:do(Chunk, State1), 48 | {Res, _State3} = luerl_old:call_function([test], [2], State2), 49 | [BoolVal, BoolVal2, BoolVal3] = Res, 50 | ?assertEqual(true, BoolVal), 51 | ?assertEqual(true, BoolVal2), 52 | ?assertEqual(true, BoolVal3). 53 | 54 | return_lib_function_test() -> 55 | State = luerl_old:init(), 56 | {_, State1} = luerl_old:do(<<"function test()\n return string.find end\n">>, State), 57 | {[{M,F,A}], _State2} = luerl_old:call_function([test], [1], State1), 58 | {Res, _State3} = apply(M, F, [A, [<<"barfooblafasel">>, <<"foo">>], State1]), 59 | ?assertEqual([4, 6], Res). 60 | 61 | define_fun_in_lua_test() -> 62 | State = luerl_old:init(), 63 | Chunk = <<"function mkadder(incby)\n" 64 | " return function(i)\n" 65 | " print(\"Call into Luerl!\")\n" 66 | " return i + incby\n" 67 | " end\n" 68 | "end\n">>, 69 | {_, State1} = luerl_old:do(Chunk, State), 70 | {[Fun], _State2} = luerl_old:call_function([mkadder], [1], State1), 71 | {[Fun2], _State3} = luerl_old:call_function([mkadder], [2], State1), 72 | ?assertEqual([5], Fun([4])), 73 | ?assertEqual([5.0], Fun([4.0])), 74 | ?assertEqual([6], Fun2([4])). 75 | 76 | define_fun2_in_lua_test() -> 77 | State = luerl_old:init(), 78 | Chunk = <<"function mklist(numentries)\n" 79 | " return function(entryval)\n" 80 | " local list = {}\n" 81 | " for i = 1,numentries do\n" 82 | " list[i] = entryval\n" 83 | " end\n" 84 | " return list\n" 85 | " end\n" 86 | "end\n">>, 87 | {_, State1} = luerl_old:do(Chunk, State), 88 | {[Fun], _State2} = luerl_old:call_function([mklist], [5], State1), 89 | {[Fun2], _State3} = luerl_old:call_function([mklist], [10], State1), 90 | ?assertEqual([[{1,4}, {2,4}, {3,4}, {4,4}, {5,4}]], 91 | Fun([4])), 92 | ?assertEqual([[{1,4.0}, {2,4.0}, {3,4.0}, {4,4.0}, {5,4.0}]], 93 | Fun([4.0])), 94 | ?assertEqual([[{1,4}, {2,4}, {3,4}, {4,4}, {5,4}, 95 | {6,4}, {7,4}, {8,4}, {9,4}, {10,4}]], 96 | Fun2([4])). 97 | 98 | newindex_metamethod_test() -> 99 | State = luerl_old:init(), 100 | Chunk = <<"local t = {}\n" 101 | "local m = setmetatable({}, {__newindex = function (tab, key, value)\n" 102 | "t[key] = value\n" 103 | "end})\n\n" 104 | "m[123] = 456\n" 105 | "return t[123], m[123]">>, 106 | {[TVal, MVal], _State1} = luerl_old:do(Chunk, State), 107 | ?assertEqual(456, TVal), 108 | ?assertEqual(nil, MVal). 109 | -------------------------------------------------------------------------------- /test/luerl_old_tests.erl: -------------------------------------------------------------------------------- 1 | %% Copyright (C) 2024 Robert Virding 2 | %% 3 | %% Licensed under the Apache License, Version 2.0 (the "License"); 4 | %% you may not use this file except in compliance with the License. 5 | %% You may obtain a copy of the License at 6 | %% 7 | %% http://www.apache.org/licenses/LICENSE-2.0 8 | %% 9 | %% Unless required by applicable law or agreed to in writing, software 10 | %% distributed under the License is distributed on an "AS IS" BASIS, 11 | %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | %% See the License for the specific language governing permissions and 13 | %% limitations under the License. 14 | 15 | -module(luerl_old_tests). 16 | 17 | -include_lib("eunit/include/eunit.hrl"). 18 | 19 | encode_test() -> 20 | State = luerl_old:init(), 21 | ?assertMatch({nil, _State}, luerl_old:encode(nil, State)), 22 | ?assertMatch({false, _State}, luerl_old:encode(false, State)), 23 | ?assertMatch({true, _State}, luerl_old:encode(true, State)), 24 | ?assertMatch({<<"binary">>, _State}, luerl_old:encode(<<"binary">>, State)), 25 | ?assertMatch({<<"atom">>, _State}, luerl_old:encode(atom, State)), 26 | ?assertMatch({5, _State}, luerl_old:encode(5, State)), 27 | ?assertMatch({{tref, _}, _State}, luerl_old:encode(#{a => 1, b => 2}, State)), 28 | ?assertMatch({{tref, _}, _State}, luerl_old:encode([{a,1},{b,2}], State)). 29 | 30 | encode_error_test() -> 31 | State = luerl_old:init(), 32 | ?assertException(error, {badarg, _}, luerl_old:encode({a,1}, State)). 33 | 34 | encode_table_test() -> 35 | {Table, State} = luerl_old:encode(#{a => 1}, luerl_old:init()), 36 | State1 = luerl_old:set_table1([<<"foo">>], Table, State), 37 | ?assertMatch({Table, _State2}, 38 | luerl_old:get_table1([<<"foo">>], State1)), 39 | ?assertMatch({tref, _}, Table). 40 | 41 | invalid_value_test() -> 42 | State = luerl_old:init(), 43 | ?assertException(error, {badarg, {invalid, value}}, 44 | luerl_old:encode({invalid, value}, State)). 45 | -------------------------------------------------------------------------------- /test/luerl_return_SUITE.erl: -------------------------------------------------------------------------------- 1 | %% Copyright (c) 2019 Ferenc Boroczki 2 | %% 3 | %% Licensed under the Apache License, Version 2.0 (the "License"); 4 | %% you may not use this file except in compliance with the License. 5 | %% You may obtain a copy of the License at 6 | %% 7 | %% http://www.apache.org/licenses/LICENSE-2.0 8 | %% 9 | %% Unless required by applicable law or agreed to in writing, software 10 | %% distributed under the License is distributed on an "AS IS" BASIS, 11 | %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | %% See the License for the specific language governing permissions and 13 | %% limitations under the License. 14 | 15 | -module(luerl_return_SUITE). 16 | 17 | -include_lib("common_test/include/ct.hrl"). 18 | 19 | -export([all/0, groups/0, init_per_suite/1, end_per_suite/1]). 20 | -export([simple_return/1, fun_return/1, use_lib/1, variable_args/1, check_unicode/1, table_tests/1]). 21 | 22 | init_per_suite(Config) -> 23 | DataDir = ?config(data_dir, Config), 24 | os:putenv("LUA_PATH", DataDir ++ "?.lua;" ++ DataDir ++ "?/init.lua"), 25 | Config. 26 | 27 | end_per_suite(Config) -> 28 | Config. 29 | 30 | all() -> 31 | [ 32 | {group, return} 33 | ]. 34 | 35 | groups() -> 36 | [ 37 | {return, [parallel], [simple_return, fun_return, use_lib, variable_args, check_unicode, table_tests]} 38 | ]. 39 | 40 | simple_return(Config) -> 41 | Tests = [ 42 | {"simple_return_1.lua", [1]}, 43 | {"simple_return_multi.lua", [1, <<"string 2">>, 3.4]} 44 | ], 45 | run_tests(Config, Tests). 46 | 47 | fun_return(Config) -> 48 | run_and_check(Config, "fun_return_multi.lua", [7, <<"str 1">>, 5.5, 11.0]). 49 | 50 | use_lib(Config) -> 51 | LuaDecimal = fun(B, E) -> [{<<"b">>, B}, {<<"e">>, E}] end, 52 | Expected = [LuaDecimal(B, E) || {B, E} <- [{13, 1}, {7, 1}, {3, 3}]], 53 | run_and_check(Config, "decimal_test.lua", Expected). 54 | 55 | variable_args(Config) -> 56 | run_tests(Config, [ 57 | {"variable_args_1.lua", [99, 88, 77]}, 58 | {"variable_args_multi.lua", [9, <<"banana">>, 8]} 59 | ]). 60 | 61 | check_unicode(Config) -> 62 | St = run_and_check(Config, "check_unicode.lua", []), 63 | check_unicode_call_fun(<<"árvíztűrő tükörfúrógép"/utf8>>, 31, check_hun, St), 64 | check_unicode_call_fun(<<"λ"/utf8>>, 2, check_lambda, St), 65 | check_unicode_call_fun(<<9810/utf8>>, 3, check_aquarius, St). 66 | 67 | check_unicode_call_fun(Input, Length, LuaFun, St) -> 68 | {ok, [Input, Input, true, Length, Length], _} = 69 | luerl:call_function_dec([LuaFun], [Input], St). 70 | 71 | table_tests(Config) -> 72 | run_and_check(Config, "table_indexed_table.lua", [111, 222, 333]). 73 | 74 | 75 | run_tests(Config, Tests) -> 76 | [run_and_check(Config, Script, Expected) || {Script, Expected} <- Tests]. 77 | 78 | run_and_check(Config, Script, Expected) -> 79 | DataDir = ?config(data_dir, Config), 80 | ScriptFile = DataDir ++ Script, 81 | {ok, Result, St} = luerl:dofile_dec(ScriptFile, luerl:init()), 82 | {true, {expected, Expected}, {result, Result}} = {Result =:= Expected, {expected, Expected}, {result, Result}}, 83 | St. 84 | -------------------------------------------------------------------------------- /test/luerl_return_SUITE_data/check_unicode.lua: -------------------------------------------------------------------------------- 1 | function check_hun(erl_str) 2 | local lua_str = "árvíztűrő tükörfúrógép" 3 | return check_values(erl_str, lua_str, 31) 4 | end 5 | 6 | function check_lambda(erl_str) 7 | local lua_str = "λ" 8 | return check_values(erl_str, lua_str, 2) 9 | end 10 | 11 | function check_aquarius(erl_str) 12 | local lua_str = utf8.char(9810) 13 | return check_values(erl_str, lua_str, 3) 14 | end 15 | 16 | function check_values(erl_str, lua_str, length) 17 | assert(string.len(lua_str) == length, "invalid lua length") 18 | assert(string.len(erl_str) == length, "invalid erl length") 19 | assert(lua_str == erl_str, "different values") 20 | 21 | return erl_str, lua_str, erl_str == lua_str, string.len(erl_str), string.len(lua_str) 22 | end 23 | -------------------------------------------------------------------------------- /test/luerl_return_SUITE_data/decimal.lua: -------------------------------------------------------------------------------- 1 | local Decimal = {} 2 | 3 | Decimal.mt = { 4 | __tostring = function(d) 5 | return d.b .. "e" .. d.e 6 | end, 7 | 8 | __add = function(a, b) 9 | a, b = Decimal.same_e(a, b) 10 | 11 | if a.e == b.e then 12 | return Decimal.new(a.b + b.b, a.e) 13 | end 14 | end, 15 | __sub = function(a, b) 16 | return a + Decimal.new(-b.b, b.e) 17 | end, 18 | __mul = function(a, b) 19 | return Decimal.new(a.b * b.b, a.e + b.e) 20 | end, 21 | __div = function(a, b) 22 | -- @todo fix 23 | return Decimal.new(a.b / b.b, a.e - b.e) 24 | end, 25 | 26 | __eq = function(a, b) 27 | a, b = Decimal.same_e(a, b) 28 | return a.b == b.b and a.e == b.e 29 | end 30 | } 31 | 32 | Decimal.new = function(b, e) 33 | local d = { 34 | b = b, 35 | e = e 36 | } 37 | setmetatable(d, Decimal.mt) 38 | return d 39 | end 40 | 41 | Decimal.change_e = function(d, e) 42 | if d.e == e then 43 | return d 44 | end 45 | if d.e > e then 46 | return Decimal.change_e(Decimal.new(10 * d.b, d.e - 1), e) 47 | end 48 | error("can't increment exponential") 49 | end 50 | 51 | Decimal.same_e = function(a, b) 52 | local min_e = math.min(a.e, b.e) 53 | return Decimal.change_e(a, min_e), Decimal.change_e(b, min_e) 54 | end 55 | 56 | return Decimal 57 | -------------------------------------------------------------------------------- /test/luerl_return_SUITE_data/decimal_test.lua: -------------------------------------------------------------------------------- 1 | local d = require("decimal") 2 | 3 | a = d.new(1, 2) -- 100 4 | b = d.new(3, 1) -- 30 5 | 6 | print(a, "+", b, "=", a + b) 7 | print(a, "-", b, "=", a - b) 8 | print(a, "*", b, "=", a * b) 9 | print(a, "/", b, "=", a / b) 10 | 11 | x1 = d.new(1, 2) 12 | x2 = d.new(2, 2) 13 | x3 = d.new(2, 1) 14 | x4 = d.new(10, 1) 15 | x5 = d.new(10, 2) 16 | print(a, "==", b, "=", a == b) 17 | print(a, "==", x1, "=", a == x1) 18 | print(a, "==", x2, "=", a == x2) 19 | print(a, "==", x3, "=", a == x3) 20 | print(a, "==", x4, "=", a == x4) 21 | print(a, "==", x5, "=", a == x5) 22 | 23 | assert(a + b == d.new(130, 0)) 24 | assert(a + b == d.new(13, 1)) 25 | assert(a - b == d.new(70, 0)) 26 | assert(a - b == d.new(7, 1)) 27 | assert(a * b == d.new(3000, 0)) 28 | assert(a * b == d.new(3, 3)) 29 | --assert(a / b == d.new(33333333333333, -15)) 30 | 31 | return a + b, a - b, a * b 32 | -------------------------------------------------------------------------------- /test/luerl_return_SUITE_data/fun_return_multi.lua: -------------------------------------------------------------------------------- 1 | function retfun(value) 2 | return value 3 | end 4 | 5 | function retfun2(value) 6 | return value, 2 * value 7 | end 8 | 9 | return retfun(7), retfun("str 1"), retfun2(5.5) 10 | -------------------------------------------------------------------------------- /test/luerl_return_SUITE_data/only_comments.lua: -------------------------------------------------------------------------------- 1 | -- No code, just comments 2 | -------------------------------------------------------------------------------- /test/luerl_return_SUITE_data/simple_return_1.lua: -------------------------------------------------------------------------------- 1 | return 1 2 | -------------------------------------------------------------------------------- /test/luerl_return_SUITE_data/simple_return_multi.lua: -------------------------------------------------------------------------------- 1 | return 1, "string 2", 3.4 2 | -------------------------------------------------------------------------------- /test/luerl_return_SUITE_data/table_indexed_table.lua: -------------------------------------------------------------------------------- 1 | tk1 = { a = 1 } 2 | tk2 = { b = 2 } 3 | 4 | t = {} 5 | t[tk1] = 111 6 | t[tk2] = 222 7 | t[t] = 333 8 | 9 | return t[tk1], t[tk2], t[t] 10 | -------------------------------------------------------------------------------- /test/luerl_return_SUITE_data/variable_args_1.lua: -------------------------------------------------------------------------------- 1 | function get_from_table(Map, Key, ...) 2 | local Value = Map[Key] 3 | if select('#', ...) > 0 then 4 | return get_from_table(Value, ...) 5 | else 6 | return Value 7 | end 8 | end 9 | 10 | local tab = { a = { x1 = { x2 = 99 } }, b = 88, c = { d = 77 } } 11 | 12 | return 13 | get_from_table(tab, "a", "x1", "x2"), 14 | get_from_table(tab, "b"), 15 | get_from_table(tab, "c", "d") 16 | -------------------------------------------------------------------------------- /test/luerl_return_SUITE_data/variable_args_multi.lua: -------------------------------------------------------------------------------- 1 | function make_table(...) 2 | local t = {} 3 | for i = 1, select('#', ...), 2 do 4 | k = select(i, ...) 5 | v = select(i + 1, ...) 6 | t[k] = v 7 | end 8 | return t 9 | end 10 | 11 | print(make_table) 12 | 13 | local tab = make_table("x", 9, 7, "banana", "z", 8) 14 | 15 | assert(tab["x"] == 9) 16 | assert(tab[7] == "banana") 17 | assert(tab.z == 8) 18 | 19 | return tab["x"], tab[7], tab.z 20 | -------------------------------------------------------------------------------- /test/luerl_tests.erl: -------------------------------------------------------------------------------- 1 | %% Copyright (C) 2024 Robert Virding 2 | %% 3 | %% Licensed under the Apache License, Version 2.0 (the "License"); 4 | %% you may not use this file except in compliance with the License. 5 | %% You may obtain a copy of the License at 6 | %% 7 | %% http://www.apache.org/licenses/LICENSE-2.0 8 | %% 9 | %% Unless required by applicable law or agreed to in writing, software 10 | %% distributed under the License is distributed on an "AS IS" BASIS, 11 | %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | %% See the License for the specific language governing permissions and 13 | %% limitations under the License. 14 | 15 | -module(luerl_tests). 16 | 17 | -include_lib("eunit/include/eunit.hrl"). 18 | 19 | encode_test() -> 20 | State = luerl:init(), 21 | ?assertMatch({nil, _State}, luerl:encode(nil, State)), 22 | ?assertMatch({false, _State}, luerl:encode(false, State)), 23 | ?assertMatch({true, _State}, luerl:encode(true, State)), 24 | ?assertMatch({<<"binary">>, _State}, luerl:encode(<<"binary">>, State)), 25 | ?assertMatch({<<"atom">>, _State}, luerl:encode(atom, State)), 26 | ?assertMatch({5, _State}, luerl:encode(5, State)), 27 | ?assertMatch({{tref, _}, _State}, luerl:encode(#{a => 1, b => 2}, State)), 28 | ?assertMatch({{tref, _}, _State}, luerl:encode([{a,1},{b,2}], State)). 29 | 30 | encode_error_test() -> 31 | State = luerl:init(), 32 | ?assertException(error, {badarg, _}, luerl:encode({a,1}, State)). 33 | 34 | encode_table_test() -> 35 | {Table, State} = luerl:encode(#{a => 1}, luerl:init()), 36 | {ok, State1} = luerl:set_table_keys([<<"foo">>], Table, State), 37 | ?assertMatch({ok, Table, _State2}, 38 | luerl:get_table_keys([<<"foo">>], State1)), 39 | ?assertMatch({tref, _}, Table). 40 | 41 | invalid_value_test() -> 42 | State = luerl:init(), 43 | ?assertException(error, {badarg, {invalid, value}}, 44 | luerl:encode({invalid, value}, State)). 45 | 46 | private_test() -> 47 | State1 = luerl:init(), 48 | State2 = luerl:put_private(secret, <<"mysecret">>, State1), 49 | ?assertMatch(<<"mysecret">>, luerl:get_private(secret, State2)), 50 | ?assertException(error, {badkey, missing}, luerl:get_private(missing, State2)), 51 | State3 = luerl:delete_private(secret, State2), 52 | ?assertException(error, {badkey, secret}, luerl:get_private(secret, State3)). 53 | 54 | loadfile_only_comments_test() -> 55 | State1 = luerl:init(), 56 | ?assertMatch({ok, _, _}, luerl:loadfile("./test/luerl_return_SUITE_data/only_comments.lua", State1)). 57 | -------------------------------------------------------------------------------- /test/luerl_time_SUITE.erl: -------------------------------------------------------------------------------- 1 | -module(luerl_time_SUITE). 2 | 3 | -include_lib("common_test/include/ct.hrl"). 4 | -include_lib("eunit/include/eunit.hrl"). 5 | 6 | -export([all/0]). 7 | -export([os_date/1]). 8 | 9 | -if(?OTP_RELEASE >= 25). 10 | -define(START_NODE(Name, Env), (fun(Name, Env) -> 11 | case ?CT_PEER(#{name => Name, env => Env}) of 12 | {ok, _PeerPid, Node} -> 13 | {ok, Node}; 14 | Err = {error, _Reason, _NodeName} -> 15 | Err 16 | end 17 | end)(Name, Env)). 18 | -else. 19 | -define(START_NODE(Name, Env), (fun(Name, Env) -> 20 | ct_slave:start(Name, [{env, Env}, {monitor_master, true}]) 21 | end)(Name, Env)). 22 | -endif. 23 | 24 | all() -> 25 | lists:flatten([windows_tests(), linux_tests()]). 26 | 27 | os_date(_Config) -> 28 | {ok, LusakaNode} = ?START_NODE(africa_lusaka, [{"TZ", "Africa/Lusaka"}]), 29 | ok = set_path(LusakaNode), 30 | {ok, LondonNode} = ?START_NODE(europe_london, [{"TZ", "Europe/London"}]), 31 | ok = set_path(LondonNode), 32 | LusakaLocalTime = rpc:call(LusakaNode, calendar, local_time, []), 33 | LondonLocalTime = rpc:call(LondonNode, calendar, local_time, []), 34 | ?assertNotMatch({badrpc, _}, LusakaLocalTime), 35 | ?assertNotMatch({badrpc, _}, LondonLocalTime), 36 | ?assert(LusakaLocalTime =/= LondonLocalTime), 37 | ?assertMatch({ok,[<<"Sat May 6 13:16:07 2023">>], _St1}, 38 | rpc:call(LusakaNode, luerl, do_dec, 39 | ["return os.date('%c', 1683371767)", luerl:init()])), 40 | ?assertMatch({ok,[<<"Sat May 6 12:16:07 2023">>], _St2}, 41 | rpc:call(LondonNode, luerl, do_dec, 42 | ["return os.date('%c', 1683371767)", luerl:init()])), 43 | ok. 44 | 45 | windows_tests() -> 46 | []. 47 | 48 | linux_tests() -> 49 | [os_date]. 50 | 51 | set_path(Node) -> 52 | [case rpc:call(Node, code, add_path, [Path]) of 53 | true -> 54 | ok; 55 | Err = {error, _} -> 56 | throw({badpath, Path, Err}) 57 | end || Path <- code:get_path(), filelib:is_dir(Path)], 58 | ok. 59 | --------------------------------------------------------------------------------