├── docs ├── .gitignore ├── _static │ └── files │ │ └── llnl-pres-703906.pdf ├── wkt │ ├── Makefile │ ├── mesh │ │ └── introduction.rst │ ├── wkt_mesh.h │ └── mesh.rst ├── requirements.txt ├── Makefile ├── index.rst ├── conf.py ├── docs.rst ├── quick_start.rst ├── irep_types.rst ├── overview.rst ├── build.rst └── reference.rst ├── examples ├── .gitignore ├── cxx-cmake │ ├── GNUmakefile │ ├── wkt_table4.h │ ├── CMakeLists.txt │ ├── input.lua │ ├── wkt_table1.h │ └── cxx_main.cpp ├── c │ ├── wkt_table4.h │ ├── wkt_table1.h │ ├── GNUmakefile │ ├── input.lua │ ├── c_main.c │ └── timer_example │ │ └── c_main.c ├── fortran │ ├── wkt_table4.h │ ├── input.lua │ ├── wkt_table1.h │ ├── GNUmakefile │ ├── print_r.lua │ └── f_main.f ├── GNUmakefile └── README.md ├── .gitignore ├── .readthedocs.yml ├── ir_end.h ├── ir_start.h ├── ir_undef.h ├── .github └── workflows │ ├── docs.yaml │ └── linux_build_tests.yaml ├── ir_std.h ├── LICENSE ├── NOTICE ├── ir_index.h ├── ir_extern.h ├── CMakeLists.txt ├── bin ├── l2ir └── irep-generate ├── share └── irep │ ├── wkt.mk │ └── irep-config.cmake ├── README.md ├── ir_macros.h └── irep.c /docs/.gitignore: -------------------------------------------------------------------------------- 1 | _build 2 | -------------------------------------------------------------------------------- /examples/.gitignore: -------------------------------------------------------------------------------- 1 | *.a 2 | *.o 3 | *.mod 4 | *_prog 5 | cxx-cmake/build 6 | build 7 | irep 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /ir_generate.h 2 | /ir_extern.f 3 | /ir_std.f 4 | *.o 5 | *.mod 6 | /libIR.a 7 | *~ 8 | *.swp 9 | -------------------------------------------------------------------------------- /docs/_static/files/llnl-pres-703906.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/llnl/irep/HEAD/docs/_static/files/llnl-pres-703906.pdf -------------------------------------------------------------------------------- /docs/wkt/Makefile: -------------------------------------------------------------------------------- 1 | wkt_mesh.rst: wkt_mesh.h 2 | ../../bin/irep-generate --mode rst $^ 3 | rm -f irep_types.rst 4 | 5 | clean: 6 | rm -f *.rst 7 | -------------------------------------------------------------------------------- /docs/requirements.txt: -------------------------------------------------------------------------------- 1 | # These dependencies should be installed using pip in order 2 | # to build the documentation. 3 | 4 | sphinx 5 | sphinxcontrib-programoutput 6 | sphinx-rtd-theme 7 | -------------------------------------------------------------------------------- /examples/cxx-cmake/GNUmakefile: -------------------------------------------------------------------------------- 1 | input = input.lua 2 | 3 | test: build/cxx_prog 4 | build/cxx_prog $(input) 5 | 6 | build/cxx_prog: 7 | mkdir -p build 8 | cd build && cmake -DCMAKE_PREFIX_PATH=../irep .. && make 9 | 10 | clean: 11 | rm -rf build 12 | -------------------------------------------------------------------------------- /.readthedocs.yml: -------------------------------------------------------------------------------- 1 | # .readthedocs.yaml 2 | # Read the Docs configuration file 3 | # See https://docs.readthedocs.io/en/stable/config-file/v2.html for details 4 | 5 | version: 2 6 | 7 | sphinx: 8 | configuration: docs/conf.py 9 | 10 | python: 11 | version: 3.7 12 | install: 13 | - requirements: docs/requirements.txt 14 | -------------------------------------------------------------------------------- /docs/wkt/mesh/introduction.rst: -------------------------------------------------------------------------------- 1 | This is documentation generated from the ``wkt_mesh.h`` described in 2 | IREP's ``README.md``. ``wkt_mesh.h`` looks like this: 3 | 4 | .. WKT headers don't lex as proper C, but prolog works ok :) 5 | .. literalinclude:: /wkt/wkt_mesh.h 6 | :language: prolog 7 | 8 | The fields in this table and its ``box`` subtable are documented below. 9 | -------------------------------------------------------------------------------- /ir_end.h: -------------------------------------------------------------------------------- 1 | // Copyright 2016-2021 Lawrence Livermore National Security, LLC and other 2 | // IREP Project Developers. See the top-level LICENSE file for details. 3 | // 4 | // SPDX-License-Identifier: MIT 5 | 6 | // Include this file at the end of each wkt_*.h file. 7 | 8 | #if defined(IREP_LANG_FORTRAN) 9 | end module 10 | 11 | #elif defined(__cplusplus) 12 | } /* end extern "C" */ 13 | } /* end namespace irep */ 14 | #endif 15 | -------------------------------------------------------------------------------- /examples/c/wkt_table4.h: -------------------------------------------------------------------------------- 1 | // Copyright 2016-2021 Lawrence Livermore National Security, LLC and other 2 | // IREP Project Developers. See the top-level LICENSE file for details. 3 | // 4 | // SPDX-License-Identifier: MIT 5 | 6 | #ifndef wkt_table4_h 7 | #define wkt_table4_h 8 | #include "ir_start.h" 9 | 10 | Beg_struct(irt_table4) 11 | ir_str(name,32,"default name") 12 | ir_reference(fooref) 13 | End_struct(irt_table4) 14 | 15 | Vir_wkt(irt_table4,table4,0:98,4) 16 | 17 | #include "ir_end.h" 18 | #endif 19 | -------------------------------------------------------------------------------- /examples/fortran/wkt_table4.h: -------------------------------------------------------------------------------- 1 | // Copyright 2016-2021 Lawrence Livermore National Security, LLC and other 2 | // IREP Project Developers. See the top-level LICENSE file for details. 3 | // 4 | // SPDX-License-Identifier: MIT 5 | 6 | #ifndef wkt_table4_h 7 | #define wkt_table4_h 8 | #include "ir_start.h" 9 | 10 | Beg_struct(irt_table4) 11 | ir_str(name,32,"default name") 12 | ir_reference(fooref) 13 | End_struct(irt_table4) 14 | 15 | Vir_wkt(irt_table4,table4,0:98,4) 16 | 17 | #include "ir_end.h" 18 | #endif 19 | -------------------------------------------------------------------------------- /examples/cxx-cmake/wkt_table4.h: -------------------------------------------------------------------------------- 1 | // Copyright 2016-2021 Lawrence Livermore National Security, LLC and other 2 | // IREP Project Developers. See the top-level LICENSE file for details. 3 | // 4 | // SPDX-License-Identifier: MIT 5 | 6 | #ifndef wkt_table4_h 7 | #define wkt_table4_h 8 | #include "ir_start.h" 9 | 10 | Beg_struct(irt_table4) 11 | ir_str(name,32,"default name") 12 | ir_reference(fooref) 13 | End_struct(irt_table4) 14 | 15 | Vir_wkt(irt_table4,table4,0:98,4) 16 | 17 | #include "ir_end.h" 18 | #endif 19 | -------------------------------------------------------------------------------- /ir_start.h: -------------------------------------------------------------------------------- 1 | // Copyright 2016-2021 Lawrence Livermore National Security, LLC and other 2 | // IREP Project Developers. See the top-level LICENSE file for details. 3 | // 4 | // SPDX-License-Identifier: MIT 5 | 6 | #include "ir_macros.h" 7 | 8 | // Include this file at the beginning of each wkt_*.h file. 9 | 10 | #if defined(IREP_LANG_FORTRAN) 11 | module IR_WKT_NAME 12 | use, intrinsic :: iso_c_binding 13 | use :: ir_std 14 | 15 | #else 16 | #include "ir_std.h" 17 | #if defined(__cplusplus) 18 | namespace irep { 19 | extern "C" { 20 | #endif 21 | #endif 22 | -------------------------------------------------------------------------------- /examples/GNUmakefile: -------------------------------------------------------------------------------- 1 | all: c cxx fortran 2 | 3 | irep: 4 | mkdir -p build && \ 5 | cd build && \ 6 | cmake -DCMAKE_INSTALL_PREFIX=../irep ../.. && \ 7 | make install 8 | 9 | c: irep 10 | @echo -e $(cgreen)Testing IREP C Executable with GNU make$(cend) 11 | make -C c test 12 | 13 | cxx: irep 14 | @echo -e $(cgreen)Testing IREP C++ Executable with CMake$(cend) 15 | make -C cxx-cmake test 16 | 17 | fortran: irep 18 | @echo -e $(cgreen)Testing IREP Fortran Executable with GNU make$(cend) 19 | make -C fortran test 20 | 21 | clean: 22 | make -C c clean 23 | make -C cxx-cmake clean 24 | make -C fortran clean 25 | rm -rf build irep 26 | -------------------------------------------------------------------------------- /ir_undef.h: -------------------------------------------------------------------------------- 1 | // Copyright 2016-2021 Lawrence Livermore National Security, LLC and other 2 | // IREP Project Developers. See the top-level LICENSE file for details. 3 | // 4 | // SPDX-License-Identifier: MIT 5 | 6 | // Undefine the IR macros. 7 | #undef BOOLEAN 8 | #undef Beg_struct 9 | #undef End_struct 10 | #undef ir_log 11 | #undef ir_dbl 12 | #undef ir_int 13 | #undef ir_str 14 | #undef Vir_log 15 | #undef Vir_dbl 16 | #undef Vir_int 17 | #undef Vir_str 18 | #undef Structure 19 | #undef Begin_cb_pattern_list 20 | #undef cb_pat 21 | #undef End_cb_pattern_list 22 | #undef Begin_sh_pattern_list 23 | #undef sh_pat 24 | #undef End_sh_pattern_list 25 | #undef ir_wkt 26 | -------------------------------------------------------------------------------- /examples/cxx-cmake/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright 2016-2021 Lawrence Livermore National Security, LLC and other 2 | # IREP Project Developers. See the top-level LICENSE file for details. 3 | # 4 | # SPDX-License-Identifier: MIT 5 | 6 | cmake_minimum_required(VERSION 3.3) 7 | project(irep-cxx-test LANGUAGES C CXX Fortran) 8 | 9 | find_package(irep REQUIRED) 10 | 11 | find_package(Lua REQUIRED) 12 | include_directories("${LUA_INCLUDE_DIR}") 13 | 14 | add_wkt_library(prog-wkt wkt_table1.h wkt_table4.h) 15 | add_wkt_index_library(prog-wkt-index wkt_table1.h wkt_table4.h) 16 | 17 | add_executable(cxx_prog cxx_main.cpp) 18 | target_link_libraries( 19 | cxx_prog 20 | prog-wkt 21 | ${IREP_LIBRARIES} 22 | prog-wkt-index 23 | prog-wkt 24 | ${LUA_LIBRARIES} 25 | ) 26 | -------------------------------------------------------------------------------- /.github/workflows/docs.yaml: -------------------------------------------------------------------------------- 1 | name: documentation 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | pull_request: 8 | branches: 9 | - master 10 | 11 | jobs: 12 | documentation: 13 | runs-on: ubuntu-latest 14 | steps: 15 | - uses: actions/checkout@v2 16 | - uses: actions/setup-python@v2 17 | with: 18 | python-version: 3.9 19 | - name: Install System packages 20 | run: | 21 | sudo apt-get -yqq update 22 | sudo apt-get install -yqq graphviz lua5.1 23 | - name: Install Python packages 24 | run: | 25 | pip install --upgrade pip six setuptools 26 | pip install --upgrade -r docs/requirements.txt 27 | - name: Build documentation 28 | run: | 29 | make -C docs 30 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Minimal makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line, and also 5 | # from the environment for the first two. 6 | SPHINXOPTS ?= -W --keep-going -n 7 | SPHINXBUILD ?= sphinx-build 8 | SOURCEDIR = . 9 | BUILDDIR = _build 10 | 11 | # build html by default 12 | html: 13 | 14 | # Put it first so that "make" without argument is like "make help". 15 | help: 16 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 17 | 18 | .PHONY: help Makefile 19 | 20 | # Catch-all target: route all unknown targets to Sphinx using the new 21 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). 22 | %: Makefile 23 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 24 | 25 | clean: 26 | make -C wkt clean 27 | -------------------------------------------------------------------------------- /examples/c/wkt_table1.h: -------------------------------------------------------------------------------- 1 | // Copyright 2016-2021 Lawrence Livermore National Security, LLC and other 2 | // IREP Project Developers. See the top-level LICENSE file for details. 3 | // 4 | // SPDX-License-Identifier: MIT 5 | 6 | #ifndef wkt_table1_h 7 | #define wkt_table1_h 8 | #include "ir_start.h" 9 | 10 | Beg_struct(irt_table2) 11 | ir_int(i,1) 12 | Callback(f2,3,1) 13 | End_struct(irt_table2) 14 | 15 | Beg_struct(irt_table3) 16 | ir_int(i,2) 17 | Callback(f3,3,2) 18 | End_struct(irt_table3) 19 | 20 | Beg_struct(irt_table1) 21 | ir_int(i,42) 22 | ir_dbl(d,3.14) 23 | Vir_dbl(e,5,3.14) 24 | ir_str(s,8,"abcd") 25 | ir_log(b,true) 26 | Structure(irt_table3,table3) 27 | Vstructure(irt_table2,table2,0:5,6) 28 | Callback(f1,3,1) 29 | Callback(f4,3,3) 30 | End_struct(irt_table1) 31 | 32 | ir_wkt(irt_table1, table1) 33 | 34 | #include "ir_end.h" 35 | #endif 36 | -------------------------------------------------------------------------------- /examples/fortran/input.lua: -------------------------------------------------------------------------------- 1 | -- Copyright 2016-2021 Lawrence Livermore National Security, LLC and other 2 | -- IREP Project Developers. See the top-level LICENSE file for details. 3 | -- 4 | -- SPDX-License-Identifier: MIT 5 | 6 | require 'print_r' 7 | 8 | table1 = { 9 | b = true, 10 | d = 2.71, 11 | e = { 1.1, 2.2, 3.3, 4.4, 5.5 }, 12 | -- f1 = function(x,y,z) return x*y*z end, 13 | f1 = 3, 14 | -- f4 = function(x,y,z) return x+y+z end, 15 | f4 = { 2, 2+3, 2+3+4 }, 16 | f5 = function(x,y,z) return { 50, 30, 40 } end, 17 | -- f5 = function(x,y,z) return "abcdef" end, 18 | i = 42, 19 | s = 'abcdefg', 20 | -- s = function() return true end, 21 | table2 = { 22 | [1] = { 23 | i = 1, 24 | f2 = function(x,y,z) return x+y+z end, 25 | }, 26 | }, 27 | table3 = { 28 | i = 2, 29 | f3 = { 1, 43 }, 30 | -- xx = { 1,2,3, "ignore me" }, 31 | }, 32 | } 33 | -------------------------------------------------------------------------------- /examples/fortran/wkt_table1.h: -------------------------------------------------------------------------------- 1 | // Copyright 2016-2021 Lawrence Livermore National Security, LLC and other 2 | // IREP Project Developers. See the top-level LICENSE file for details. 3 | // 4 | // SPDX-License-Identifier: MIT 5 | 6 | #ifndef wkt_table1_h 7 | #define wkt_table1_h 8 | #include "ir_start.h" 9 | 10 | Beg_struct(irt_table2) 11 | ir_int(i,1) 12 | Callback(f2,3,1) 13 | End_struct(irt_table2) 14 | 15 | Beg_struct(irt_table3) 16 | ir_int(i,2) 17 | Callback(f3,3,2) 18 | End_struct(irt_table3) 19 | 20 | Beg_struct(irt_table1) 21 | ir_int(i,42) 22 | ir_dbl(d,3.14) 23 | Vir_dbl(e,5,3.14) 24 | ir_str(s,8,"abcd") 25 | ir_log(b,true) 26 | Structure(irt_table3,table3) 27 | Vstructure(irt_table2,table2,0:5,6) 28 | Callback(f1,3,1) 29 | Callback(f4,3,3) 30 | Callback(f5,3,-1) 31 | End_struct(irt_table1) 32 | 33 | ir_wkt(irt_table1, table1) 34 | 35 | #include "ir_end.h" 36 | #endif 37 | -------------------------------------------------------------------------------- /examples/cxx-cmake/input.lua: -------------------------------------------------------------------------------- 1 | -- Copyright 2016-2021 Lawrence Livermore National Security, LLC and other 2 | -- IREP Project Developers. See the top-level LICENSE file for details. 3 | -- 4 | -- SPDX-License-Identifier: MIT 5 | 6 | table1 = { 7 | b = true, 8 | d = 2.71, 9 | e = { 1.1, 2.2, 3.3, 4.4, 5.5 }, 10 | -- f1 = function(x,y,z) return x*y*z end, 11 | f1 = 3, 12 | -- f4 = function(x,y,z) return x+y+z end, 13 | f4 = { 2, 2+3, 2+3+4 }, 14 | -- f5 = function(x,y,z) return { 50, 30, 40 } end, 15 | -- f5 = function(x,y,z) return "abcdef" end, 16 | f5 = { 1.23, 3.45, 4.56 }, 17 | i = 42, 18 | s = 'abcdefg', 19 | -- s = function() return true end, 20 | table2 = { 21 | [1] = { 22 | i = 1, 23 | f2 = function(x,y,z) return x+y+z end, 24 | }, 25 | }, 26 | table3 = { 27 | i = 2, 28 | f3 = { 1, 43 }, 29 | -- xx = { 1,2,3, "ignore me" }, 30 | }, 31 | } 32 | -------------------------------------------------------------------------------- /ir_std.h: -------------------------------------------------------------------------------- 1 | // Copyright 2016-2021 Lawrence Livermore National Security, LLC and other 2 | // IREP Project Developers. See the top-level LICENSE file for details. 3 | // 4 | // SPDX-License-Identifier: MIT 5 | 6 | #ifndef ir_std_h 7 | #define ir_std_h 8 | #include "ir_macros.h" 9 | 10 | #if defined(IREP_LANG_FORTRAN) 11 | module IR_WKT_NAME 12 | use, intrinsic :: iso_c_binding 13 | #endif 14 | 15 | #if defined(__cplusplus) 16 | extern "C" { 17 | #endif 18 | 19 | // Standard types and structures predefined by the IR. 20 | 21 | // Data for Lua callback function. 22 | Beg_struct(lua_cb_data) 23 | ir_int(fref, -1) // -1 == LUA_REFNIL 24 | ir_int(npnr, -9) // packed nprm,nret 25 | ir_int(base_npnr, -9) // unaltered version 26 | ir_ptr(data) 27 | End_struct(lua_cb_data) 28 | 29 | #if defined(__cplusplus) 30 | } 31 | #endif 32 | 33 | #if defined(IREP_LANG_FORTRAN) 34 | end module 35 | #endif 36 | 37 | #endif 38 | -------------------------------------------------------------------------------- /examples/cxx-cmake/wkt_table1.h: -------------------------------------------------------------------------------- 1 | // Copyright 2016-2021 Lawrence Livermore National Security, LLC and other 2 | // IREP Project Developers. See the top-level LICENSE file for details. 3 | // 4 | // SPDX-License-Identifier: MIT 5 | 6 | #ifndef wkt_table1_h 7 | #define wkt_table1_h 8 | #include "ir_start.h" 9 | 10 | Beg_struct(irt_table2) 11 | ir_int(i,1) 12 | Callback(f2,3,1) 13 | End_struct(irt_table2) 14 | 15 | Beg_struct(irt_table3) 16 | ir_int(i,2) 17 | Callback(f3,3,2) 18 | End_struct(irt_table3) 19 | 20 | Beg_struct(irt_table1) 21 | ir_int(i,42) 22 | ir_dbl(d,3.14) 23 | Vir_dbl(e,5,3.14) 24 | ir_str(s,8,"abcd") 25 | ir_log(b,true) 26 | Structure(irt_table3,table3) 27 | Vstructure(irt_table2,table2,0:5,6) 28 | Callback(f1,3,1) 29 | Callback(f4,3,3) 30 | Callback(f5,3,-1) 31 | //Callback(f5,3,3) 32 | End_struct(irt_table1) 33 | 34 | ir_wkt(irt_table1, table1) 35 | 36 | #include "ir_end.h" 37 | #endif 38 | -------------------------------------------------------------------------------- /examples/c/GNUmakefile: -------------------------------------------------------------------------------- 1 | # Copyright 2016-2021 Lawrence Livermore National Security, LLC and other 2 | # IREP Project Developers. See the top-level LICENSE file for details. 3 | # 4 | # SPDX-License-Identifier: MIT 5 | 6 | include ../irep/share/irep/wkt.mk 7 | 8 | # Gnu compilers (gcc, g++, gfortran) require version 4.7.4 or greater. 9 | 10 | .DEFAULT_GOAL := test 11 | 12 | obj = c_main.o 13 | prog = c_prog 14 | input = input.lua 15 | 16 | wkt_table4.mod: wkt_table1.mod 17 | 18 | wkt.lib = libprog-wkt.a libprog-wkt-index.a 19 | prog.wkt_src = $(wildcard wkt_*.h) 20 | prog.wkt_index_src = $(prog.wkt_src) 21 | 22 | test: $(prog) 23 | ./$(prog) $(input) 24 | 25 | $(prog): $(obj) $(wkt.lib) 26 | $(COMPILE.c) -o $@ $(obj) \ 27 | -L$(irep_dir)/lib -L. \ 28 | -Wl,--start-group -lprog-wkt -lIR -lprog-wkt-index -Wl,--end-group \ 29 | $(LUA_LIBRARIES) -lm -lgfortran -ldl 30 | 31 | .PHONY: clean 32 | clean: 33 | rm -f $(prog) $(obj) $(wkt.lib) *.mod *.o 34 | -------------------------------------------------------------------------------- /examples/fortran/GNUmakefile: -------------------------------------------------------------------------------- 1 | # Copyright 2016-2021 Lawrence Livermore National Security, LLC and other 2 | # IREP Project Developers. See the top-level LICENSE file for details. 3 | # 4 | # SPDX-License-Identifier: MIT 5 | 6 | include ../irep/share/irep/wkt.mk 7 | 8 | # Gnu compilers (gcc, g++, gfortran) require version 4.7.4 or greater. 9 | 10 | .DEFAULT_GOAL := test 11 | 12 | obj = f_main.o 13 | prog = f_prog 14 | input = input.lua 15 | 16 | f_main.o: wkt_table4.mod wkt_table1.mod 17 | 18 | wkt.lib = libprog-wkt.a libprog-wkt-index.a 19 | prog.wkt_src = $(wildcard wkt_*.h) 20 | prog.wkt_index_src = $(prog.wkt_src) 21 | 22 | test: $(prog) 23 | ./$(prog) $(input) 24 | 25 | $(prog): $(obj) $(wkt.lib) 26 | $(COMPILE.f) -o $@ $(obj) \ 27 | -L$(irep_dir)/lib -L. \ 28 | -Wl,--start-group -lprog-wkt -lIR -lprog-wkt-index -Wl,--end-group \ 29 | $(LUA_LIBRARIES) -lm -lgfortran -ldl 30 | 31 | .PHONY: clean 32 | clean: 33 | rm -f $(prog) $(obj) $(wkt.lib) *.mod *.o 34 | -------------------------------------------------------------------------------- /examples/c/input.lua: -------------------------------------------------------------------------------- 1 | -- Copyright 2016-2021 Lawrence Livermore National Security, LLC and other 2 | -- IREP Project Developers. See the top-level LICENSE file for details. 3 | -- 4 | -- SPDX-License-Identifier: MIT 5 | 6 | table1 = { 7 | b = true, 8 | d = 2.71, 9 | e = { 1.1, 2.2, 3.3, 4.4, 5.5 }, 10 | f1 = function(x,y,z) return x*y*z end, 11 | -- f4 = function(x,y,z) return x+y+z end, 12 | f4 = { 2, 2+3, 2+3+4 }, 13 | i = 42, 14 | s = 'abcdefg', 15 | -- s = function() return true end, 16 | table2 = { 17 | [1] = { 18 | i = 1, 19 | f2 = function(x,y,z) return x+y+z end, 20 | }, 21 | }, 22 | table3 = { 23 | i = 2, 24 | f3 = { 1, 43 }, 25 | -- xx = { 1,2,3, "ignore me" }, 26 | }, 27 | } 28 | 29 | table4 = { 30 | [1] = { 31 | name = "this is table4[1].name", 32 | }, 33 | [2] = { 34 | name = "this is table4[2].name", 35 | fooref = { 3,2,1 }, 36 | -- fooref = "abcdef", 37 | -- fooref = function() return 3 end, 38 | }, 39 | -- [3]: Use the IREP default. 40 | } 41 | -------------------------------------------------------------------------------- /.github/workflows/linux_build_tests.yaml: -------------------------------------------------------------------------------- 1 | name: linux builds 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | pull_request: 8 | branches: 9 | - master 10 | 11 | jobs: 12 | build: 13 | runs-on: ubuntu-latest 14 | steps: 15 | - uses: actions/checkout@v2 16 | - name: Install Packages 17 | run: | 18 | sudo apt-get update 19 | sudo apt-get install -yqq gcc g++ gfortran lua5.1 lua5.1-dev make 20 | - name: Build IREP 21 | run: | 22 | mkdir build && cd build 23 | cmake .. 24 | make -j8 25 | - name: C Example 26 | run: | 27 | cd examples && make c LUA_INCLUDE=-I/usr/include/lua5.1 LUA_LIBRARIES="-L/usr/lib/x86_64-linux-gnu -llua5.1" 28 | - name: C++/CMake Example 29 | run: | 30 | cd examples && make cxx LUA_INCLUDE=-I/usr/include/lua5.1 LUA_LIBRARIES="-L/usr/lib/x86_64-linux-gnu -llua5.1" 31 | - name: Fortran Example 32 | run: | 33 | cd examples && make fortran LUA_INCLUDE=-I/usr/include/lua5.1 LUA_LIBRARIES="-L/usr/lib/x86_64-linux-gnu -llua5.1" 34 | -------------------------------------------------------------------------------- /docs/wkt/wkt_mesh.h: -------------------------------------------------------------------------------- 1 | #ifndef wkt_mesh_h 2 | #define wkt_mesh_h 3 | #include "ir_start.h" 4 | 5 | Beg_struct(irt_box) 6 | ir_int(nx, 1) Doc(( Number of grid cells in 1st dimension )) 7 | ir_int(ny, 1) Doc(( Number of grid cells in 2nd dimension )) 8 | ir_int(nz, 1) Doc(( Number of grid cells in 3rd dimension )) 9 | ir_dbl(xmin, 0.0) Doc(( Location of left-most cell face )) 10 | ir_dbl(xmax, 1.0) Doc(( Location of right-most cell face )) 11 | ir_dbl(ymin, 0.0) Doc(( Location of nearest cell face )) 12 | ir_dbl(ymax, 1.0) Doc(( Location of farthest cell face )) 13 | ir_dbl(zmin, 0.0) Doc(( Location of lowest cell face )) 14 | ir_dbl(zmax, 1.0) Doc(( Location of highest cell face )) 15 | End_struct(irt_box) 16 | 17 | Beg_struct(irt_mesh) 18 | ir_str(file, FILENAMESIZE, 'none') Doc(( Name of mesh )) 19 | ir_int(refinement_level, 0) 20 | ir_log(amr, false) Doc(( Use amr? )) 21 | Structure(irt_box,box) 22 | End_struct(irt_mesh) 23 | 24 | // Declare the structure 25 | ir_wkt(irt_mesh, mesh) 26 | 27 | #include "ir_end.h" 28 | #endif // wkt_mesh_h 29 | -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | .. Copyright 2016-2021 Lawrence Livermore National Security, LLC and other 2 | .. IREP Project Developers. See the top-level LICENSE file for details. 3 | .. 4 | .. SPDX-License-Identifier: MIT 5 | 6 | IREP 7 | ==== 8 | 9 | IREP is a tool that enables mixed-language simulation codes to use a 10 | common, Lua-based format for their input decks. Essentially, the input 11 | format is a set of `tables `_ -- Lua's 12 | one (and only?) data structure. IREP is an *intermediate representation* 13 | that is used to generate plain-old-data (POD) structures in C, C++, 14 | Fortran, and Lua. 15 | 16 | For some background and motivation, see this :download:`presentation 17 | <_static/files/llnl-pres-703906.pdf>` and the corresponding `video 18 | `_. 19 | 20 | .. toctree:: 21 | :maxdepth: 3 22 | :caption: Contents: 23 | 24 | overview 25 | quick_start 26 | build 27 | docs 28 | reference 29 | irep_types 30 | 31 | Indices and tables 32 | ================== 33 | 34 | * :ref:`genindex` 35 | * :ref:`modindex` 36 | * :ref:`search` 37 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016-2021 Lawrence Livermore National Security, LLC. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /NOTICE: -------------------------------------------------------------------------------- 1 | This work was produced under the auspices of the U.S. Department of 2 | Energy by Lawrence Livermore National Laboratory under Contract 3 | DE-AC52-07NA27344. 4 | 5 | This work was prepared as an account of work sponsored by an agency of 6 | the United States Government. Neither the United States Government nor 7 | Lawrence Livermore National Security, LLC, nor any of their employees 8 | makes any warranty, expressed or implied, or assumes any legal liability 9 | or responsibility for the accuracy, completeness, or usefulness of any 10 | information, apparatus, product, or process disclosed, or represents that 11 | its use would not infringe privately owned rights. 12 | 13 | Reference herein to any specific commercial product, process, or service 14 | by trade name, trademark, manufacturer, or otherwise does not necessarily 15 | constitute or imply its endorsement, recommendation, or favoring by the 16 | United States Government or Lawrence Livermore National Security, LLC. 17 | 18 | The views and opinions of authors expressed herein do not necessarily 19 | state or reflect those of the United States Government or Lawrence 20 | Livermore National Security, LLC, and shall not be used for advertising 21 | or product endorsement purposes. 22 | -------------------------------------------------------------------------------- /examples/fortran/print_r.lua: -------------------------------------------------------------------------------- 1 | -- example usage: 2 | -- 3 | -- require 'print_r' 4 | -- a = {x=1, y=2, label={text='hans', color='blue'}, list={'a','b','c'}} 5 | -- pr(a, 'My table') 6 | -- 7 | --My table = { 8 | --| y = 2 9 | --| x = 1 10 | --| list = { 11 | --| | [1] = "a" 12 | --| | [2] = "b" 13 | --| | [3] = "c" 14 | --| } 15 | --| label = { 16 | --| | color = "blue" 17 | --| | text = "hans" 18 | --| } 19 | --} 20 | -- 21 | -- Copyright 2009: hans@hpelbers.org 22 | -- This is freeware 23 | 24 | 25 | function print_r (t, name, indent) 26 | local tableList = {} 27 | function table_r (t, name, indent, full) 28 | local id = not full and name 29 | or type(name)~="number" and tostring(name) or '['..name..']' 30 | local tag = indent .. id .. ' = ' 31 | local out = {} -- result 32 | if type(t) == "table" then 33 | if tableList[t] ~= nil then table.insert(out, tag .. '{} -- ' .. tableList[t] .. ' (self reference)') 34 | else 35 | tableList[t]= full and (full .. '.' .. id) or id 36 | if next(t) then -- Table not empty 37 | table.insert(out, tag .. '{') 38 | for key,value in pairs(t) do 39 | --table.insert(out,table_r(value,key,indent .. '| ',tableList[t])) 40 | table.insert(out,table_r(value,key,indent .. ' ',tableList[t])) 41 | end 42 | table.insert(out,indent .. '}') 43 | else table.insert(out,tag .. '{}') end 44 | end 45 | else 46 | local val = type(t)~="number" and type(t)~="boolean" and '"'..tostring(t)..'"' or tostring(t) 47 | table.insert(out, tag .. val) 48 | end 49 | return table.concat(out, '\n') 50 | end 51 | return table_r(t,name or 'Value',indent or '') 52 | end 53 | 54 | function pr (t, name) 55 | print(print_r(t,name)) 56 | end 57 | 58 | function print_table(name) 59 | print(print_r(_G[name], name)) 60 | end 61 | -------------------------------------------------------------------------------- /ir_index.h: -------------------------------------------------------------------------------- 1 | // Copyright 2016-2021 Lawrence Livermore National Security, LLC and other 2 | // IREP Project Developers. See the top-level LICENSE file for details. 3 | // 4 | // SPDX-License-Identifier: MIT 5 | 6 | #ifndef ir_index_h 7 | #define ir_index_h 8 | 9 | // These are definitions for IREP's internal index representation. 10 | // It's used by generated wkt indexes, but is not intended for use 11 | // by IREP users' code. 12 | 13 | // needed for size_t 14 | #include 15 | 16 | // Enum of all data types described by the IREP index. 17 | enum ir_type { 18 | T_int, 19 | T_dbl, 20 | T_log, 21 | T_str, 22 | T_cbk, 23 | T_tbl, 24 | T_ref, 25 | T_ptr 26 | }; 27 | 28 | 29 | // Descriptor for an IREP variable. 30 | typedef struct { 31 | const char *name; // Name of the variable. 32 | int ti; // If variable is itself a struct, index into ir_ta, or -1. 33 | size_t sz; // Size of (one element of) the variable. 34 | size_t off; // Offset of the variable in its enclosing struct. 35 | int len; // Max length for string variable; include trailing null. 36 | int flb; // Fortran lower bound, if array. 37 | int fub; // Fortran upper bound, if array. Zero for scalar. 38 | int typ; // Type code for the variable. See Typ above. 39 | } ir_element; 40 | 41 | 42 | // Descriptor for an IREP well known table. 43 | typedef struct { 44 | void *p; // Address of the table instance. 45 | ir_element e; // As above. 46 | } ir_wkt_desc; 47 | 48 | 49 | // These lookup tables need to be generated by irep-generate for the entire 50 | // program, and must include *all* wkt's. See irep-generate for details; 51 | // linking irep into a program that does not define ir_wktt and ir_ta will 52 | // result in link failure. 53 | 54 | // list of all well known tables and sub-tables 55 | extern ir_element *ir_ta[]; 56 | 57 | // the index of top-level wkt's (this is where ir_read looks to figure out 58 | // where to write things) 59 | extern ir_wkt_desc ir_wktt[]; 60 | 61 | // total number of wkt's in the index 62 | extern size_t ir_wktt_size; 63 | 64 | 65 | #endif // ir_index_h 66 | -------------------------------------------------------------------------------- /docs/wkt/mesh.rst: -------------------------------------------------------------------------------- 1 | =============================================================== 2 | The ``mesh`` table 3 | =============================================================== 4 | 5 | .. include:: ./mesh/introduction.rst 6 | 7 | 8 | 9 | .. include:: 10 | 11 | .. |br| raw:: html 12 | 13 |
14 | 15 | .. list-table:: 16 | :widths: 15 5 80 17 | :header-rows: 1 18 | 19 | * - Name / Type 20 | - Default 21 | - Description 22 | * - .. _mesh.amr: 23 | 24 | **amr** |br| :ref:`boolean ` 25 | - ``false`` 26 | - Use amr? 27 | * - .. _mesh.file: 28 | 29 | **file** |br| :ref:`string(256) ` 30 | - ``'none'`` 31 | - Name of mesh 32 | * - .. _mesh.refinement_level: 33 | 34 | **refinement_level** |br| :ref:`integer ` 35 | - ``0`` 36 | - 37 | 38 | .. _mesh.subtable.box: 39 | 40 | subtable ``box`` 41 | --------------------------------------------------------------- 42 | 43 | .. list-table:: 44 | :widths: 15 5 80 45 | :header-rows: 1 46 | 47 | * - Name / Type 48 | - Default 49 | - Description 50 | * - .. _mesh.box.nx: 51 | 52 | **nx** |br| :ref:`integer ` 53 | - ``1`` 54 | - Number of grid cells in 1st dimension 55 | * - .. _mesh.box.ny: 56 | 57 | **ny** |br| :ref:`integer ` 58 | - ``1`` 59 | - Number of grid cells in 2nd dimension 60 | * - .. _mesh.box.nz: 61 | 62 | **nz** |br| :ref:`integer ` 63 | - ``1`` 64 | - Number of grid cells in 3rd dimension 65 | * - .. _mesh.box.xmax: 66 | 67 | **xmax** |br| :ref:`double ` 68 | - ``1.0`` 69 | - Location of right-most cell face 70 | * - .. _mesh.box.xmin: 71 | 72 | **xmin** |br| :ref:`double ` 73 | - ``0.0`` 74 | - Location of left-most cell face 75 | * - .. _mesh.box.ymax: 76 | 77 | **ymax** |br| :ref:`double ` 78 | - ``1.0`` 79 | - Location of farthest cell face 80 | * - .. _mesh.box.ymin: 81 | 82 | **ymin** |br| :ref:`double ` 83 | - ``0.0`` 84 | - Location of nearest cell face 85 | * - .. _mesh.box.zmax: 86 | 87 | **zmax** |br| :ref:`double ` 88 | - ``1.0`` 89 | - Location of highest cell face 90 | * - .. _mesh.box.zmin: 91 | 92 | **zmin** |br| :ref:`double ` 93 | - ``0.0`` 94 | - Location of lowest cell face 95 | -------------------------------------------------------------------------------- /ir_extern.h: -------------------------------------------------------------------------------- 1 | // Copyright 2016-2021 Lawrence Livermore National Security, LLC and other 2 | // IREP Project Developers. See the top-level LICENSE file for details. 3 | // 4 | // SPDX-License-Identifier: MIT 5 | 6 | #ifndef ir_extern_h 7 | #define ir_extern_h 8 | 9 | #if defined(IREP_LANG_FORTRAN) 10 | module ir_extern 11 | use, intrinsic :: iso_c_binding 12 | use ir_std 13 | implicit none 14 | private 15 | public :: ir_read, ir_exists, ir_rtlen, ir_nprm, ir_nret, ir_unread 16 | public :: ir_get_function_name 17 | public :: lua_cb_data 18 | 19 | interface ! Let Fortran call C functions ir_read, ir_exists, ir_rtlen. 20 | integer(c_int) function ir_read(L, t) bind(c, name="ir_read") 21 | use iso_c_binding 22 | type(c_ptr), value :: L 23 | character(kind=c_char), dimension(*) :: t 24 | end function 25 | integer(c_int) function ir_unread(L, t) bind(c, name="ir_unread") 26 | use iso_c_binding 27 | type(c_ptr), value :: L 28 | character(kind=c_char), dimension(*) :: t 29 | end function 30 | integer(c_int) function ir_exists(L, t) bind(c, name="ir_exists") 31 | use iso_c_binding 32 | type(c_ptr), value :: L 33 | character(kind=c_char), dimension(*) :: t 34 | end function 35 | function ir_get_stringref(L, n, len) bind(c, name="ir_get_stringref") 36 | use, intrinsic :: iso_c_binding, only : c_ptr, c_int 37 | type(c_ptr), value :: L 38 | integer(kind=c_int), value :: n 39 | type(c_ptr), value :: len 40 | type(c_ptr) :: ir_get_stringref 41 | end function 42 | integer(c_int) function ir_rtlen(L, t) bind(c, name="ir_rtlen") 43 | use iso_c_binding 44 | type(c_ptr), value :: L 45 | character(kind=c_char), dimension(*) :: t 46 | end function 47 | integer(c_int) function ir_nprm(npnr) bind(c, name="ir_nprm") 48 | use iso_c_binding 49 | integer(c_int), value :: npnr 50 | end function 51 | integer(c_int) function ir_nret(npnr) bind(c, name="ir_nret") 52 | use iso_c_binding 53 | integer(c_int), value :: npnr 54 | end function 55 | function ir_get_function_name(L, p) bind(c, name="ir_get_function_name") 56 | use iso_c_binding 57 | type(c_ptr), value :: L 58 | type(c_ptr), value :: p 59 | type(c_ptr) :: ir_get_function_name 60 | end function 61 | end interface 62 | 63 | end module 64 | 65 | #else 66 | #include "ir_std.h" 67 | 68 | #if defined(__cplusplus) 69 | extern "C" { 70 | #endif 71 | 72 | // These IR functions are intended to be visible in client C/C++ code. 73 | extern int ir_read(lua_State *L, const char *t); 74 | extern int ir_unread(lua_State *L, const char *t); 75 | extern int ir_exists(lua_State *L, const char *t); 76 | extern int ir_rtlen(lua_State *L, const char *s); 77 | extern int ir_nprm(int npnr); 78 | extern int ir_nret(int npnr); 79 | extern char *ir_get_function_name(lua_State *L,void *p); 80 | extern char *ir_get_stringref(lua_State *L,int n, int *len); 81 | 82 | #if defined(__cplusplus) 83 | } 84 | #endif 85 | 86 | #endif 87 | 88 | #endif 89 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright 2016-2021 Lawrence Livermore National Security, LLC and other 2 | # IREP Project Developers. See the top-level LICENSE file for details. 3 | # 4 | # SPDX-License-Identifier: MIT 5 | 6 | # To use this cmakefile Lua needs to be installed, or you can add Lua's 7 | # prefix to CMAKE_PREFIX_PATH 8 | 9 | cmake_minimum_required(VERSION 3.1) 10 | project(irep LANGUAGES C Fortran) 11 | 12 | # Find lua and figure out bin dir 13 | find_package(Lua REQUIRED) 14 | string(REGEX REPLACE "/include$" "/bin" LUA_BIN ${LUA_INCLUDE_DIR}) 15 | 16 | # Set up IREP_GENERATE varaible 17 | include(${CMAKE_CURRENT_SOURCE_DIR}/share/irep/irep-config.cmake) 18 | 19 | # Be sure to include lua headers 20 | include_directories( 21 | ${CMAKE_CURRENT_SOURCE_DIR} 22 | ${LUA_INCLUDE_DIR} 23 | ) 24 | 25 | # ir_std.f & ir_extern.f is generated from coresponding 26 | # headers using IREP_GENERATE 27 | add_custom_command( 28 | OUTPUT ir_std.f 29 | COMMAND 30 | ${CMAKE_COMMAND} -E env PATH="${LUA_BIN}:$ENV{PATH}" 31 | ${IREP_GENERATE} --mode fortran 32 | ${CMAKE_CURRENT_SOURCE_DIR}/ir_std.h > ir_std.f 33 | ) 34 | 35 | add_custom_command( 36 | DEPENDS ir_std.f 37 | OUTPUT ir_extern.f 38 | COMMAND 39 | ${CMAKE_COMMAND} -E env PATH="${LUA_BIN}:$ENV{PATH}" 40 | ${IREP_GENERATE} --mode fortran 41 | ${CMAKE_CURRENT_SOURCE_DIR}/ir_extern.h > ir_extern.f 42 | ) 43 | 44 | # All Fortran files have free formatting 45 | set(CMAKE_Fortran_FORMAT FREE) 46 | 47 | # Fortran modules should be output to the build directory 48 | set(CMAKE_Fortran_MODULE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) 49 | 50 | # libIR.a is mostly basic functions from irep.c. ir_extern.f, & ir_std.f 51 | # are very much like wkt headers. This ensures their tables are in 52 | # libIR.a. Note that they are automatically included in index libraries 53 | # by irep-config.cmake 54 | add_library( 55 | IR STATIC 56 | irep.c 57 | ${CMAKE_CURRENT_BINARY_DIR}/ir_extern.f 58 | ${CMAKE_CURRENT_BINARY_DIR}/ir_std.f 59 | ) 60 | 61 | # Install irep-generate in bin dir 62 | install( 63 | PROGRAMS bin/irep-generate 64 | DESTINATION ${CMAKE_INSTALL_PREFIX}/bin 65 | ) 66 | 67 | # Install libIR.a in lib dir 68 | install( 69 | TARGETS IR 70 | DESTINATION ${CMAKE_INSTALL_PREFIX}/lib 71 | ) 72 | 73 | # Headers and Fortran modules go in include 74 | install( 75 | FILES 76 | # headers 77 | ir_end.h ir_extern.h ir_index.h ir_macros.h ir_start.h 78 | ir_std.h ir_undef.h 79 | # fortran modules 80 | ${CMAKE_Fortran_MODULE_DIRECTORY}/ir_std.mod 81 | ${CMAKE_Fortran_MODULE_DIRECTORY}/ir_extern.mod 82 | DESTINATION ${CMAKE_INSTALL_PREFIX}/include 83 | ) 84 | 85 | # Install share/irep/* in share/irep dir 86 | install( 87 | FILES 88 | share/irep/irep-config.cmake 89 | share/irep/wkt.mk 90 | DESTINATION ${CMAKE_INSTALL_PREFIX}/share/irep 91 | ) 92 | 93 | # Install doc files needed to build docs from an installed irep 94 | install( 95 | FILES 96 | docs/irep_types.rst 97 | DESTINATION ${CMAKE_INSTALL_PREFIX}/docs 98 | ) 99 | -------------------------------------------------------------------------------- /bin/l2ir: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env lua 2 | -- Copyright 2016-2021 Lawrence Livermore National Security, LLC and other 3 | -- IREP Project Developers. See the top-level LICENSE file for details. 4 | -- 5 | -- SPDX-License-Identifier: MIT 6 | 7 | -- Convert lua tables into IR macros. 8 | -- 9 | -- For scalars, the type and value of the assigned value dictates the 10 | -- type and default value of the IR macro output. 11 | -- a = 3 -- is an integer named "a" with default value "3" 12 | -- b = 3.14 -- is a double, with default "3.14". 13 | -- 14 | -- For vectors, we expect the type of v[1] to be the type of the vector, 15 | -- v[2] to be the number of elements desired, and v[3] the default value. 16 | -- aa = { 1, 10, 3 } -- is integer, with 10 elements, default == 3. 17 | -- 18 | -- Note that vector strings don't currently accept default values. For 19 | -- string vectors, v[1] is type, v[2] is the length, v[3] is nelems. 20 | -- 21 | -- Functions need to be just functions: f = function(x) return 1 end. 22 | -- Remember that scalar string lengths default to 32. 23 | 24 | 25 | if #arg ~= 2 then 26 | print("Usage: l2ir foo.lua tablename") 27 | return 28 | end 29 | 30 | local slen = 32 -- default string length 31 | local tlist = {} 32 | 33 | local function digest(pname, tname, t, lev) 34 | for k,v in pairs(t) do 35 | if type(v) == "table" then 36 | if #v == 0 then 37 | table.insert(tlist,{pname=pname,tname=k,val=v,lev=lev+1}) 38 | digest(k,k,v,lev+1) 39 | end 40 | end 41 | end 42 | end 43 | 44 | local function generate(t) 45 | for k,v in pairs(t.val) do 46 | if type(v) == "table" then 47 | if #v == 0 then 48 | io.write(" Structure(irt_", k, ",", k, ")\n") 49 | else 50 | if type(v[1]) == "string" then 51 | io.write(" Vir_str(",k,",",v[2],",",v[3],")\n") 52 | elseif type(v[1]) == "number" then 53 | if v[1]%1 ~= 0 then io.write(" Vir_dbl(",k,",",v[2],",",v[3],")\n") 54 | else io.write(" Vir_int(",k,",",v[2],",",v[3],")\n") 55 | end 56 | elseif type(v[1]) == "boolean" then 57 | if v[3] then io.write(" Vir_log(",k,",",v[2],",","true)\n") 58 | else io.write(" Vir_log(",k,",",v[2],",","false)\n") 59 | end 60 | else 61 | print("ERROR: ", k, v) 62 | end 63 | end 64 | elseif type(v) == "string" then 65 | io.write(" ir_str(",k,",",slen,",",'"',v,'"',")\n") 66 | elseif type(v) == "number" then 67 | if v%1 ~= 0 then io.write(" ir_dbl(",k,",",v,")\n") 68 | else io.write(" ir_int(",k,",",v,")\n") 69 | end 70 | elseif type(v) == "boolean" then 71 | if v then io.write(" ir_log(",k,",true)\n") 72 | else io.write(" ir_log(",k,",false)\n") 73 | end 74 | elseif type(v) == "function" then 75 | io.write(" Callback(",k,")\n") 76 | else 77 | print("ERROR: ", k, v) 78 | end 79 | end 80 | end 81 | 82 | dofile(arg[1]) 83 | table.insert(tlist, {pname="_G", tname=arg[2], val=_G[arg[2]],lev=0}) 84 | digest(arg[2], arg[2], _G[arg[2]], 0) 85 | 86 | table.sort(tlist, function(a,b) return a.lev > b.lev end) 87 | for k,v in ipairs(tlist) do 88 | io.write("Beg_struct(irt_", v.tname, ")\n") 89 | generate(v) 90 | io.write("End_struct(irt_", v.tname, ")\n\n") 91 | end 92 | -------------------------------------------------------------------------------- /examples/c/c_main.c: -------------------------------------------------------------------------------- 1 | // Copyright 2016-2021 Lawrence Livermore National Security, LLC and other 2 | // IREP Project Developers. See the top-level LICENSE file for details. 3 | // 4 | // SPDX-License-Identifier: MIT 5 | 6 | #include 7 | #include 8 | #include 9 | #if defined(__cplusplus) 10 | #include "lua.hpp" 11 | #else 12 | #include "lua.h" 13 | #include "lualib.h" 14 | #include "lauxlib.h" 15 | #endif 16 | #include "ir_extern.h" 17 | #include "wkt_table1.h" 18 | #include "wkt_table4.h" 19 | #include 20 | #include 21 | 22 | #if defined(__cplusplus) 23 | extern "C" { 24 | using namespace irep; 25 | #endif 26 | 27 | int eval(lua_State *L, lua_cb_data *d, double *x, double *v) { 28 | int i, nprm, nret; 29 | 30 | if (d->fref == LUA_REFNIL) return 1; 31 | 32 | nprm = ir_nprm(d->npnr); 33 | nret = ir_nret(d->npnr); 34 | if (d->fref == LUA_NOREF) { 35 | double *data = (double *)d->data; 36 | for (i=0; i < nret; ++i) v[i] = data[i]; 37 | return 0; 38 | } 39 | 40 | // Push the function onto the stack. 41 | lua_rawgeti(L, LUA_REGISTRYINDEX, d->fref); 42 | for (i=0; i < nprm; i++) lua_pushnumber(L, x[i]); // Push args. 43 | 44 | if (lua_pcall(L, nprm, nret, 0) != 0) 45 | return luaL_error(L, "error: %s", lua_tostring(L,-1)); 46 | 47 | for (i=0; i < nret; i++) { 48 | if (lua_type(L, i-nret) != LUA_TNUMBER) 49 | return luaL_error(L, "error: expected number for return value: %d", i+1); 50 | v[i] = lua_tonumber(L, i - nret); 51 | } 52 | lua_pop(L, nret); // Pop return values. 53 | return 0; 54 | } 55 | 56 | int main(int argc, char *argv[]) { 57 | lua_State *L = luaL_newstate(); 58 | int ios, i, n, j, ii; 59 | double x[3] = { 2.0, 3.0, 4.0 }, v=0.0, v2[2]; 60 | 61 | if (argc < 2) { 62 | fprintf(stderr, "Usage: %s *.lua\n", argv[0]); 63 | return 0; 64 | } 65 | luaL_openlibs(L); 66 | if (luaL_loadfile(L, argv[1]) || lua_pcall(L, 0, 0, 0)) 67 | return luaL_error(L, 68 | "cannot run configuration file: %s", lua_tostring(L,-1)); 69 | 70 | ios = ir_read(L, "table1"); 71 | printf("\nREAD TABLE1: ios=%d\n",ios); 72 | printf("table1.i = %d\n", table1.i); 73 | printf("table1.d = %g\n", table1.d); 74 | printf("table1.b = %d\n", table1.b); 75 | 76 | char *ss = strdup(IR_STR(table1.s)); 77 | printf("table1.s = \"%s\"\n", ss); 78 | 79 | i = eval(L, &table1.f1, x, &v); 80 | if (!i) printf("table1.f1(%g,%g,%g) = %g\n", x[0],x[1],x[2], v); 81 | else printf("f1 undefined\n"); 82 | 83 | i = eval(L, &table1.table2[1].f2, x, &v); 84 | if (!i) printf("table1.table2[1].f2(%g,%g,%g) = %g\n", x[0],x[1],x[2], v); 85 | else printf("f2 undefined\n"); 86 | 87 | i = eval(L, &table1.table3.f3, x, v2); 88 | if (!i) printf("table1.table3.f3(%g,%g,%g) = %g %g\n", x[0],x[1],x[2], v2[0],v2[1]); 89 | else printf("f3 undefined\n"); 90 | 91 | n = sizeof(table1.e)/sizeof(*table1.e); 92 | printf("\ntable1.e: %d elements, %d given\n", n, ir_rtlen(L, "table1.e")); 93 | for (i=0;i` can be used to auto-generate `sphinx 8 | `_ documentation for WKT headers. You can 9 | include the generated ``.rst`` files in your code's documentation. 10 | 11 | ``irep-generate --mode rst`` 12 | ---------------------------- 13 | 14 | To generate ``.rst`` files for WKT headers, run ``irep-generate --mode 15 | rst`` as follows: 16 | 17 | .. code-block:: console 18 | 19 | $ irep-generate --mode rst wkt_foo.h wkt_bar.h wkt_baz.h 20 | 21 | This will generate ``foo.rst``, ``bar.rst``, and ``baz.rst``, and it will 22 | also copy in an ``irep_types.rst`` file that describes IREP data types. 23 | ``foo.rst`` and friends will contain referencs to type descriptions in 24 | this file. 25 | 26 | You can see what the generated documentation looks like here: 27 | 28 | .. toctree:: 29 | 30 | wkt/mesh 31 | 32 | ``Doc`` macros 33 | ------------------ 34 | 35 | You can annotate any WKT header with a ``Doc`` macro like this:: 36 | 37 | ir_int(nx, 1) Doc(( Number of grid cells in 1st dimension )) 38 | 39 | If ``irep-generate`` finds a ``Doc`` string in an IREP header, it is 40 | carried into the **Description** field in tables in the generated IREP 41 | documentation. ``Doc`` strings can be considered free-form RST, so you 42 | can use inline makup to create more complex descriptions. For example, if 43 | you wanted to use the RST ``::`` construct to include some preformatted 44 | text, you coudl do that like this:: 45 | 46 | ir_int(nx, 1) Doc(( 47 | 48 | :: 49 | 50 | This is a longer descrpition: 51 | - thing one 52 | - thing two 53 | - etc. 54 | )) 55 | 56 | Basically, the ``Doc`` string is one paragraph of text, and will be 57 | reformatted (nicely) in the HTML output. 58 | 59 | Introductions 60 | ------------- 61 | 62 | You can have IREP include introductory text in your table definitions. If 63 | you are generating code for ``wkt_mesh.h``, and ``irep-generate finds 64 | ``mesh/introduction.rst`` in the current directory, then it will be 65 | included at the top of the documentation for ``wkt_mesh.h``. 66 | 67 | By default, ``irep-generate`` searches the current directory, but you can 68 | tell it to look else where with ``--doc-dir ``. 69 | 70 | Thre is an example ``introduction.rst`` file in the documentation for 71 | IREP itself. See that for an example for how you might set up your own 72 | docs. 73 | 74 | Details files 75 | ------------- 76 | 77 | If additional details for elements, or groups of elements, are desired, 78 | you can write a *details file*. These go alongside the 79 | ``introduction.rst`` files described above. If ``irep-generate`` finds a 80 | details file under ``mesh/`` when generating code for ``wkt_mesh.h``, it 81 | will generate a link to it in the generated documentation. 82 | 83 | The naming of the details file follows a simple rule. For example, 84 | consider the parameter:: 85 | 86 | material.opacity.polynomial.absorption.e_pow 87 | 88 | The following filenames are searched in order: 89 | 90 | 1. ``mesh/box-xmax.rst`` 91 | 2. ``mesh/box.rst`` 92 | 3. ``mesh/mesh.rst`` 93 | 94 | That is, the search first looks for the most specific possible file. If 95 | your tables are deeper than our examples, you can keep adding levels 96 | separate by dashes (``-``). If one of these names exists, it is linked as 97 | the "details" file for that parameter. If not, the search goes to the 98 | closest surrounding subtable. (So you could give details on all the 99 | parameters in the "absorption" table by making a details file with the 100 | second name in the list above.) And so on. The search ends by looking for 101 | a details file "material.rst". If none are found, no details link is made 102 | for the element. 103 | 104 | As with ``introduction.rst``, you can use the ``--doc-dir`` parameter to 105 | ``irep-generate`` to tell it where to look. 106 | -------------------------------------------------------------------------------- /share/irep/wkt.mk: -------------------------------------------------------------------------------- 1 | # Copyright 2016-2021 Lawrence Livermore National Security, LLC and other 2 | # IREP Project Developers. See the top-level LICENSE file for details. 3 | # 4 | # SPDX-License-Identifier: MIT 5 | 6 | # wkt.mk should be included by makefiles that need to build IREP WKT 7 | # libraries. 8 | # 9 | # To build a wkt library, e.g., `libfoo-wkt.a`, simply include this file, 10 | # define a variable `foo.wkt_src`, and add the WKT library as a make target. 11 | # For example: 12 | # 13 | # include /path/to/wkt.mk 14 | # foo.wkt_src = wkt_shapes.h wkt_colors.h wkt_numbers.h 15 | # all: libfoo-wkt.a 16 | # 17 | # The wkt library will be generated and built from the provided WKT headers. 18 | # 19 | # You can need to set some standard GNU Make variables to control how 20 | # WKT libs are generated: 21 | # 22 | # CC: Full path to C compiler. 23 | # FC: Full path to Fortran compiler. 24 | # CFLAGS: Compiler flags for C. 25 | # FFLAGS: Compiler flags for Fortran. 26 | # CPPFLAGS: Flags for the C preprocessor. 27 | # 28 | # You can use these variables to inject system Lua configuration. 29 | # You can use these in makefiles that import wkt.mk. 30 | # 31 | # LUA_INCLUDE: path to directory containing lua.h 32 | # LUA_LIBRARIES: path to directory containing liblua 33 | # 34 | # It's less likely that you'll need to update these: 35 | # 36 | # AR: implicitly provided by GNU Make 37 | # RANLIB: ranlib program (optional) 38 | 39 | CC = gcc 40 | FC = gfortran 41 | CFLAGS = -O2 -g 42 | FFLAGS = -O2 -g -cpp -ffree-form -ffree-line-length-0 -fbackslash 43 | 44 | # RANLIB can be optionally set 45 | RANLIB = $(shell command -v ranlib 2> /dev/null || true) 46 | 47 | # like $(dir), but remove trailing slash and handle / 48 | dirname = $(if $(patsubst %/,%,$(dir $(1))),$(patsubst %/,%,$(dir $(1))),/) 49 | 50 | # By default, assume lua is the one in PATH, and that its includes are in 51 | # $lua_root/include and libs are in $lua_root/lib. Lua varies quite a bit 52 | # from system to system, so you may need to inject LUA_INCLUDE and 53 | # LUA_LIBRARIES yourself. 54 | LUA := $(shell command -v lua 2> /dev/null) 55 | LUA_ROOT := $(call dirname,$(call dirname,$(LUA))) 56 | LUA_INCLUDE := -I$(LUA_ROOT)/include 57 | $(info $(LUA_INCLUDE)) 58 | LUA_LIBRARIES := -L$(LUA_ROOT)/lib -llua 59 | 60 | # find location of this makefile so we can find irep tools 61 | irep_dir := $(realpath $(dir $(lastword $(MAKEFILE_LIST)))/../..) 62 | 63 | # ensure generator can find lua and headers from irep 64 | CPPFLAGS += $(LUA_INCLUDE) -I$(irep_dir)/include -I. 65 | export CPPFLAGS 66 | 67 | # utility program for generating code from wkt.h files 68 | irep_generate = $(irep_dir)/bin/irep-generate 69 | 70 | # Rules for compiling C and Forran files -- note that the appropriate 71 | # IREP -D flag must be set for files that include IREP headers. 72 | COMPILE.c = $(CC) $(CFLAGS) $(CPPFLAGS) 73 | COMPILE.f = $(FC) $(FFLAGS) $(CPPFLAGS) -DIREP_LANG_FORTRAN 74 | %.o: %.c ; $(COMPILE.c) -c $< 75 | %.o %.mod: %.f ; $(COMPILE.f) -c $< 76 | 77 | # override gmake's default assumption that .mod is a modula file 78 | # without this, you'll get warnings about cicular dependencies and 79 | # gmake will try to run m2c on module files. 80 | %.o: %.mod 81 | 82 | # generate fortran from every wkt_%.h file 83 | wkt_%.f: wkt_%.h 84 | $(irep_generate) --mode fortran $< > $@ 85 | 86 | # helper function for finding wkt files with absolute paths 87 | containing = $(foreach v,$2,$(if $(findstring $1,$v),$v)) 88 | 89 | # These functions define the pieces of a wkt library. 90 | # They're defined as functions so they can be used as prerequisites 91 | # in our doubly expanded pattern rule for WKT libs below. 92 | ir-wkt = $(filter %.h,$(call containing,wkt_,$($(1)))) 93 | ir-wobj = $(subst .h,.o,$(foreach v,$(call ir-wkt,$(1)),$(notdir $v))) 94 | 95 | # add a little flair to the output 96 | cgreen = "\033[1;32m" 97 | cend = "\033[0m" 98 | 99 | # Any target like libfoo-wkt.a will be built like a wkt library. 100 | # The user needs only define foo.wkt_src to make the magic happen. 101 | .SECONDEXPANSION: 102 | lib%-wkt.a: $$(call ir-wobj,$$*.wkt_src) 103 | $(if $^,,$(error "No wkt_*.h files were provided for '$@'. Did you set $*.wtk_src?")) 104 | $(AR) -rc $@ $^ 105 | $(RANLIB) $@ 106 | @echo -e Successfully created IREP WKT library $(cgreen)$@$(cend). 107 | $(info $^) 108 | 109 | %-wkt-index.c: $$(call ir-wkt,$$*.wkt_index_src) 110 | $(if $^,,$(error "No wkt_*.h files were provided for '$@'. Did you set $*.wtk_index_src?")) 111 | $(irep_generate) $(irep_dir)/ir_std.h $^ > $@ 112 | 113 | # The index library contains a set of tables that allow us to look up 114 | # wkt structs by name. 115 | lib%-wkt-index.a: $$*-wkt-index.o 116 | $(AR) -rc $@ $(filter %.o,$^) 117 | $(RANLIB) $@ 118 | @echo -e Successfully created IREP WKT index library $(cgreen)$@$(cend). 119 | -------------------------------------------------------------------------------- /examples/cxx-cmake/cxx_main.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2016-2021 Lawrence Livermore National Security, LLC and other 2 | // IREP Project Developers. See the top-level LICENSE file for details. 3 | // 4 | // SPDX-License-Identifier: MIT 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include "lua.hpp" 11 | #include "ir_std.h" // lua_cb_data 12 | #include "ir_extern.h" 13 | #include "wkt_table1.h" 14 | #include "wkt_table4.h" 15 | 16 | #if 0 17 | static void itemdump(lua_State *L, int i, char c) { 18 | int t = lua_type(L, i); 19 | switch (t) { 20 | case LUA_TSTRING: { 21 | printf("%d: (string): %s%c", i, lua_tostring(L,i), c); 22 | break; 23 | } 24 | case LUA_TBOOLEAN: { 25 | printf("%d: (bool): %s%c", 26 | i, lua_toboolean(L, i) ? "true":"false", c); 27 | break; 28 | } 29 | case LUA_TNUMBER: { 30 | printf("%d: (number): %g%c", i, lua_tonumber(L, i), c); 31 | break; 32 | } 33 | default: { 34 | printf("%d: (%s): %p%c", 35 | i, lua_typename(L, t), lua_topointer(L,i), c); 36 | break; 37 | } 38 | } 39 | } 40 | static void stackdump(lua_State *L) { 41 | int i, top = lua_gettop(L); 42 | for (i=1; i<= top; i++) itemdump(L, i, '|'); 43 | printf("\n"); 44 | } 45 | #endif 46 | 47 | class LuaCallback { 48 | public: 49 | LuaCallback(lua_State *L, lua_cb_data &d); 50 | ~LuaCallback(){} 51 | virtual int Evaluate(std::vector &v, const std::vector &x); 52 | virtual double Evaluate(const std::vector &x); 53 | 54 | private: 55 | lua_State *L; 56 | lua_cb_data &d; 57 | int nprm; 58 | int nret; 59 | }; 60 | 61 | // Constructor. 62 | LuaCallback::LuaCallback(lua_State *L, lua_cb_data &d) : L(L), d(d) { 63 | nprm = ir_nprm(d.npnr); 64 | nret = ir_nret(d.npnr); 65 | } 66 | 67 | // Evaluator for scalar returns. 68 | double LuaCallback::Evaluate(const std::vector &x) { 69 | std::vector v(1); 70 | (void)LuaCallback::Evaluate(v, x); 71 | return v[0]; 72 | } 73 | 74 | // Evaluator for vector returns. 75 | int LuaCallback::Evaluate(std::vector &v, const std::vector &x) 76 | { 77 | int actual_nret = nret; 78 | 79 | if (d.fref == LUA_NOREF) { 80 | // In the LUA_NOREF case, nret always has the actual length of d.data. 81 | // (Even if nret was originally specified as -1 in the wkt file.) 82 | if (nret > (int)v.size()) { 83 | fprintf(stderr,"ERROR: v is too small\n"); exit(1); 84 | } 85 | double *data = (double *)d.data; 86 | for (int i=0; i (int)x.size()) { 90 | fprintf(stderr,"ERROR: not enough parameters given\n"); exit(1); 91 | } 92 | lua_rawgeti(L, LUA_REGISTRYINDEX, d.fref); 93 | for (int i=0; i (int)v.size()) { 105 | fprintf(stderr,"ERROR: v is too small for table returned\n"); exit(1); 106 | } 107 | for (int i=1; i<=actual_nret; i++) { 108 | lua_rawgeti(L,-1,i); 109 | if (lua_type(L,-1) != LUA_TNUMBER) { 110 | fprintf(stderr,"ERROR: table item is not number: %d\n", i); exit(1); 111 | } 112 | v[i-1] = lua_tonumber(L,-1); 113 | lua_pop(L,1); 114 | } 115 | lua_pop(L,1); 116 | 117 | } else { // Standard return on the stack. 118 | for (int i=0; i x (arr, arr+sizeof(arr)/sizeof(arr[0])); 137 | std::vector v(3); 138 | 139 | if (argc < 2) { 140 | fprintf(stderr, "Usage: %s *.lua\n", argv[0]); 141 | return 0; 142 | } 143 | luaL_openlibs(L); 144 | if (luaL_loadfile(L, argv[1]) || lua_pcall(L, 0, 0, 0)) 145 | return luaL_error(L, 146 | "cannot run configuration file: %s", lua_tostring(L,-1)); 147 | 148 | ios = ir_read(L, "table1"); 149 | printf("\nREAD TABLE1: ios=%d\n",ios); 150 | printf("table1.i = %d\n", irep::table1.i); 151 | printf("table1.d = %g\n", irep::table1.d); 152 | printf("table1.b = %d\n", irep::table1.b); 153 | 154 | LuaCallback *f1 = new LuaCallback(L, irep::table1.f1); 155 | dd = f1->Evaluate(x); 156 | printf("return from f1: %g\n", dd); 157 | 158 | LuaCallback *f4 = new LuaCallback(L, irep::table1.f4); 159 | i = f4->Evaluate(v, x); 160 | printf("return from f4: %d, %g\n", i,v[0]); 161 | 162 | LuaCallback *f5 = new LuaCallback(L, irep::table1.f5); 163 | i = f5->Evaluate(v, x); 164 | printf("return from f5: %d, %g %g %g\n", i,v[0],v[1],v[2]); 165 | printf("Full name for f5: %s\n", ir_get_function_name(L,&irep::table1.f5)); 166 | 167 | return 0; 168 | } 169 | -------------------------------------------------------------------------------- /share/irep/irep-config.cmake: -------------------------------------------------------------------------------- 1 | # Copyright 2016-2021 Lawrence Livermore National Security, LLC and other 2 | # IREP Project Developers. See the top-level LICENSE file for details. 3 | # 4 | # SPDX-License-Identifier: MIT 5 | 6 | # IREP lives two directories above this config file 7 | get_filename_component(irep_DIR ${CMAKE_CURRENT_LIST_DIR}/../.. ABSOLUTE) 8 | 9 | # location of the irep-generate executable 10 | set(IREP_GENERATE ${irep_DIR}/bin/irep-generate) 11 | 12 | # location of the irep-generate executable 13 | set(IREP_LIBRARIES ${irep_DIR}/lib/libIR.a) 14 | 15 | # location of the irep-generate executable 16 | set(IREP_INCLUDE_DIR ${irep_DIR}/include) 17 | 18 | # add_wkt_library() 19 | # 20 | # Generate Fortran modules and a WKT library from wkt_*.h headers. 21 | # 22 | # Usage: 23 | # 24 | # add_wkt_library( 25 | # name-wkt # name of WKT library 26 | # wkt_foo.h wkt_bar.h ... # non-generated wkt headers 27 | # [GENERATED wkt_gen1.h ...] # generated wkt headers (optional) 28 | # ) 29 | # 30 | # Output variables: 31 | # NAME_FFILES Fortran files that went into libname-wkt.a 32 | # NAME_MODFILES Fortran modules generated from NAME_FFILES 33 | # 34 | function(add_wkt_library name) 35 | cmake_parse_arguments(WKT_LIB "" "" "GENERATED" ${ARGN}) 36 | set(WKT_HEADERS ${WKT_LIB_UNPARSED_ARGUMENTS}) 37 | 38 | set(WKT_FFILES "") 39 | set(WKT_MODFILES "") 40 | list(APPEND WKT_HEADERS "${WKT_LIB_GENERATED}") 41 | foreach(WKT_H ${WKT_HEADERS}) 42 | if(NOT WKT_H MATCHES ".h$") 43 | message(FATAL_ERROR "Invalid WKT header name: '${WKT_H}'") 44 | endif() 45 | 46 | # get abspath to WKT_H to handle wkt's in source dir correctly 47 | get_filename_component(WKT_H "${WKT_H}" ABSOLUTE) 48 | 49 | string(REGEX REPLACE ".h$" ".f" WKT_F "${WKT_H}") 50 | get_filename_component(WKT_F "${WKT_F}" NAME) 51 | list(APPEND WKT_FFILES "${WKT_F}") 52 | 53 | string(REGEX REPLACE ".h$" ".mod" WKT_MOD "${WKT_H}") 54 | get_filename_component(WKT_MOD "${WKT_MOD}" NAME) 55 | list(APPEND WKT_MODFILES "${WKT_MOD}") 56 | 57 | set_source_files_properties( 58 | ${WKT_F} PROPERTIES 59 | Fortran_FORMAT FREE 60 | COMPILE_FLAGS -DIREP_LANG_FORTRAN -assume bscc 61 | ) 62 | 63 | # ensure that CPPFLAGS is set to include lib target properties 64 | set(incl "$") 65 | set(defs "$") 66 | list(APPEND WKT_LIB_CPPFLAGS 67 | "$<$:-I$-I>>" 68 | "$<$:-D$-D>>" 69 | ) 70 | add_custom_command( 71 | OUTPUT ${WKT_F} 72 | DEPENDS ${WKT_H} 73 | COMMAND 74 | ${CMAKE_COMMAND} -E env CPPFLAGS="${WKT_LIB_CPPFLAGS}" 75 | ${IREP_GENERATE} --mode fortran ${WKT_H} > ${WKT_F} 76 | COMMAND_EXPAND_LISTS 77 | ) 78 | endforeach() 79 | 80 | add_library("${name}" STATIC ${WKT_FFILES} ${WKT_LIB_GENERATED}) 81 | set_target_properties("${name}" PROPERTIES LINKER_LANGUAGE C) 82 | target_include_directories( 83 | "${name}" PUBLIC 84 | ${IREP_INCLUDE_DIR} 85 | $ 86 | $ 87 | ) 88 | 89 | # convert the target name to a typical variable identifier 90 | # (uppercase, no dashes, etc.) 91 | string(MAKE_C_IDENTIFIER "${name}" varname) 92 | string(TOUPPER "${varname}" varname) 93 | 94 | # set some output variables for this function using the identifier we made 95 | set(${varname}_FFILES "${WKT_FFILES}" PARENT_SCOPE) 96 | set(${varname}_MODFILES "${WKT_MODFILES}" PARENT_SCOPE) 97 | endfunction() 98 | 99 | 100 | # add_wkt_index_library() 101 | # 102 | # Generate an irep WKT index library from a list of WKT files. 103 | # 104 | # Usage: 105 | # 106 | # add_wkt_index_library( 107 | # name-index-wkt # name of WKT index library 108 | # wkt_foo.h wkt_bar.h ... # non-generated wkt headers 109 | # [GENERATED wkt_gen1.h ...] # generated wkt headers (optional) 110 | # ) 111 | # 112 | function(add_wkt_index_library name) 113 | cmake_parse_arguments(WKT_INDEX "" "" "GENERATED" ${ARGN}) 114 | set(WKT_HEADERS ${WKT_INDEX_UNPARSED_ARGUMENTS}) 115 | 116 | list(APPEND WKT_HEADERS "${WKT_INDEX_GENERATED}") 117 | set(list_WKT_HEADERS "") 118 | foreach(WKT_H ${WKT_HEADERS}) 119 | if(NOT WKT_H MATCHES ".h$") 120 | message(FATAL_ERROR "Invalid WKT header name: '${WKT_H}'") 121 | endif() 122 | 123 | get_filename_component(WKT_H ${WKT_H} ABSOLUTE) 124 | list(APPEND list_WKT_HEADERS ${WKT_H}) 125 | endforeach() 126 | 127 | # name of single C source file for index library 128 | set(WKT_INDEX_C "${name}.c") 129 | 130 | # ensure that CPPFLAGS is set to include lib target properties 131 | set(incl "$") 132 | set(defs "$") 133 | list(APPEND WKT_INDEX_CPPFLAGS 134 | "$<$:-I$-I>>" 135 | "$<$:-D$-D>>" 136 | ) 137 | 138 | add_custom_command( 139 | OUTPUT ${WKT_INDEX_C} 140 | DEPENDS ${WKT_HEADERS} 141 | COMMAND 142 | ${CMAKE_COMMAND} -E env CPPFLAGS="${WKT_INDEX_CPPFLAGS}" 143 | ${IREP_GENERATE} --mode index ${WKT_HEADERS} > ${WKT_INDEX_C} 144 | COMMAND_EXPAND_LISTS 145 | ) 146 | 147 | add_library("${name}" STATIC ${WKT_INDEX_C} ${WKT_INDEX_GENERATED}) 148 | set_target_properties("${name}" PROPERTIES LINKER_LANGUAGE C) 149 | target_include_directories( 150 | "${name}" PUBLIC 151 | ${IREP_INCLUDE_DIR} 152 | ${CMAKE_Fortran_MODULE_DIRECTORY} 153 | ${CMAKE_CURRENT_SOURCE_DIR} 154 | ) 155 | endfunction() 156 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # IREP 2 | 3 | [![linux builds](https://github.com/LLNL/irep/actions/workflows/linux_build_tests.yaml/badge.svg)](https://github.com/LLNL/irep/actions/workflows/linux_build_tests.yaml) 4 | [![Documentation Status](https://readthedocs.org/projects/irep/badge/?version=latest)](https://irep.readthedocs.io/en/latest/?badge=latest) 5 | 6 | IREP is a tool that enables mixed-language simulation codes to use a 7 | common, Lua-based format for their input decks. Essentially, the input 8 | format is a set of [tables](https://www.lua.org/pil/2.5.html) -- Lua's 9 | one (and only?) data structure. IREP is an *intermediate representation* 10 | that is used to generate plain-old-data (POD) structures in C, C++, 11 | Fortran, and Lua. 12 | 13 | ## Documentation 14 | 15 | There are some high-level docs here to get you started, but you can find 16 | more in the [full IREP Documentation](https://irep.readthedocs.io/). 17 | 18 | ## Installation 19 | 20 | To build IREP, run `make` in the root directory. This will create 21 | `libIR.a`. IREP does not (yet) have a proper installation target. 22 | 23 | ## Building with IREP 24 | 25 | IREP has integration for both CMake and gmake-based builds. You can find 26 | more about how to intgrate IREP into your project here: 27 | 28 | * [IREP Build Docs](https://irep.readthedocs.io/en/latest/build.html) on ReadTheDocs. 29 | * [IREP Examples](https://github.com/LLNL/irep/tree/master/examples) in this repository. 30 | 31 | You can build and run the examples by running running `make test` in the 32 | root of this repository. 33 | 34 | ## Basics 35 | 36 | To use IREP, you can write a header, like this one for defining a 37 | very simple structured mesh. Let's call it `wkt_mesh.h`: 38 | 39 | ```c 40 | #ifndef wkt_mesh_h 41 | #define wkt_mesh_h 42 | #include "ir_start.h" 43 | 44 | Beg_struct(irt_box) 45 | ir_int(nx, 1) Doc(( Number of grid cells in 1st dimension )) 46 | ir_int(ny, 1) Doc(( Number of grid cells in 2nd dimension )) 47 | ir_int(nz, 1) Doc(( Number of grid cells in 3rd dimension )) 48 | ir_dbl(xmin, 0.0) Doc(( Location of left-most cell face )) 49 | ir_dbl(xmax, 1.0) Doc(( Location of right-most cell face )) 50 | ir_dbl(ymin, 0.0) Doc(( Location of nearest cell face )) 51 | ir_dbl(ymax, 1.0) Doc(( Location of farthest cell face )) 52 | ir_dbl(zmin, 0.0) Doc(( Location of lowest cell face )) 53 | ir_dbl(zmax, 1.0) Doc(( Location of highest cell face )) 54 | End_struct(irt_box) 55 | 56 | Beg_struct(irt_mesh) 57 | ir_str(file, FILENAMESIZE, 'none') Doc(( Name of mesh )) 58 | ir_int(refinement_level, 0) 59 | ir_log(amr, false) Doc(( Use amr? )) 60 | Structure(irt_box,box) 61 | End_struct(irt_mesh) 62 | 63 | // Declare the structure 64 | ir_wkt(irt_mesh, mesh) 65 | 66 | #include "ir_end.h" 67 | #endif // wkt_mesh_h 68 | ``` 69 | 70 | In IREP, headers like this are called "Well Known Tables", or "WKTs". WKT 71 | headers are includable directly into C and C++ code, and you can refer to 72 | elements of the defined structures like this: 73 | 74 | ```c 75 | #include "wkt_mesh.h" 76 | 77 | void do_something() { 78 | double xrange = mesh.box.xmax - mesh.xmin; 79 | double yrange = mesh.box.ymax - mesh.ymin; 80 | double zrange = mesh.box.zmax - mesh.zmin; 81 | } 82 | ``` 83 | 84 | IREP can also be used to generate Fortran modules and Lua code for WKTs, 85 | and you can refer to their elements in a similarly direct way in those 86 | languages, e.g.: 87 | 88 | ``` 89 | mesh.box.xmax // C, C++ 90 | mesh.box.xmax -- Lua C 91 | mesh.box.xmax ! Fortran 92 | ``` 93 | 94 | ## Using IREP for input decks 95 | 96 | IREP's main use is to allow multi-language (usually C, C++, and Fortran) 97 | integrated codes to read Lua input files ("input decks" for those of use 98 | who've been in the simulation field for a while). With the `wkt_mesh.h` 99 | we've seen so far, we could write an input file like this: 100 | 101 | ```lua 102 | mesh = { 103 | file = 'my_mesh', 104 | refinement_level = 1, 105 | amr = true, 106 | box = { 107 | nx = 100, 108 | ny = 100, 109 | nz = 100, 110 | }, 111 | } 112 | ``` 113 | 114 | Note that the `xmin`, `xmax`, `ymin`, `ymax`, `zmin`, and `zmax` fields 115 | in `box` are not defined in the input, but they will take on the default 116 | values from the `wkt_mesh.h` header. 117 | 118 | If you want to read this input file from `C`, you could write some code 119 | like this: 120 | 121 | ```c 122 | #include "ir_extern.h" 123 | #include "wkt_mesh.h" 124 | #include "lua.h" 125 | 126 | int main(int argc, char **argv) { 127 | // set up lua and load some lua code 128 | lua_State *L = luaL_newstate(); 129 | luaL_openlibs(L); 130 | luaL_loadfile(L, "mesh_example.lua"); 131 | 132 | ir_read(L, "mesh"); 133 | printf("mesh.box.xmax = %d\n", mesh.box.xmax); 134 | printf("mesh.box.ymax = %d\n", mesh.box.ymax); 135 | printf("mesh.box.zmax = %d\n", mesh.box.zmax); 136 | ``` 137 | 138 | This sets up an embedded Lua interpreter, loads the Lua input file, and 139 | then reads WKT values out of the native Lua tables and into a global 140 | `mesh` structure that can be accessed from `C`. 141 | 142 | ## Authors 143 | 144 | IREP was created by Lee Busby, busby1@llnl.gov. 145 | 146 | Thanks also to `irep`'s 147 | [contributors](https://github.com/LLNL/irep/graphs/contributors)!. 148 | 149 | ## License 150 | 151 | IREP is distributed under the terms of the MIT license. Copyrights in the 152 | IREP project are retained by contributors. No copyright assignment is 153 | required to contribute to IREP. All new contributions must be made under 154 | the MIT license. 155 | 156 | See [LICENSE](https://github.com/LLNL/irep/blob/master/LICENSE) and 157 | [NOTICE](https://github.com/LLNL/irep/blob/master/NOTICE) for details. 158 | 159 | SPDX-License-Identifier: MIT 160 | 161 | LLNL-CODE-702338 162 | -------------------------------------------------------------------------------- /examples/c/timer_example/c_main.c: -------------------------------------------------------------------------------- 1 | // Copyright 2016-2021 Lawrence Livermore National Security, LLC and other 2 | // IREP Project Developers. See the top-level LICENSE file for details. 3 | // 4 | // SPDX-License-Identifier: MIT 5 | 6 | #include 7 | #include 8 | #include 9 | #if defined(__cplusplus) 10 | #include "lua.hpp" 11 | #else 12 | #include "lua.h" 13 | #include "lualib.h" 14 | #include "lauxlib.h" 15 | #endif 16 | #include "ir_extern.h" 17 | #include "wkt_table1.h" 18 | #include "wkt_table4.h" 19 | #include 20 | #include 21 | 22 | #if defined(__cplusplus) 23 | extern "C" { 24 | using namespace irep; 25 | #endif 26 | 27 | #if 0 28 | static double msec_clock(void) { 29 | struct timespec ts; 30 | (void) clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &ts); 31 | return (double)(1000.0*ts.tv_sec + ts.tv_nsec/1000000.0); 32 | } 33 | 34 | int eval_d4(lua_State *L, lua_cb_dd_data *d, double *x, double *v) { 35 | int i, tv; 36 | 37 | if (d->fref == LUA_REFNIL) return 1; 38 | if (d->fref == LUA_NOREF) { 39 | for (i=0; i < d->nret; ++i) v[i] = d->const_val[i]; 40 | return 0; 41 | } 42 | 43 | lua_rawgeti(L, LUA_REGISTRYINDEX, d->fref); 44 | tv = lua_type(L,-1); 45 | 46 | if (tv == LUA_TFUNCTION) { 47 | for (i=0; i < d->nprm; i++) lua_pushnumber(L, x[i]); // Push args. 48 | if (lua_pcall(L, d->nprm, d->nret, 0) != 0) 49 | return luaL_error(L, "error: %s", lua_tostring(L,-1)); 50 | for (i=0; i < d->nret; i++) { 51 | if (lua_type(L, i-d->nret) != LUA_TNUMBER) 52 | return luaL_error(L, "error: expected number as return value: %d", i+1); 53 | v[i] = lua_tonumber(L, i - d->nret); 54 | } 55 | lua_pop(L, d->nret); // Pop return values. 56 | 57 | } else if (tv == LUA_TTABLE) { 58 | int len = lua_objlen(L,-1); 59 | for (i=1; i<=len; i++) { 60 | lua_rawgeti(L,-1,i); 61 | if (lua_type(L,-1) != LUA_TNUMBER) return 1; 62 | v[i-1] = lua_tonumber(L,-1); 63 | lua_pop(L,1); 64 | } 65 | lua_pop(L,1); 66 | 67 | } else { 68 | fprintf(stderr,"error, wrong type\n"); 69 | return 1; 70 | } 71 | 72 | return 0; 73 | } 74 | #endif 75 | 76 | int eval_dd(lua_State *L, lua_cb_dd_data *d, double *x, double *v) { 77 | int i; 78 | 79 | if (d->fref == LUA_REFNIL) return 1; 80 | if (d->fref == LUA_NOREF) { 81 | for (i=0; i < d->nret; ++i) v[i] = d->const_val[i]; 82 | return 0; 83 | } 84 | 85 | // Push the function onto the stack. 86 | lua_rawgeti(L, LUA_REGISTRYINDEX, d->fref); 87 | for (i=0; i < d->nprm; i++) lua_pushnumber(L, x[i]); // Push args. 88 | 89 | if (lua_pcall(L, d->nprm, d->nret, 0) != 0) 90 | return luaL_error(L, "error: %s", lua_tostring(L,-1)); 91 | 92 | for (i=0; i < d->nret; i++) { 93 | if (lua_type(L, i-d->nret) != LUA_TNUMBER) 94 | return luaL_error(L, "error: expected number for return value: %d", i+1); 95 | v[i] = lua_tonumber(L, i - d->nret); 96 | } 97 | lua_pop(L, d->nret); // Pop return values. 98 | return 0; 99 | } 100 | 101 | int main(int argc, char *argv[]) { 102 | lua_State *L = luaL_newstate(); 103 | int ios, i, n, j, ii; 104 | double x[3] = { 2.0, 3.0, 4.0 }, v=0.0, v2[2]; 105 | 106 | if (argc < 2) { 107 | fprintf(stderr, "Usage: %s *.lua\n", argv[0]); 108 | return 0; 109 | } 110 | luaL_openlibs(L); 111 | if (luaL_loadfile(L, argv[1]) || lua_pcall(L, 0, 0, 0)) 112 | return luaL_error(L, 113 | "cannot run configuration file: %s", lua_tostring(L,-1)); 114 | 115 | ios = ir_read(L, "table1"); 116 | printf("\nREAD TABLE1: ios=%d\n",ios); 117 | printf("table1.i = %d\n", table1.i); 118 | printf("table1.d = %g\n", table1.d); 119 | printf("table1.b = %d\n", table1.b); 120 | 121 | char *ss = strdup(IR_STR(table1.s)); 122 | printf("table1.s = \"%s\"\n", ss); 123 | 124 | #if 0 125 | start = msec_clock(); 126 | for (n=0;n<1000000;n++) { 127 | i = eval_dd(L, &table1.f1, x, &v); 128 | vv += v; 129 | } 130 | end = msec_clock(); 131 | printf("Call f1 1000000 times: %g (%g)\n", end-start, vv); 132 | 133 | start = msec_clock(); 134 | for (n=0;n<1000000;n++) { 135 | i = eval_d4(L, &table1.f4, x, vvv); 136 | vv += vvv[0] + vvv[1] + vvv[2]; 137 | } 138 | end = msec_clock(); 139 | printf("Call f4 1000000 times: %g (%g)\n", end-start, vv); 140 | 141 | vv = 0.0; 142 | start = msec_clock(); 143 | for (n=0;n<1000000;n++) { 144 | i = eval_dd(L, &table1.table3.f3, x, &v); 145 | vv += v; 146 | } 147 | end = msec_clock(); 148 | printf("Call f3 1000000 times: %g (%g)\n", end-start, vv); 149 | #endif 150 | 151 | i = eval_dd(L, &table1.f1, x, &v); 152 | if (!i) printf("table1.f1(%g,%g,%g) = %g\n", x[0],x[1],x[2], v); 153 | else printf("f1 undefined\n"); 154 | 155 | i = eval_dd(L, &table1.table2[1].f2, x, &v); 156 | if (!i) printf("table1.table2[1].f2(%g,%g,%g) = %g\n", x[0],x[1],x[2], v); 157 | else printf("f2 undefined\n"); 158 | 159 | i = eval_dd(L, &table1.table3.f3, x, v2); 160 | if (!i) printf("table1.table3.f3(%g,%g,%g) = %g %g\n", x[0],x[1],x[2], v2[0],v2[1]); 161 | else printf("f3 undefined\n"); 162 | 163 | n = sizeof(table1.e)/sizeof(*table1.e); 164 | printf("\ntable1.e: %d elements, %d given\n", n, ir_rtlen(L, "table1.e")); 165 | for (i=0;ilibrary))); 77 | 78 | Limitations and constraints 79 | --------------------------- 80 | 81 | Lua tables can express combinations of data and data types in ways that 82 | are not easy to represent in C/C++ or Fortran structured data objects. 83 | The IR itself is shared between C/C++ and Fortran using Fortran's 84 | ``ISO_C_BINDING`` machinery. This is not generally an issue for integer, 85 | double, and logical variables, but it does affect the representation 86 | of character strings. There are some additional limitations imposed 87 | by the Fortran namelist statement, and limitations in how well actual 88 | compilers support all the features. Assignments are effectively parsed 89 | according to Fortran rules. The major difference for C/C++ (and Lua) 90 | programmers to recall is that Fortran identifiers are case-insensitive. 91 | 92 | The global structs IREP generates are statically declared, so the size of all 93 | array types is given and fixed at compile time. 94 | 95 | Strings 96 | ------- 97 | 98 | Character strings are stored in the IR as blank-delimited arrays, one 99 | character per array element. String vectors are stored as two-dimensional 100 | arrays of characters. Strings are null-terminated. String length is 101 | currently limited to 512 characters. It is usually best to access 102 | character strings using one of the convenience functions provided. 103 | If table ``t`` contains a scalar string ``ss`` and a vector of strings ``vs``: 104 | 105 | // C++ 106 | std::string my_ss(IR_STR(t.ss)); // Access scalar string 107 | std::string my_vs(IR_STR(t.vs[0])); // First element of vector string 108 | 109 | ! Fortran 110 | character(len=64) :: my_ss, my_vs 111 | my_ss = trim(ir_fstr(t%ss)) ! Access scalar string 112 | my_vs = trim(ir_fstr(t%vs(:,1))) ! First element of vector string 113 | 114 | ``IR_STR`` and ``ir_fstr`` basically depend on ``sizeof``, or its Fortran 115 | equivalent. So they can be used (only) with operands for which ``sizeof`` 116 | will return a sensible value. Note that default values can be given only 117 | for scalar strings in the IR header files. 118 | 119 | 120 | Compilers 121 | --------- 122 | 123 | Gfortran received a critical patch at version 4.7.4, that allowed 124 | it to properly read derived type components in namelist statements. 125 | That version or later is necessary for the IR to function correctly. 126 | Portland Group compilers 14.7 and 15.1 were tested, and appear to 127 | function correctly. IBM bgxlf and bgxlc, version 14.1, were tested, 128 | and appear correct. Intel ifort 14.0 was tested, and appears to give 129 | correct results. 130 | 131 | 132 | Callback functions 133 | ------------------ 134 | 135 | The IREP ``lua_cb_data`` struct has an integer component ``fref``, which 136 | nominally contains a Lua reference to the associated Lua callback 137 | function. We overload the meaning of the possible fref values to cover 138 | several cases of interest:: 139 | 140 | Value of fref Meaning 141 | --------------------------------------------------------------------- 142 | LUA_REFNIL No entry was in the Lua input. 143 | LUA_NOREF Lua input is a constant function, number or vector. 144 | Lua input is a real Lua function 145 | 146 | Fref is statically initialized to ``LUA_REFNIL``. If no actual 147 | Lua input for the given function name is found, this will therefore be 148 | the value of fref. 149 | 150 | If an input entry is found, we check the type. If it is a number, 151 | the value of fref is set to ``LUA_NOREF``, and the number is stored in 152 | the structure's ``const_val`` component. If the entry is instead a Lua 153 | function, we put the function in the Lua registry and store a reference 154 | to it in ``fref``. 155 | 156 | In particular, we can use the value of fref to infer whether or not 157 | any number or function was read:: 158 | 159 | fref == LUA_REFNIL ==> No entry of any kind was available; 160 | fref != LUA_REFNIL ==> Some entry (number or function) was read. 161 | -------------------------------------------------------------------------------- /docs/irep_types.rst: -------------------------------------------------------------------------------- 1 | .. Copyright 2016-2021 Lawrence Livermore National Security, LLC and other 2 | .. IREP Project Developers. See the top-level LICENSE file for details. 3 | .. 4 | .. SPDX-License-Identifier: MIT 5 | 6 | ========================= 7 | About IREP data types 8 | ========================= 9 | 10 | .. include:: 11 | 12 | IREP is a tool for generating an "intermediate representation" of Lua 13 | input decks for simulation codes. It allows Lua inputs to be read as 14 | plain old struct data from multiple languages (C, C++, and Fortran). 15 | 16 | This file provides a quick refernce on IREP data types. More on IREP can 17 | be found `on GitHub `_. 18 | 19 | 20 | .. _irep-double: 21 | 22 | Doubles 23 | ------- 24 | 25 | All Lua numbers are double precision floating point numbers, conforming 26 | to the IEEE 754 standard. You can write numeric constants in a Lua input 27 | file using any of the usual forms: 28 | 29 | 4 0.4 4.57e-3 0.3e12 5E+20 30 | 31 | Although IREP has both C++ and Fortran code internally, Lua input is the 32 | same for both. Note that Lua does *not* accept otherwise standard Fortran 33 | syntax for double precision constants: ``4.57d-3``. You must use ``e`` or 34 | ``E`` to indicate the exponent. 35 | 36 | 37 | .. _irep-vector-double: 38 | 39 | Vector doubles 40 | -------------- 41 | 42 | Like scalar doubles, ``vector doubles`` are stored as double precision 43 | floating point numbers. A vector is a 1-dimensional array of scalars, and 44 | is defined in Lua using the usual syntax: 45 | 46 | .. code-block:: lua 47 | 48 | x = { 3, 6, 9 } 49 | 50 | This defines an array ``x`` such that ``x[1] == 3``, etc. In Lua, the 51 | length of the array could vary, and is given by the set of values 52 | assigned to it. When using IREP, the maximum length of an input vector is 53 | limited. The limit is denoted in brackets (e.g., ``double[10]``) in the 54 | documentation. 55 | 56 | Sometimes the maximum number of elements is a not-to-exceed upper bound. 57 | This is usually some high number like ``double[99]``. For other fields, 58 | the maximum number of elements is exactly the number of elements you are 59 | required to enter, e.g. ``double[3]`` for a 3-dimensional vector. IREP 60 | does not distinguish between the two cases so you'll need to consult the 61 | documentation for each field. 62 | 63 | 64 | .. _irep-integer: 65 | 66 | Integers 67 | -------- 68 | 69 | Lua integers are represented internally by double precision floating 70 | point numbers. Thus, all integers whose absolute value is less than or 71 | equal to 2^53 can be represented exactly as a Lua number. 72 | 73 | In Lua, the literal constant ``37`` is the same value as ``37.0``. 74 | It would not be a (Lua) error to write 75 | 76 | .. code-block:: lua 77 | 78 | x = 37 79 | x = 37.0 80 | x = 37.000001 81 | 82 | However, IREP *does* distinguish integer input parameters from double 83 | precision parameters. If ``x`` is an integer input parameter, the third 84 | line above would produce an error. 85 | 86 | 87 | .. _irep-vector-integer: 88 | 89 | Vector integers 90 | --------------- 91 | 92 | Integer vector input variables are 1-dimensional arrays of integers: 93 | 94 | .. code-block:: lua 95 | 96 | i = { 5, 10, 15, 20, 25, 30 } 97 | 98 | In Lua, the length of the array might vary. With IREP, the maximum length 99 | is limited, and is denoted in brackets (e.g., ``integer[10]``) in the documentation. 100 | 101 | 102 | .. _irep-boolean: 103 | 104 | Booleans 105 | -------- 106 | 107 | The Lua boolean type has two values ``false`` and ``true``. These produce 108 | the expected result when they represent a IREP boolean or logical 109 | parameter. 110 | 111 | In Lua, conditional tests consider either ``false`` or ``nil`` as 112 | logically false. Everything else is ``true``, including the number ``0`` 113 | and the empty string, ``''``. 114 | 115 | 116 | .. _irep-vector-boolean: 117 | 118 | 119 | Vector booleans 120 | --------------- 121 | 122 | Boolean or logical vector input variables are 1-dimensional arrays of 123 | boolean or logical values: 124 | 125 | .. code-block:: lua 126 | 127 | bb = { true, false, true } 128 | 129 | In Lua, the length of the array can vary. With IREP, the maximum length 130 | is limited, and is denoted in brackets (e.g., ``boolean[10]``) in the 131 | documentation. 132 | 133 | 134 | .. _irep-string: 135 | 136 | Strings 137 | ------- 138 | 139 | Lua strings can be written using either single quote or double quote to 140 | delimit them. In Lua, the length of the string varies with the length of 141 | the value assigned to it. However, IREP input parameters do have a 142 | specific maximum length. The maximium length of a string is denoted in 143 | parentheses, e.g. ``string(256)`` in the documentation. 144 | 145 | 146 | .. _irep-vector-string: 147 | 148 | Vector strings 149 | --------------- 150 | 151 | String vector input variables are 1-dimensional arrays of strings: 152 | 153 | .. code-block:: lua 154 | 155 | s = { "abc", "defg", "hijkl" } 156 | 157 | In Lua, the length of the array, and the length of each string in the 158 | array might vary. In IREP, the maximum number of elements is limited, and 159 | is denoted in brackets (e.g., ``string(32)[64]``) in the documentation. 160 | The maximum length of each string is shown in parentheses, as with scalar 161 | strings. 162 | 163 | 164 | .. _irep-callback: 165 | 166 | Callback functions 167 | ------------------ 168 | 169 | Lua functions are an important part of IREP interfaces. They can be used 170 | to define fields, e.g., you might define a physical field as a functions 171 | of three space variables and one time variable. 172 | 173 | All input parameters for IREP's Lua functions are double precision 174 | numbers, as are all the return values. If the function returns one value, 175 | it defines a *scalar* field. Functions returning multiple values can be 176 | used to define *vector* (or sometimes *tensor*) fields. 177 | 178 | In the documentation, the arity (number of parameters) of a function is 179 | denoted with a ``/``, and the number of return values is shown after a 180 | right arrow (|rarr|). For example, a function that takes one parameter 181 | and returns three values would be denoted as: 182 | 183 | callback/1 |rarr| 3 184 | 185 | Functions that return *constant* values are common enough to receive 186 | special treatment in IREP. You can always write a constant function as a 187 | normal Lua function: 188 | 189 | .. code-block:: lua 190 | 191 | initial_conditions = { 192 | temperature = function(x,y,z,t) return 0.0 end, 193 | } 194 | 195 | would set the temperature field to zero everywhere, for all times. More 196 | conveniently, in IREP, you can also write it as 197 | 198 | .. code-block:: lua 199 | 200 | initial_conditions = { 201 | temperature = 0.0, 202 | } 203 | 204 | IREP knows that ``initial_conditions.temperature`` is a function, so it 205 | will interpret the second syntax correctly. If the function in question 206 | is vector-valued (and you want to return a constant value), you can 207 | either write a vector function, use an *array*, or use a scalar. A scalar 208 | value will be *broadcast* to all components of the return value. (But 209 | note that broadcasting only applies to constant scalar return values -- 210 | if you write a Lua function, it must return all required components.) 211 | 212 | .. code-block:: lua 213 | 214 | initial_conditions = { 215 | velocity = function(x,y,z,t) return 1.0, 2.0, 3.0 end, 216 | } 217 | 218 | initial_conditions = { 219 | velocity = { 1.0, 2.0, 3.0 }, 220 | } 221 | 222 | initial_conditions = { 223 | velocity = 0.0, -- equivalent to { 0,0,0 } 224 | } 225 | 226 | Defining an IREP callback function as a number or array generally 227 | executes faster than calling a regular Lua function, so this is a good 228 | technique to keep in mind. 229 | 230 | One other special kind of callback function defines the number of return 231 | values as ``-1`` (e.g., callback/3 |rarr| -1). This means that the 232 | function can return a one-dimensional array of arbitrary length. (Or you 233 | can return a Lua ``table``). Function-specific documentation should 234 | normally give some additional information to help you write an actual 235 | instance of the function. 236 | -------------------------------------------------------------------------------- /docs/overview.rst: -------------------------------------------------------------------------------- 1 | .. Copyright 2016-2021 Lawrence Livermore National Security, LLC and other 2 | .. IREP Project Developers. See the top-level LICENSE file for details. 3 | .. 4 | .. SPDX-License-Identifier: MIT 5 | 6 | .. _irep-overview: 7 | 8 | =================== 9 | Overview of IREP 10 | =================== 11 | 12 | The Intermediate Representation (or IR or IREP) is a tool to simplify 13 | reading some kinds of external program input, a means to organize the 14 | input into native language structures, and a way to share those 15 | structures among program modules written in either C/C++ or Fortran. It 16 | can handle several varieties of ``Plain Old Data`` and has mechanisms to 17 | extend its abilities beyond POD. It requires access to a Lua 5.1 18 | interpreter, C and Fortran compilers, and follows standards for those 19 | languages. A reader for tables written in the Lua programming language 20 | is included. 21 | 22 | Introduction 23 | ------------ 24 | 25 | Sharing Data Among Lua, C/C++ and Fortran 26 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 27 | 28 | Lua tables and constructors are a flexible way to define program input. 29 | It is usually easy to roughly map a Lua table into structured data types 30 | for C/C++ and Fortran:: 31 | 32 | Lua C/C++ Fortran 33 | ____________________________________________________________ 34 | t = { typedef struct irt_t { type, bind(c) :: irt_t 35 | a = 1, int a; integer(c_int) :: a = 1 36 | b = {2.2,3.3}, double b[2]; real(c_double) :: b(2) 37 | c = "foo", char c[16]; character(c_char) :: c(16) 38 | d = true, _Bool d; logical(c_bool) :: d = .true. 39 | } } irt_t; end type irt_t 40 | 41 | This works best for, but is not strictly limited to, ``Plain Old Data`` 42 | (POD). For our purposes, POD is a scalar variable or 1D vector of type 43 | integer, double, string, or boolean. The Fortran ``ISO_C_BINDING`` lets 44 | us map structures between C/C++ and Fortran. The C preprocessor specifies 45 | the IR in a language-neutral way. Here is the same structure as above, 46 | written using the IR macros: 47 | 48 | .. code-block:: C 49 | 50 | Beg_struct(irt_t) Doc(( Begin structure declaration )) 51 | ir_int(a,1) Doc(( Scalar integer with default value = 1 )) 52 | Vir_dbl(b,2,3.0) Doc(( Vector double, 2 elements, default = 3.0 )) 53 | ir_str(c,16,"foo") Doc(( Scalar string, maxlen=16, default = "foo" )) 54 | ir_log(d,true) Doc(( Scalar boolean, default = true )) 55 | End_struct(irt_t) Doc(( End structure )) 56 | 57 | Structures in the IR are defined in a C header. When the file is included 58 | from C/C++ source, the macros output a C/C++ struct declaration. From 59 | Fortran, the output is the equivalent derived0m type. The Fortran version 60 | defines and initializes the structures, while C/C++ declares them as 61 | extern objects. There is one shared instance of any IR variable in the 62 | data segment of a running program. 63 | 64 | Nested Tables 65 | ^^^^^^^^^^^^^ 66 | 67 | The IR includes a Structure macro to allow nested C/C++ and Fortran 68 | structures:: 69 | 70 | IREP C/C++ Fortran 71 | __________________________________________________ 72 | Beg_struct(irt_T1) ... ... 73 | Structure(irt_T2,x) irt_T2 x; type(irt_T2) :: x 74 | End_struct(irt_T1) ... ... 75 | 76 | Nested structures follow C ordering rules: ``irt_T2`` must be declared 77 | before ``irt_T1``, etc. After the IR has been defined and compiled, and after 78 | data has been read from the Lua input file into the IR, the host code can 79 | gaccess data using native language operations:: 80 | 81 | -- First, set the value by reading the Lua table. 82 | material[1].EOS.table.number = 1018 83 | 84 | // Later, read the value from C/C++. 85 | int n = material[1].EOS.table.number; // 1018 86 | 87 | ! Or read the value from Fortran. 88 | integer :: n 89 | n = material(1)%EOS%table%number ! 1018 90 | 91 | Notice that the example above uses index ``1`` for all three languages. This is a consequence of the way that the material table 92 | was declared in the IREP. See the discussion of the Vir_wkt0m 93 | macro in the IREP man page that accompanies this paper for more 94 | information. 95 | 96 | Reading Data into the IR 97 | ^^^^^^^^^^^^^^^^^^^^^^^^ 98 | 99 | The compiled data store in the IR is independent of any means to load or 100 | change its values. We provide a Lua table reader for that purpose. If the 101 | IR data store defines a top-level structure named material, as in the 102 | example above, we expect the Lua input to contain a global table also 103 | named material, and call this a well known table (WKT). The elements of 104 | the Lua table must match the elements of the IR structure. Whenever the 105 | Lua reader reaches a leaf node in its table, it looks up the address of 106 | the matching IR element, and places the value at that memory location. 107 | 108 | Designing the User Interface 109 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 110 | 111 | Lua tables and Lua variables can be quite different from C/C++ or Fortran 112 | structures and variables. Here are some Lua idioms that are difficult to 113 | model in the IR data store: 114 | 115 | 1. Polymorphic element values: Lua is happy to allow both ``x=1`` and 116 | ``x="one"`` (at different times) in the same table. This is hard to do 117 | in the IR, because C/C++ and Fortran variables can't change type. One 118 | place where we do allow polymorphic elements is our implementation of 119 | callback functions: 120 | 121 | .. code-block:: lua 122 | 123 | f = 3 124 | f = { 1, 2, 3 } -- max len is 9 elements 125 | f = function() return 3 end 126 | 127 | We frequently use constant callback functions, so it is convenient to 128 | allow scalar or array values in place of a true Lua function, as shown 129 | in the first two examples above. Callback functions require some 130 | special handling already, so this case does not present much extra 131 | trouble for the Lua reader. In general, however, it's a good idea to 132 | limit polymorphic elements in the user interface. 133 | 134 | 2. Lua tables with both record and array parts: 135 | 136 | .. code-block:: lua 137 | 138 | a = { 3,2,1, x=4 } 139 | 140 | That is, ``a[1] == 3``, and ``a.x == 4``. There is no easy way to 141 | handle this case with the IR, because ``a`` is neither a vector, nor 142 | is it a structured type. It's some of both. Don't do this, or use an 143 | ir_reference if you must. 144 | 145 | 3. Lua arrays with mixed types: 146 | 147 | .. code-block:: lua 148 | 149 | a = { 3,2,1, "liftoff" } 150 | 151 | In this case, ``a`` is a vector, but its elements don't have any 152 | single type. Arrays in C/C++ and Fortran cannot have elements of 153 | more than one type. So don't do this either. 154 | 155 | In designing the user interface, we can choose, case by case, whether to 156 | let the constraints imposed by the IR also constrain or shape the Lua 157 | interface. By its nature, the IR rewards simplicity in user interface 158 | design. 159 | 160 | Some usage notes 161 | ---------------- 162 | 163 | The ``ir_read`` and ``ir_exists`` functions 164 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 165 | 166 | In addition to defining IR input data, the host code is responsible for 167 | reading the well known tables. The function provided for this task is 168 | ir_read. For example, in C, we might read the materials table: 169 | 170 | .. code-block:: C 171 | 172 | int n = ir_read(L, "materials"); 173 | if (n != 0) { 174 | printf("ERROR: %d\n", n); 175 | exit(1); 176 | } 177 | 178 | ``ir_read`` returns the total number of errors it encountered. For 179 | well known tables that may be optional, the ``ir_exists`` function is 180 | provided to allow the host code to query whether a given table 181 | element is in the Lua state before attempting to read it. 182 | 183 | Strings 184 | ^^^^^^^ 185 | 186 | Sharing strings between Fortran and C/C++ is easier than it once was. 187 | That said, reading strings from the IR requires extra care for both 188 | languages. Strings are stored in the IR as an array of characters, 189 | ``NULL`` terminated. The max length is part of the original declaration, 190 | and it includes the ``NULL`` character. The IR provides convenience 191 | functions and macros to simplify some common operations:: 192 | 193 | std::string myfoo(IR_STR(t.c)); // C++: Declare/initialize. 194 | character(len=64) :: myfoo ! Fortran: Declare scalar. 195 | myfoo = trim(fstr(IR_STR(t.c))) ! Initialize it. 196 | -------------------------------------------------------------------------------- /examples/fortran/f_main.f: -------------------------------------------------------------------------------- 1 | ! Copyright 2016-2021 Lawrence Livermore National Security, LLC and other 2 | ! IREP Project Developers. See the top-level LICENSE file for details. 3 | ! 4 | ! SPDX-License-Identifier: MIT 5 | 6 | module mainmod 7 | use, intrinsic :: iso_c_binding 8 | implicit none 9 | 10 | integer(kind=c_int), parameter :: LUA_NOREF = -2 11 | integer(kind=c_int), parameter :: LUA_REGISTRYINDEX = -10000 12 | integer(kind=c_int), parameter :: LUA_TNUMBER = 3 13 | integer(kind=c_int), parameter :: LUA_TTABLE = 5 14 | 15 | interface fstr 16 | module procedure fstr_arr, fstr_ptr 17 | end interface 18 | 19 | interface 20 | 21 | pure function strlen_ptr(s) result(result) bind(c,name="strlen") 22 | use, intrinsic :: iso_c_binding 23 | integer(c_int) :: result 24 | type(c_ptr), value, intent(in) :: s 25 | end function strlen_ptr 26 | 27 | function lua_objlen(L, index) bind(c, name="lua_objlen") 28 | use, intrinsic :: iso_c_binding, only : c_int, c_ptr, c_size_t 29 | type(c_ptr), value :: L 30 | integer(kind=c_int), value :: index 31 | integer(kind=c_size_t) :: lua_objlen 32 | end function 33 | 34 | function luaL_newstate() bind(c, name="luaL_newstate") 35 | use, intrinsic :: iso_c_binding, only : c_ptr 36 | type(c_ptr) :: luaL_newstate 37 | end function 38 | 39 | function luaL_loadfile(L, filename) bind(c, name="luaL_loadfile") 40 | use, intrinsic :: iso_c_binding, only : c_ptr, c_int, c_char 41 | type(c_ptr), value :: L 42 | character(kind=c_char), dimension(*) :: filename 43 | integer(kind=c_int) :: luaL_loadfile 44 | end function 45 | 46 | function luaL_loadstring(L, s) bind(c, name="luaL_loadstring") 47 | use, intrinsic :: iso_c_binding, only : c_ptr, c_int, c_char 48 | type(c_ptr), value :: L 49 | character(kind=c_char), dimension(*) :: s 50 | integer(kind=c_int) :: luaL_loadstring 51 | end function 52 | 53 | function lua_tolstring(L, index, len) bind(c, name="lua_tolstring") 54 | use, intrinsic :: iso_c_binding, only : c_ptr, c_int 55 | type(c_ptr), value :: L 56 | integer(kind=c_int), value :: index 57 | type(c_ptr), value :: len 58 | type(c_ptr) :: lua_tolstring 59 | end function 60 | 61 | function lua_pcall(L, nargs, nresults, errfunc) bind(c, name="lua_pcall") 62 | use, intrinsic :: iso_c_binding, only : c_int, c_ptr 63 | type(c_ptr), value :: L 64 | integer(kind=c_int), value :: nargs 65 | integer(kind=c_int), value :: nresults 66 | integer(kind=c_int), value :: errfunc 67 | integer(kind=c_int) :: lua_pcall 68 | end function 69 | 70 | subroutine luaL_openlibs(L) bind(c, name="luaL_openlibs") 71 | use, intrinsic :: iso_c_binding, only : c_ptr 72 | type(c_ptr), value :: L 73 | end subroutine 74 | 75 | subroutine lua_rawgeti(L, index, n) bind(c, name="lua_rawgeti") 76 | use, intrinsic :: iso_c_binding, only : c_ptr, c_int 77 | type(c_ptr), value :: L 78 | integer(kind=c_int), value :: index, n 79 | end subroutine 80 | 81 | subroutine lua_pushnumber(L, n) bind(c, name="lua_pushnumber") 82 | use, intrinsic :: iso_c_binding, only : c_ptr, c_double 83 | type(c_ptr), value :: L 84 | real(kind=c_double), value :: n 85 | end subroutine 86 | 87 | function lua_type(L, index) bind(c, name="lua_type") 88 | use, intrinsic :: iso_c_binding, only : c_ptr, c_int 89 | type(c_ptr), value :: L 90 | integer(kind=c_int), value :: index 91 | integer(kind=c_int) :: lua_type 92 | end function 93 | 94 | function lua_tonumber(L, index) bind(c, name="lua_tonumber") 95 | use, intrinsic :: iso_c_binding, only : c_ptr, c_int, c_double 96 | type(c_ptr), value :: L 97 | integer(kind=c_int), value :: index 98 | real(kind=c_double) :: lua_tonumber 99 | end function 100 | 101 | subroutine lua_settop(L, index) bind(c, name="lua_settop") 102 | use, intrinsic :: iso_c_binding, only : c_ptr, c_int 103 | type(c_ptr), value :: L 104 | integer(kind=c_int), value :: index 105 | end subroutine 106 | 107 | end interface 108 | 109 | ! ---------------------------------------------------------------------- 110 | contains 111 | 112 | subroutine lua_pop(L, index) 113 | use, intrinsic :: iso_c_binding, only : c_ptr, c_int 114 | type(c_ptr), value :: L 115 | integer(kind=c_int), value :: index 116 | call lua_settop(L, -(index)-1) 117 | end subroutine 118 | 119 | function lua_tostring(L, index) 120 | use, intrinsic :: iso_c_binding, only : c_ptr, c_int, c_null_ptr 121 | type(c_ptr), value :: L 122 | integer(kind=c_int), value :: index 123 | type(c_ptr) :: lua_tostring, x = c_null_ptr 124 | lua_tostring = lua_tolstring(L, index, x) 125 | end function 126 | 127 | ! Convert a null-terminated C "char *" pointer to a scalar Fortran string. 128 | function fstr_ptr(s) result(fs) 129 | use, intrinsic :: iso_c_binding, only: c_char, c_ptr, c_f_pointer 130 | type(c_ptr), intent(in) :: s 131 | character(kind=c_char, len=strlen_ptr(s)) :: fs 132 | character(kind=c_char), pointer :: cptr(:) 133 | integer :: i 134 | call c_f_pointer(s, cptr, [len(fs)]) 135 | do i=1, len(fs) 136 | fs(i:i) = cptr(i) 137 | enddo 138 | end function 139 | 140 | ! Convert a null-terminated array of characters to a scalar Fortran string. 141 | function fstr_arr(s) result(fs) 142 | use, intrinsic :: iso_c_binding, only : c_char, c_null_char 143 | character(kind=c_char, len=1), intent(in) :: s(:) 144 | character(kind=c_char, len=size(s)) :: fs 145 | integer :: i 146 | fs = ' ' 147 | do i = 1, len(fs) 148 | if (s(i) == c_null_char) exit 149 | fs(i:i) = s(i) 150 | enddo 151 | end function 152 | 153 | ! Convert a fortran string in 's' to a null-terminated array of characters. 154 | pure function cstr(s) 155 | use, intrinsic :: iso_c_binding, only : c_char, c_null_char 156 | character(len=*), intent(in) :: s 157 | character(kind=c_char, len=1) :: cstr(len_trim(s)+1) 158 | integer :: i 159 | !i=0 ! Some compilers warn about uninitialized variable without this. 160 | if (len_trim(s) > 0) cstr = [ (s(i:i), i=1,len_trim(s)) ] 161 | cstr(len_trim(s)+1) = c_null_char 162 | end function 163 | 164 | ! A simple evaluator for IREP callback functions. 165 | function eval_vector(L, d, x, v) result(actual_nret) 166 | use, intrinsic :: iso_c_binding 167 | use, intrinsic :: iso_fortran_env, only : error_unit 168 | use ir_std 169 | use ir_extern, only: ir_nprm, ir_nret, ir_get_function_name 170 | implicit none 171 | type(c_ptr), value, intent(in) :: L 172 | type(lua_cb_data), target, intent(in) :: d 173 | real(c_double), intent(in) :: x(:) 174 | real(c_double), intent(out) :: v(:) 175 | integer(c_int) :: i, nprm, nret, actual_nret 176 | real(c_double), pointer :: data(:) 177 | 178 | nprm = ir_nprm(d%npnr) 179 | nret = ir_nret(d%npnr) 180 | actual_nret = nret 181 | 182 | if (d%fref == LUA_NOREF) then 183 | if (nret > size(v)) then 184 | write(error_unit,'(a,2i6)') "ERROR: v is too small",nret,size(v) 185 | stop 1 186 | endif 187 | call c_f_pointer(d%data,data,[nret]) 188 | do i=1,nret 189 | v(i) = data(i) 190 | enddo 191 | return 192 | endif 193 | 194 | if (nprm > size(x)) then 195 | write(error_unit,'(a)') "ERROR: not enough parameters given" 196 | stop 1 197 | endif 198 | 199 | ! Push the function and arguments onto the stack. 200 | call lua_rawgeti(L, LUA_REGISTRYINDEX, d%fref) 201 | do i = 1, nprm 202 | call lua_pushnumber(L, x(i)) 203 | enddo 204 | if (nret .eq. -1) actual_nret = 1 205 | if (lua_pcall(L, nprm, actual_nret, 0) /= 0) then 206 | write(error_unit,'(2a)') "LUA ERROR: ", fstr(lua_tostring(L,-1)) 207 | stop 1 208 | endif 209 | 210 | if (nret .eq. -1) then ! Arbitrary length table expected. 211 | if (lua_type(L,-1) /= LUA_TTABLE) then 212 | write(error_unit,'(a)') "ERROR: Expected function to return table" 213 | write(error_unit,'(2a)') "NAME:",fstr(ir_get_function_name(L,c_loc(d))) 214 | stop 1 215 | endif 216 | actual_nret = lua_objlen(L,-1) 217 | if (actual_nret > size(v)) then 218 | write(error_unit,'(a)') "ERROR: v is too small for table returned" 219 | stop 1 220 | endif 221 | do i=1,actual_nret 222 | call lua_rawgeti(L,-1,i) 223 | if (lua_type(L,-1) /= LUA_TNUMBER) then 224 | write(error_unit,'(a)') "ERROR: table item is not number" 225 | stop 1 226 | endif 227 | v(i) = lua_tonumber(L,-1) 228 | call lua_pop(L,1) 229 | enddo 230 | call lua_pop(L,1) 231 | 232 | else ! Standard return on the stack. 233 | do i=1,nret 234 | if (lua_type(L, i - nret - 1) /= LUA_TNUMBER) then 235 | write(error_unit,'(a)') "error: expected number for return value: " 236 | stop 1 237 | endif 238 | v(i) = lua_tonumber(L, i - nret - 1) 239 | enddo 240 | call lua_pop(L, nret) 241 | endif 242 | 243 | end function 244 | 245 | end 246 | 247 | ! ------------------------------------------------------------------------ 248 | program main 249 | use, intrinsic :: iso_c_binding 250 | use ir_std 251 | use ir_extern 252 | use wkt_table1 253 | use wkt_table4 254 | use mainmod 255 | implicit none 256 | 257 | integer :: ios, i, n, ng 258 | real(c_double) :: v(3), x(3) = [ 2.0, 3.0, 4.0 ] 259 | type(c_ptr) :: L 260 | character(len=64) :: arg, name 261 | 262 | L = luaL_newstate() 263 | call luaL_openlibs(L) 264 | 265 | if (command_argument_count() .lt. 1) then 266 | print *, "Usage: fprog *.lua" 267 | stop 0 268 | endif 269 | call get_command_argument(1, arg) 270 | if (luaL_loadfile(L, cstr(arg)) .ne. 0) then 271 | print *, "cannot load file: ", fstr(lua_tostring(L,-1)) 272 | stop 1 273 | endif 274 | if (lua_pcall(L, 0, 0, 0) .ne. 0) then 275 | print *, "cannot run file: ", fstr(lua_tostring(L,-1)) 276 | stop 1 277 | endif 278 | 279 | if (ir_read(L, cstr("table1")) .ne. 0) stop 280 | 281 | print *,"READ TABLE1" 282 | write(*,"(a,i4)") "table1.i = ", table1%i 283 | write(*,"(a,f6.2)") "table1.d = ", table1%d 284 | write(*,"(a,l2)") "table1.b = ", table1%b 285 | write(*,"(a,a)") "table1.s = ", trim(fstr(table1%s)) 286 | 287 | i = eval_vector(L, table1%f1, x, v) 288 | write(*,"(i4,a,f3.1,a,f3.1,a,f3.1,a,f6.2)") & 289 | i, " table1.f1(",x(1),",",x(2),",",x(3),") = ",v(1) 290 | 291 | i = eval_vector(L, table1%f4, x, v) 292 | write(*,"(i4,a,f3.1,a,f3.1,a,f3.1,a,f6.2)") & 293 | i, " table1.f4(",x(1),",",x(2),",",x(3),") = ",v(1) 294 | 295 | i = eval_vector(L, table1%f5, x, v) 296 | write(*,"(i4,a,f3.1,a,f3.1,a,f3.1,a,f6.2)") & 297 | i, " table1.f5(",x(1),",",x(2),",",x(3),") = ",v(1) 298 | 299 | print *, "" 300 | n = size(table1%e) 301 | ng = ir_rtlen(L, cstr("table1.e")) 302 | write(*, "(a,i2,a,i2,a)") "table1.e: ",n, " elements, ",ng, " given" 303 | do i=1,n 304 | write(*,"(a,i1,a,f6.2)") "table1.e(",i,") = ", table1%e(i) 305 | enddo 306 | 307 | name = fstr(ir_get_function_name(L,c_loc(table1%f5))) 308 | print *, "f5:", name 309 | 310 | end 311 | -------------------------------------------------------------------------------- /docs/build.rst: -------------------------------------------------------------------------------- 1 | =============================== 2 | Building IREP into applications 3 | =============================== 4 | 5 | Integrating IREP into an application requires that you write ``wkt_*.h`` 6 | headers (as described in the :ref:`overview `) *and* that 7 | you generate libraries from those headers, which you can then link into 8 | your application. Since IREP's internal data structures are statically 9 | defined, and since IREP's library routines (like ``ir_read()``) need to 10 | look up tables dynamically, linking IREP into an application requires 11 | some special steps. 12 | 13 | IREP's :ref:`build system integration ` can 14 | ease this process. We'll describe that later, but before we do, we'll 15 | define some terms. 16 | 17 | ``wkt`` and ``wkt-index`` libraries 18 | ----------------------------------- 19 | 20 | To use IREP, you'll need to generate at least two types of libraries: 21 | 22 | 1. One or more ``wkt`` libraries that define the static data structures 23 | in particular WKT headers, and 24 | 2. A ``wkt-index`` library that tells IREP how to look up data by name in 25 | those structures. 26 | 27 | A simple example 28 | ^^^^^^^^^^^^^^^^ 29 | 30 | Suppose that you have an application with modules `lib1` and `lib2`. 31 | 32 | .. graphviz:: 33 | 34 | digraph { 35 | "app" -> "liba" 36 | "app" -> "libb" 37 | } 38 | 39 | And suppose that your application has a WKT header, ``wkt_app.h``, that 40 | defines its input format. To use IREP, you'll need to generate a WKT 41 | library and a WKT index from ``wkt_app.h``, then link everything together 42 | with IREP. The figure below shows the dependency relationships between 43 | your application components and the various IREP libraries: 44 | 45 | .. graphviz:: 46 | 47 | digraph { 48 | irep [shape=record, 49 | label="{libIR.a}"] 50 | 51 | "app" -> "liba" 52 | "app" -> "libb" 53 | 54 | "libapp-wkt.a" [shape=record, 55 | label="{libapp-wkt.a | {wkt_app.h}}"] 56 | 57 | "libapp-wkt-index.a" [shape=record, 58 | label="{libapp-wkt-index.a | {wkt_app.h}}"] 59 | 60 | "app" -> "liba" 61 | "app" -> "libb" 62 | 63 | "app" -> "irep" 64 | "app" -> "libapp-wkt.a" 65 | "app" -> "libapp-wkt-index.a" 66 | } 67 | 68 | 69 | Adding components to larger applications 70 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 71 | 72 | IREP is designed to be modular, so that different components in an 73 | application can define different parts of an input format, or so that 74 | components can share an input format. Suppose that you want to introduce 75 | ``wkt_physics.h`` and ``wkt_geometry.h`` that define settings for physics 76 | and geometry in your application, *and* in ``liba`` and ``libb``. That 77 | is, ``app``, ``liba``, and ``libb`` need to include these headers and 78 | access data from these parts of the input format. 79 | 80 | To deal with this, you need only introduce one more WKT library, and 81 | include the new headers in your WKT index. Let's call it 82 | ``libshared-wkt.a``, as it is shared by all three of your app's 83 | components. Here are the new dependency relationships: 84 | 85 | .. graphviz:: 86 | 87 | digraph { 88 | irep [shape=record, 89 | label="{libIR.a}"] 90 | 91 | "libshared-wkt.a" [shape=record, 92 | label="{libshared-wkt.a | {wkt_physics.h} | {wkt_geometry.h}}"] 93 | 94 | "libapp-wkt.a" [shape=record, 95 | label="{libapp-wkt.a | {wkt_app.h}}"] 96 | 97 | "libapp-wkt-index.a" [shape=record, 98 | label="{libapp-wkt-index.a | {wkt_app.h} | {wkt_physics.h} | {wkt_geometry.h}}"] 99 | 100 | "app" -> "liba" 101 | "app" -> "libb" 102 | 103 | "app" -> "irep" 104 | "app" -> "libapp-wkt.a" 105 | "app" -> "libapp-wkt-index.a" 106 | "app" -> "libshared-wkt.a" 107 | 108 | "liba" -> "libshared-wkt.a" 109 | "liba" -> "irep" 110 | 111 | "libb" -> "libshared-wkt.a" 112 | "libb" -> "irep" 113 | } 114 | 115 | All the dependencies that were present when only ``app`` was using IREP 116 | are still there, but now all components need to link to ``libIR.a`` (so 117 | that they can call routines like ``ir_read()``), and all components link 118 | to ``libshared-wkt.a``, as it contains the ``struct`` definitions for the 119 | new WKTs. Generating WKT libraries also creates the Fortran ``.mod`` 120 | files needed to include WKT headers into Fortran programs. 121 | 122 | ``libapp-wkt-index.a`` has now expanded -- it is now generated from 123 | ``wkt_app.h``, ``wkt_physics.h``, *and* ``wkt_geometry.h`` -- all the 124 | WKTs known to any part of the application. Again, this is necessary 125 | because this library tells ``ir_read()`` where to find data. Without it, 126 | ``ir_read()`` cannot look up IREP fields by name. 127 | 128 | If you expand your application to include more components, you can add 129 | new WKT libraries as needed by new components, and you'll need to 130 | remember to update the list of WKTs the index with each new component. 131 | 132 | .. _build-system-integration: 133 | 134 | Build system integration 135 | ------------------------ 136 | 137 | It may seem daunting to write the code for these libraries, but IREP 138 | includes integration for GNU Make and for CMake that makes it simple to 139 | create them. 140 | 141 | GNU Make 142 | ^^^^^^^^ 143 | 144 | To use IREP from GNU make, you simply import the ``wkt.mk`` file that 145 | comes with IREP, and it defines implicit rules for building WKT and WKT 146 | index libraries. You need only say what to build and what source files 147 | (WKT headers) to include. 148 | 149 | Let's look at an example ``GNUMakefile`` based on our example above. 150 | Assume that ``irep.root`` is set to the path to your IREP installation: 151 | 152 | .. code-block:: makefile 153 | 154 | # include this file to use IREP's gmake integration 155 | include $(irep.root)/share/irep/wkt.mk 156 | 157 | # define the libraries you want with -wkt.a and -wkt-index.a suffixes 158 | all: libapp-wkt.a libapp-wkt-index.a 159 | 160 | # index libraries must define .wkt_index_src to include all WKT 161 | # headers from the application 162 | app.wkt_index_src = wkt_app.h wkt_physics.h wkt_geometry.h 163 | 164 | # WKT libraries need .wkt_src defined. 165 | # libshared-wkt.a (w/geometry and physics) would be defined elsewhere 166 | app.wkt_src = wkt_app.h 167 | 168 | # You can add defines to CPPFLAGS so that WKT headers can use them 169 | # You must export CPPFLAGS so that irep-generate can see it. 170 | CPPFLAGS += -DSPECIAL_OPTION -I$(shared_wkt_dir) 171 | export CPPFLAGS 172 | 173 | # this rule cleans up the generated .a, .f, .o, and .mod files 174 | .PHONY: clean 175 | clean: 176 | rm -f *.[afo] *.mod 177 | 178 | If you only wanted to generate a WKT library, you could remove the 179 | required ``libapp-wkt-index.a`` target and not set ``app.wkt_index.src``. 180 | 181 | The Makefile above generates the libraries for you, but you'll still need 182 | to ensure that they and ``libIR.a`` are linked into your application. 183 | 184 | CMake 185 | ^^^^^ 186 | 187 | To use IREP from CMake, you'll first need to include the IREP directory 188 | in `CMAKE_PREFIX_PATH 189 | `_. 190 | You probably want to include this on the command line when you invoke 191 | ``cmake``, like this: 192 | 193 | .. code-block:: console 194 | 195 | $ cmake -DCMAKE_PREFIX_PATH=/path/to/irep;/other/path ... 196 | 197 | Once that is done, you'll be able to call ``find_package(irep)`` in your 198 | CMake code. This will import two useful CMake functions: 199 | ``add_wkt_library()`` and ``add_wkt_index_library()``. Here's a CMake 200 | example that is equivalent to the GMake one above: 201 | 202 | .. code-block:: cmake 203 | 204 | # find irep 205 | find_package(irep REQUIRED) 206 | 207 | add_wkt_library(app-wkt wkt_app.h) 208 | target_include_directories(app-wkt PUBLIC -I${shared_wkt_dir}) 209 | target_compile_definitions(app-wkt PUBLIC -DSPECIAL_OPTION) 210 | 211 | add_wkt_index_library(app-wkt-index wkt_app.h wkt_physics.h wkt_geometry.h) 212 | target_include_directories(app-wkt-index PUBLIC -I${shared_wkt_dir}) 213 | target_compile_definitions(app-wkt-index PUBLIC -DSPECIAL_OPTION) 214 | 215 | The libraries are created as CMake targets with calls to 216 | ``add_wkt_library()`` and ``add_wkt_index_library()``, and what was 217 | handled with ``CPPFLAGS`` in GMake is handled for each library with 218 | ``target_include_directories`` and ``target_compile_definitions``. 219 | 220 | Now you can use the ``app-wkt`` and ``app-wkt-index`` targets as you 221 | would any other in CMake -- you'll need to add them to your application 222 | to ensure that it links correctly. 223 | 224 | .. _irep-generate: 225 | 226 | The ``irep-generate`` command 227 | ----------------------------- 228 | 229 | You've already seen how IREP integrates with build systems, but if you 230 | need more than this, you can use the ``irep-generate`` command to handle 231 | all the magic yourself. ``wkt.mk`` and IREP's CMake integration use this 232 | command to generate C and Fortran code for WKT and WKT index libraries. 233 | It is also used to :ref:`generate documentation ` 234 | from WKT headers. 235 | 236 | .. it would be great to use command-output here, but we can't because 237 | .. readthedocs does not have Lua in its environment, and we cannot 238 | .. install system packages on RTD. If that becomes possible, this 239 | .. should just invoke irep-generate. 240 | 241 | ``irep-generate`` usage is pretty simple -- it looks like this:: 242 | 243 | ./irep-generate --help 244 | irep-generate [-h] [--mode PART] HEADERS 245 | 246 | This script generates source code for IREP well-known-table (WKT) 247 | libraries. 248 | 249 | Generated code is printed to standard out. 250 | 251 | Positional Arguments: 252 | HEADERS wkt_*.h files from which to generate code, e.g.: 253 | wkt_geometry.h wkt_physics.h ... 254 | 255 | Generation options 256 | --mode index generate only index (default) 257 | --mode fortran generate fortran module from a single wkt header 258 | --mode lua generate loadable, nested lua tables 259 | --mode rst generate restructured text (.rst) documentation 260 | 261 | --module-name name for generated module (fortran mode only, 262 | inferred from header name by default) 263 | 264 | Documentation options (for use with --mode rst) 265 | --doc-dir DIR documentation directory where we look for 266 | details/intros for WKTs (default: .) 267 | 268 | Environment variables: 269 | CPP C preprocessor to use (default gcc -E) 270 | CPPFLAGS flags for the C preprocessor 271 | 272 | Help: 273 | -h, --help display this message 274 | 275 | ``irep-generate`` can be used to translate ``wkt*.h`` files into C, Fortran, 276 | Lua code, and it can also generate RST documentation. The particular mode 277 | of generation used is controlled by the ``--mode`` flag. 278 | 279 | The most interesting options here are ``--mode index`` and ``--mode 280 | fortran``. 281 | 282 | Fortran generation 283 | ^^^^^^^^^^^^^^^^^^ 284 | 285 | Running: 286 | 287 | .. code-block:: console 288 | 289 | $ irep-generate --mode fortran wkt_foo.h 290 | 291 | will generate ``wkt_foo.f``, which can be compiled into a Fortran 292 | ``.mod`` file and a ``.o`` file. The ``.o`` file contains the definition 293 | for the table structures, and the ``.mod`` file is includable by Fortran 294 | code. Createing a WKT library is just a matter of including all these 295 | ``.o`` files into a single library. So, WKT libraries just contain 296 | structure definitions for your IREP data -- nothing else. 297 | 298 | Index generation 299 | ^^^^^^^^^^^^^^^^ 300 | 301 | Running: 302 | 303 | .. code-block:: console 304 | 305 | $ irep-generate --mode index wkt_foo.h wkt_bar.h wkt_baz.h 306 | 307 | Will generate C code that IREP uses to look up the location of fields 308 | from well known tables. This needs to be run on by each application using 309 | IREP, over all of the ``wkt*.h`` files included in that application. If 310 | WKT files are not included, bad things can happen, so you should ensure 311 | that you use the same ``CPPFLAGS`` and the same headers that you did when 312 | you generated your WKT libraries. 313 | 314 | Controlling code generation 315 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ 316 | 317 | ``irep-generate`` invokes the C preprocessor to translate ``wkt_*.h`` 318 | files into something easy to parse. This adds some flexibility to the 319 | IREP model, because your table definitions can include other code, 320 | optional segments using ``#ifdef``, etc. You can control how the 321 | preprocessor is invoked through the following environment variables: 322 | 323 | * ``CPP``: Set this to the preprocessor you want to use. The default is 324 | ``gcc -E``, but if you build with another compiler you may want to swap 325 | it in. 326 | 327 | * ``CPPFLAGS``: To pass flags to ``irep-generate``, you need only ensure 328 | that the standard ``CPPFLAGS`` variable is set when you run 329 | ``irep-generate``. You can add ``-I`` and ``-D`` directives here. 330 | 331 | As mentioned above, it is important to ensure that you use the same 332 | ``CPPFLAGS`` in all the places where you call ``irep-generate``. Not 333 | doing so can cause IREP to be confused when it tries to find fields in 334 | its data structures. 335 | 336 | Lua code generation 337 | ^^^^^^^^^^^^^^^^^^^ 338 | 339 | Running: 340 | 341 | .. code-block:: console 342 | 343 | $ irep-generate --mode lua wkt_foo.h 344 | 345 | Will generate importable Lua code for a WKT module. This is useful for 346 | seeing, in simple Lua tables, what the data looks like in a WKT. You can 347 | also use it to generate a skeleton input deck. 348 | -------------------------------------------------------------------------------- /docs/reference.rst: -------------------------------------------------------------------------------- 1 | .. Copyright 2016-2021 Lawrence Livermore National Security, LLC and other 2 | .. IREP Project Developers. See the top-level LICENSE file for details. 3 | .. 4 | .. SPDX-License-Identifier: MIT 5 | 6 | .. _irep-reference: 7 | 8 | ============== 9 | IREP Reference 10 | ============== 11 | 12 | IREP - An Intermediate Representation for Structured Input 13 | 14 | .. code-block:: C 15 | 16 | // C/C++ 17 | #include "ir_extern.h" 18 | #include "wkt_TABLENAME.h" 19 | 20 | int ir_read(lua_State *L, const char *tbl_elem); 21 | int ir_exists(lua_State *L, const char *t); 22 | int ir_rtlen(lua_State *L, const char *s); 23 | int ir_nprm(int npnr); 24 | int ir_nret(int npnr); 25 | int ir_unread(lua_State *L, const char *ir_tbl) 26 | char *ir_get_stringref(lua_State *L, int n, int *len); 27 | 28 | .. code-block:: fortran 29 | 30 | ! Fortran 31 | use ir_extern 32 | use wkt_TABLENAME 33 | 34 | Description 35 | ----------- 36 | 37 | The IREP is a toolkit and associated C library, to read Lua 38 | tables into a compiled data store. The toolkit defines the data 39 | store as a set of structured variables in C/C++ and Fortran. The 40 | library reads Lua tables, comparing table keys with the 41 | corresponding variable in the data store, and puts the table 42 | values into the data store. 43 | 44 | Usage 45 | ----- 46 | 47 | Step one is to design and define one 48 | or more well-known tables for the data store (see below). After 49 | creating the well-known tables, you can build the IREP library, 50 | and link it to your C/C++ and/or Fortran code - the host code. 51 | Your host code reads Lua tables into the data store: 52 | 53 | .. code-block:: C 54 | 55 | int nerr = ir_read(L, "mytable"); 56 | 57 | You can access a variable from ``mytable`` using native language 58 | operations in the host code: 59 | 60 | .. code-block:: C 61 | 62 | // C 63 | #include "wkt_mytable.h" 64 | int n = mytable.subtable.n; 65 | 66 | .. code-block:: C++ 67 | 68 | // C++ (The IREP datastore is in namespace "irep".) 69 | #include "wkt_mytable.h" 70 | int n = irep::mytable.subtable.n; 71 | 72 | .. code-block:: Fortran 73 | 74 | ! Fortran 75 | use wkt_mytable 76 | integer n 77 | n = mytable%subtable%n 78 | 79 | You can query the Lua table to discover whether a particular 80 | element actually was given in the input: 81 | 82 | .. code-block:: C 83 | 84 | int e = ir_exists(L, "mytable.subtable.n"); 85 | if (e) { 86 | printf("User input explicitly defines N\n"); 87 | } else { 88 | printf("Using IREP default value for N\n"); 89 | } 90 | 91 | If a table element is a vector, you can ask how long the actual 92 | input array was: 93 | 94 | .. code-block:: C 95 | 96 | int nelem = ir_rtlen(L, "mytable.subtable.v1"); 97 | 98 | If a table element is a string, you can copy it from the data 99 | store into a local variable using a macro or function from IREP: 100 | 101 | .. code-block:: C 102 | 103 | // C 104 | char *ss = strndup(IR_STR(t.ss)); // Access scalar string. 105 | char *vs = strndup(IR_STR(t.vs[0])); // First element of vector. 106 | 107 | .. code-block:: C++ 108 | 109 | // C++ 110 | std::string my_ss(IR_STR(irep::t.ss)); // Access scalar string 111 | std::string my_vs(IR_STR(irep::t.vs[0])); // First element of vector. 112 | 113 | .. code-block:: fortran 114 | 115 | ! Fortran 116 | character(len=64) :: my_ss, my_vs 117 | my_ss = trim(fstr(t%ss)) ! Access scalar string. 118 | my_vs = trim(fstr(t%vs(:,1))) ! First element of vector. 119 | 120 | The remaining functions in the IREP API are briefly described: 121 | 122 | ``int ir_nprm(int npnr); // Return NPRM, given an npnr value.`` 123 | 124 | ``int ir_nret(int npnr); // Return NRET, given an npnr value.`` 125 | These two functions are typically used by the host code during 126 | evaluation of Lua callback functions (see 127 | :ref:`lua-callback-functions`). 128 | 129 | ``int ir_unread(lua_State *L, const char *ir_tbl)`` 130 | This function is experimental at present. It is effectively the 131 | reverse of ir_read: IREP can read one of the tables in the data 132 | store, and create a corresponding table in the lua_State. 133 | 134 | ``char *ir_get_function_name(lua_State *L,void *p);`` 135 | This function is aimed mainly at error reporting, during callback 136 | function evaluation. When the Lua input file is read, IREP stores the 137 | full name of each callback function using the address of its 138 | associated ``lua_cb_data`` structure as a key. The function name can 139 | thus be accessed later, typically to produce better error messages. 140 | 141 | ``char *ir_get_stringref(lua_State *L, int n, int *len);`` 142 | 143 | This function handles the case where a Lua string of arbitrary length 144 | has been stored using the IREP ir_reference macro. (See 145 | ``ir_get_stringref``.) The client code typically calls 146 | ir_get_stringref as follows: 147 | 148 | .. code-block:: C 149 | 150 | if (ir_exists(L, "physics.foo")) { 151 | int nn; 152 | std::string foo = ir_get_stringref(L,irep::physics.foo,&nn); 153 | lua_pop(L,-1); 154 | } 155 | 156 | The nn parameter returns the length of the string; it can be 157 | passed as ``(char *) NULL`` if you do not need this value. 158 | 159 | 160 | Defining the Data Store 161 | ----------------------- 162 | 163 | The IREP data store is a collection of C/C++ "structs" or their 164 | equivalent Fortran "derived type". The code defining each variable in the 165 | data store is written once, using cpp(1) macros that expand into either 166 | C/C++ or Fortran source code, depending on the context: 167 | 168 | Example Table in the IREP Data Store:: 169 | 170 | IREP Input C/C++ Output Fortran Output 171 | --------------------------------------------------------------------- 172 | Beg_struct(irt_t) struct irt_t { type, bind(c) :: irt_t 173 | ir_int(a,1) int a; integer(c_int) :: a=1 174 | Vir_dbl(b,2) double b[2]; real(c_double) :: b(2) 175 | ir_str(c,8,"foo") char c[8]; character(c_char) :: c(8)="foo" 176 | ir_log(d,true) _Bool d; logical(c_bool) :: d=.true. 177 | End_struct(irt_t) } irt_t; end type irt_t 178 | 179 | The rules for constructing the IREP data store are precisely the 180 | rules for C and Fortran, constrained by the Fortran 181 | ``ISO_C_BINDING``. Structures can be nested in arbitrary fashion. 182 | Scalar and one-dimensional array variables (vectors) of type 183 | integer, double, boolean, or character (strings) can be defined. 184 | In addition, special types are available to define Lua callback0m 185 | functions and Lua references. 186 | 187 | Summary of IREP Macros 188 | ---------------------- 189 | 190 | Beg_struct(T) 191 | Begin declaration for a table of type T. 192 | 193 | End_struct(T) 194 | End declaration for a table of type T. 195 | 196 | ir_wkt(T,ID) 197 | Define a table named ID, of type T. 198 | 199 | Vir_wkt(T,ID,FB,CB) 200 | Define a vector of tables named ID, of type T. The vector has Fortran 201 | bounds FB0m and C bounds CB. E.g., if FB=``0:5'', and CB=``6'', the 202 | vector will have six elements indexed from zero to five. Since Lua 203 | normally indexes from 1, the 0th element will presumably be unused. This 204 | is useful if you want a particular index value to have the same semantics 205 | in all three of C/C++, Fortran, and Lua. Alternatively, if you set 206 | FB=``1:6'', then the first element of the array would have index 0 in 207 | C/C++, but index 1 in Fortran and Lua. 208 | 209 | ``Structure(T,ID)`` 210 | Declare a table named ID, with typename T. 211 | 212 | ``Vstructure(T,ID,FB,CB)`` 213 | Declare a vector of tables ID, typename T. The vector has Fortran 214 | bounds FB and C bounds CB. (See Vir_wkt above.) 215 | 216 | ``ir_dbl(ID,DV)`` 217 | Declare scalar variable of type double named ID, default value DV. 218 | 219 | ``ir_int(ID,DV)`` 220 | Declare scalar variable of type integer named ID, default value DV. 221 | 222 | ``ir_log(ID,DV)`` 223 | Declare scalar variable of type boolean named ID, default value 224 | 225 | ``ir_str(ID,LEN,DV)`` 226 | 227 | Declare scalar string named ID, max len LEN, default value 228 | 229 | ``Vir_dbl(ID,NELEM,DV)`` 230 | Declare vector variable of type double named ID, with NELEM0m 231 | elements, default value DV. 232 | 233 | ``Vir_int(ID,NELEM,DV)`` 234 | Declare vector variable of type integer named ID, with NELEM 235 | elements, default value DV. 236 | 237 | ``Vir_log(ID,NELEM,DV)`` 238 | Declare vector variable of type boolean named ID, with NELEM 239 | elements, default value DV. 240 | 241 | ``Vir_str(ID,LEN,NELEM)`` 242 | Declare vector string named ID, with NELEM elements, max len LEN. 243 | Note that string vectors cannot set a default value. 244 | 245 | ``Callback(ID,NPRM,NRET)`` 246 | Declare a Lua callback function named ID, with NPRM parameters, 247 | returning NRET double precision values. 248 | 249 | ``ir_reference(ID)`` 250 | Declare an IREP variable as a Lua reference. In practice, Lua stores 251 | an (integer) reference to the matching element in the Lua table, for 252 | later use by the host code. This is effectively an escape mechanism 253 | to allow embedding of arbitrary Lua tables, functions, or strings 254 | into the well known table. The host code will need to be specialized 255 | to handle each ``ir_reference`` value. See also ``ir_get_stringref``. 256 | 257 | ``ir_ptr(ID)`` 258 | Declare an IREP variable as a C pointer. This will normally require 259 | some special treatment by the IREP reader. At the present time, the 260 | only use is internal, by the ``Callback`` macro. 261 | 262 | ``Doc(( a comment ))`` 263 | Add an inline comment. This macro must occur at the end of a line 264 | containing one of the other IR macros. It can be multi-line and can 265 | contain free-form RST. 266 | 267 | Reading a Well Known Table 268 | -------------------------- 269 | 270 | The host code finds and loads (compiles) Lua input using the normal 271 | ``luaL_loadfile``, ``lua_pcall`` sequence. If the Lua input contains a 272 | well-known table named, say, ``table1``, that table can be read into the 273 | IREP data store using the call: 274 | 275 | .. code-block:: C 276 | 277 | int n = ir_read(L, "table1"); 278 | 279 | This recursively reads the entire table. If ``table1`` itself contains a 280 | subtable ``table2``, that table could be separately read as: 281 | 282 | .. code-block:: C 283 | 284 | int n = ir_read(L, "table1.table2"); 285 | 286 | That is, ``ir_read`` can start at any point in a Lua table, and reads 287 | recursively from that element down. The syntax of the second argument is 288 | equivalent to the Lua syntax that would reference the same element. 289 | Reading just the subtable ignores elements in ``table1`` that are outside 290 | of ``table2``. If the environment variable ``irep_debug`` is set to a 291 | positive integer value, ``ir_read`` will produce a listing to stderr of 292 | each variable read from the Lua table. 293 | 294 | .. _lua-callback-functions: 295 | 296 | Lua Callback Functions 297 | ^^^^^^^^^^^^^^^^^^^^^^ 298 | 299 | Lua functions are stored in the IREP data store using a ``struct``: 300 | 301 | .. code-block:: C 302 | 303 | Beg_struct(lua_cb_data) 304 | ir_int(fref, -1) // -1 == LUA_REFNIL 305 | ir_int(npnr, -9) // packed nprm,nret 306 | ir_ptr(data) 307 | End_struct(lua_cb_data) 308 | 309 | The ``fref`` component stores a Lua reference to the (Lua) 310 | function. The ``npnr`` component packs the NPRM and NRET values 311 | into a single integer, to save space. The packing algorithm 312 | effectively will allow values for NPRM in the range [-9,1014] and 313 | for NRET, the range [-9,2097142]. (But note that the enforced 314 | lower bound for NPRM and NRET is currently -1, not -9.) Values 315 | for the NPRM parameter have the following meaning: 316 | 317 | .. code-block:: C 318 | 319 | nprm == -1 // The Lua function accepts a variable number of arguments. 320 | nprm >= 0 // The Lua function accepts exactly NPRM arguments. 321 | 322 | Values for the NRET parameter have the following meaning: 323 | 324 | .. code-block:: C 325 | 326 | nret == -1 // The Lua function will return a table of any length. 327 | nret >= 0 // The Lua function will return exactly NRET values. 328 | 329 | This is important to IREP because, for convenience and 330 | efficiency, IREP allows an extended set of Lua types to be 331 | treated as functions. Some examples may be useful. 332 | 333 | .. code-block:: C 334 | 335 | Callback(f1,2,1) // A function with NPRM==2, NRET=1. 336 | 337 | For f1, either of the following Lua inputs is legal: 338 | 339 | .. code-block:: C 340 | 341 | f1 = function(x,y) return math.sin(3) end 342 | f1 = math.sin(3) 343 | 344 | The first input is a true Lua function, that happens to return a 345 | constant value. The second input is a number, obviously 346 | constant, which IREP will also allow as the definition of ``f1``. In 347 | the second case, IREP uses the ``data`` component of the 348 | ``lua_cb_data`` structure associated with ``f1``. It allocates space 349 | for one double precision number, and stores math.sin(3) at that 350 | location. It sets the ``fref`` component to LUA_NOREF as an 351 | indication that there is no true Lua function to call in this 352 | case. Later on, the host code may evaluate ``f1``. If 353 | ``fref==LUA_NOREF``, the evaluator can use the stored value. Here 354 | is a second example: 355 | 356 | .. code-block:: C 357 | 358 | Callback(f2,3,3) // A function with NPRM==3, NRET=3. 359 | 360 | For f2, any of the following Lua inputs is legal: 361 | 362 | .. code-block:: lua 363 | 364 | f2 = function(x,y,z) return 3,4,5 end 365 | f2 = { 3,4,5 } 366 | f2 = 3 367 | 368 | The first input is a true Lua function, returning 3 values, which 369 | also happen to be constant in this example. The second input is 370 | Lua table, containing the same values returned by the function. 371 | IREP treats the table like it did the number in the first 372 | example: It allocates space for the 3 values, and stores them in 373 | the ``data`` component for later use by the host code. The third 374 | input is also legal. The scalar number will be treated as if the 375 | input was 376 | 377 | .. code-block:: lua 378 | 379 | f2 = { 3,3,3 }. 380 | 381 | That is, broadcast to fill the 3 return slots expected for this 382 | function. Finally, the case where ``NRET == -1``: 383 | 384 | .. code-block:: C 385 | 386 | Callback(f3,3,-1) // A function with NPRM==3, NRET=-1. 387 | 388 | In this case, IREP expects the function to return a table (a 389 | rank-1 array of double precision numbers) of arbitrary length. A 390 | true Lua function can of course calculate a new table each time 391 | it is called: 392 | 393 | .. code-block:: lua 394 | 395 | f3 = function(x,y,z) return { x, x+y, x+y+z } end 396 | 397 | Note that in this case, the function returns a table, distinct 398 | from the list returned by the function in example two above. Or 399 | the input can be a (constant) Lua table: 400 | 401 | .. code-block:: lua 402 | 403 | f3 = { 1,2,3,4,5 } 404 | 405 | If it is a table, IREP allocates space and stores the table 406 | values in the ``data`` component, as before. In this case, it also 407 | modifies the ``npnr`` component to reflect the actual number of 408 | elements read from the Lua table. 409 | 410 | Return Values 411 | ------------- 412 | 413 | * ``ir_read`` returns the number of errors encountered. 414 | 415 | * ``ir_exists`` returns 1 if the element is found in the Lua input, 0 if 416 | not. 417 | 418 | * ``ir_rtlen`` returns -1 if the given Lua value is not present, 0 if the 419 | value is a (scalar) number, or whatever ``lua_objlen`` returns 420 | otherwise. For a string, this is the length of the string. For an 421 | array, it is the same as the ``#`` operator in Lua. 422 | 423 | 424 | Bugs 425 | ---- 426 | 427 | Variables in the IREP data store are generally static. This is a 428 | consequence of current ``ISO_C_BINDING`` rules for sharing data between 429 | Fortran and This means that the maximum length of arrays (and therefore 430 | strings) in the IREP data store is fixed at compile time. 431 | 432 | 433 | Author 434 | ------ 435 | 436 | IREP was created by Lee Busby at Lawrence Livermore National Laboratory. 437 | 438 | See also 439 | -------- 440 | 441 | See :ref:`irep-overview` for more discussion 442 | of some of the code in IREP. 443 | -------------------------------------------------------------------------------- /irep.c: -------------------------------------------------------------------------------- 1 | // Copyright 2016-2021 Lawrence Livermore National Security, LLC and other 2 | // IREP Project Developers. See the top-level LICENSE file for details. 3 | // 4 | // SPDX-License-Identifier: MIT 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #if defined(__cplusplus) 17 | extern "C" { 18 | #endif 19 | 20 | #include "lua.h" 21 | #include "lualib.h" 22 | #include "lauxlib.h" 23 | 24 | #include "ir_index.h" 25 | #include "ir_std.h" 26 | 27 | // BSZ is the internal buffer size for strings typically containing the 28 | // name of Lua table elements such as "table1.table2[123].foo.bar". 29 | #define BSZ 2048 30 | 31 | // Set irep_debug=1 in the environment, to see elements visited during ir_read. 32 | static int irep_debug = -1; 33 | static void Dbg_print(const char *fmt, ...) 34 | { 35 | va_list argp; 36 | if (irep_debug > 0) { 37 | char buf[BSZ]; 38 | const int i = sprintf(buf, "IR_DBG: "); 39 | va_start(argp, fmt); 40 | (void)vsnprintf(buf+i,sizeof(buf)-10,fmt,argp); 41 | va_end(argp); 42 | (void)fprintf(stderr,"%s\n",buf); 43 | } 44 | } 45 | 46 | // Note comma operator below, specifying the return value. 47 | #define Ir_error(fmt,...) \ 48 | fprintf(stderr,"ERROR (Lua/IR): " fmt "\n",__VA_ARGS__),1 49 | 50 | // TYP_ERR is common enough to get its own macro. 51 | #define TYP_ERR(lrep, ltyp, ityp) \ 52 | Ir_error("Type mismatch: %s (%s): Expected: %s",lrep,s_typ[ltyp],s_typ[ityp]) 53 | 54 | // Type specifiers (Typ), and their string equivalents (s_typ). 55 | 56 | static const char *s_typ[] = { "integer", "double", "logical", "string", 57 | "callback", "table", "reference", "pointer", "new_callback" }; 58 | 59 | // Find index of "name" in element table tp. 60 | static int find_element(const char *name, ir_element *tp) { 61 | int i; 62 | for (i=0; tp[i].name; i++) 63 | if (strcmp(name, tp[i].name) == 0) return i; 64 | return -1; 65 | } 66 | 67 | // Find the entry for the well-known table "name". 68 | static int find_wkt(const char *name) { 69 | int i; 70 | for (i=0; i < ir_wktt_size; i++) { 71 | if (strcmp(name, ir_wktt[i].e.name) == 0) return i; 72 | } 73 | return -1; 74 | } 75 | 76 | #if 0 77 | // Print out an IREP structure. Unused, except for debugging. 78 | static int iir_print(char *lrep,char *lp,void *bp,ir_element *ep, int treat_as_scalar) { 79 | int i, j, errcnt = 0; 80 | 81 | if (ep->typ == T_tbl) { // Current IREP element is a struct. 82 | printf("T: %4ld %10s %2d %3ld %3ld %3d %d:%d %d %s\n", 83 | (long int)bp, ep->name, 84 | ep->ti, ep->sz, ep->off, ep->len, ep->flb, ep->fub, ep->typ, lrep); 85 | 86 | char *nlp; 87 | void *nbp; 88 | ir_element *nep = ep; 89 | 90 | // Scalar struct, or 1 element of an array. 91 | if (ep->fub == 0 || treat_as_scalar) { 92 | for (i=0; ir_ta[ep->ti][i].name; i++) { 93 | nep = &ir_ta[ep->ti][i]; 94 | nlp = lp + snprintf(lp, BSZ+(lrep-lp), ".%s", nep->name); 95 | nbp = bp + nep->off; 96 | errcnt += iir_print(lrep, nlp, nbp, nep, 0); 97 | } 98 | 99 | } else { // Array of structs. 100 | for (j=ep->flb; j<=ep->fub; j++) { 101 | nlp = lp + snprintf(lp, BSZ+(lrep-lp), "[%d]", j); 102 | nbp = bp + (j - ep->flb)*ep->sz; 103 | errcnt += iir_print(lrep, nlp, nbp, nep, 1); 104 | } 105 | } 106 | 107 | // Current IREP element is NOT a struct. (It is either scalar POD, 108 | // or an array thereof.) 109 | } else { 110 | // Loop below executes at least once, for a scalar. More for array. 111 | i = ep->flb; // For a scalar, flb is always 1, and fub is always 0. 112 | do { 113 | printf("%d: %4ld %10s %2d %3ld %3ld %3d %d:%d %d %s\n", i, 114 | (long int)(bp + (i - ep->flb)*ep->sz), ep->name, 115 | ep->ti, ep->sz, ep->off, ep->len, ep->flb, ep->fub, ep->typ, lrep); 116 | } while (++i <= ep->fub); 117 | } 118 | *lp = '\0'; 119 | return errcnt; 120 | } 121 | #endif 122 | 123 | // Handle variables of "type" ir_reference. These variables become 124 | // Lua references, to be handled later by the compiled code as needed. 125 | static int read_ref(lua_State *L,char *lrep,void *bp) { 126 | *((int *)bp) = luaL_ref(L, LUA_REGISTRYINDEX); 127 | lua_pushnil(L); 128 | Dbg_print("%s = %d", lrep, *((int *)bp)); 129 | return 0; 130 | } 131 | 132 | // Store a name (lrep) using an address (its associated lua_cb_data) as 133 | // the key. Internal, used by read_cbk. 134 | static void ir_set_function_name(lua_State *L,char *lrep,void *p) { 135 | lua_pushlightuserdata(L,p); 136 | lua_pushstring(L,lrep); 137 | lua_settable(L,LUA_REGISTRYINDEX); 138 | } 139 | 140 | // Retrieve a name by address. Strdup is a memory leak. This routine 141 | // should normally only ever get used in error reporting, however, so 142 | // the memory leak is probably not very important. 143 | char *ir_get_function_name(lua_State *L,void *p) { 144 | lua_pushlightuserdata(L,p); 145 | lua_gettable(L,LUA_REGISTRYINDEX); 146 | char *s = strdup(lua_tostring(L,-1)); 147 | lua_pop(L,1); 148 | return s; 149 | } 150 | 151 | // These two functions are externals, used by Lua callback evaluators. 152 | int ir_nprm(int npnr) { return npnr%1024 - 9; } 153 | int ir_nret(int npnr) { return npnr/1024 - 9; } 154 | 155 | // Read a Lua callback function. 156 | static int read_cbk(lua_State *L,char *lrep,void *bp,ir_element *ep) { 157 | int i, ii, fref = LUA_NOREF, tv = lua_type(L,-1), npnr = ep->len, base_npnr = ep->len; 158 | lua_cb_data *cb = (lua_cb_data *)bp; 159 | 160 | if (tv!=LUA_TNUMBER && tv!=LUA_TTABLE && tv!=LUA_TFUNCTION) 161 | return Ir_error("Expected function, array, or number: %s", lrep); 162 | 163 | if (tv == LUA_TFUNCTION) { 164 | fref = luaL_ref(L, LUA_REGISTRYINDEX); 165 | lua_pushnil(L); 166 | 167 | } else { 168 | // Unpack nprm, nret. Ir_generate enforces nprm>=-1, nret>=-1. 169 | // The packing algorithm will allow a lower limit of -9. 170 | int nprm = ir_nprm(npnr); 171 | int nret = ir_nret(npnr); 172 | 173 | if (nret == 0) 174 | return Ir_error("``%s'': Function declares zero return values." 175 | " Returning a Lua scalar or constant array is not allowed.", lrep); 176 | 177 | ii = (tv==LUA_TTABLE) ? lua_objlen(L,-1) : (nret>0) ? nret : 1; 178 | if (nret != -1 && ii != nret) 179 | return Ir_error("``%s'': need %d return val(s), got %d", lrep, nret, ii); 180 | 181 | // Recalculate npnr if nret was originally -1. This is done so that 182 | // the callback evaluator will receive the actual length of the data 183 | // buffer in the nret value. 184 | if (nret == -1) npnr = (ii+9)*1024 + nprm+9; 185 | 186 | cb->data = realloc(cb->data, ii*sizeof(double)); 187 | if (!cb->data) return Ir_error("``%s'': realloc failed", lrep); 188 | double *dp = (double *)cb->data; 189 | 190 | if (tv == LUA_TNUMBER) { // Input is a scalar Lua number. 191 | // Broadcast the scalar to all return values. We treat nret==-1 192 | // as if it were nret==1. (The scalar is treated as a table of 193 | // length 1.) 194 | i = 0; 195 | do { 196 | dp[i] = lua_tonumber(L,-1); 197 | Dbg_print("%s.data[%d] = %25.17e", lrep, i, dp[i]); 198 | } while (++i < nret); 199 | 200 | } else { // Input is a Lua table. 201 | for (i=1; i<=ii; i++) { 202 | lua_rawgeti(L,-1,i); 203 | if (lua_type(L,-1) != LUA_TNUMBER) 204 | return Ir_error("Bad entry: %s[%d]: %s",lrep,i,lua_tostring(L,-1)); 205 | dp[i-1] = lua_tonumber(L,-1); 206 | Dbg_print("%s.data[%d] = %25.17e",lrep,i-1,dp[i-1]); 207 | lua_pop(L,1); 208 | } 209 | } 210 | } 211 | cb->npnr = npnr; 212 | cb->base_npnr = base_npnr; 213 | Dbg_print("%s.npnr = %d (%d,%d)", lrep, npnr, ir_nprm(npnr),ir_nret(npnr)); 214 | cb->fref = fref; 215 | Dbg_print("%s.fref = %d", lrep, fref); 216 | ir_set_function_name(L,lrep,bp); 217 | return 0; 218 | } 219 | 220 | // The internal table reader. 221 | // L: Lua top-of-stack, contains the equivalent of lrep. 222 | // lrep: Current full name, e.g., "table.subtable.element[3].key" 223 | // lp: Pointer to the right end of lrep. 224 | // bp: IREP base address for the current element. 225 | // ep: Descriptor for the current element. 226 | static int iir_read(lua_State *L,char *lrep,char *lp,void *bp,ir_element *ep) { 227 | int i, errcnt = 0, tv = lua_type(L,-1); 228 | 229 | // A self-referential table will overflow. 230 | if (!lua_checkstack(L,6)) return Ir_error("stack overflow: %s",lrep); 231 | 232 | // Callback functions and references are handled separately. 233 | if (ep->typ == T_cbk) return read_cbk(L, lrep, bp, ep); 234 | if (ep->typ == T_ref) return read_ref(L, lrep, bp); 235 | 236 | if (tv != LUA_TTABLE) { // if top of stack is a scalar value, read it now. 237 | if (tv == LUA_TSTRING) { 238 | char *pchar = (char *)bp; 239 | size_t vlen; 240 | const char *vp=lua_tolstring(L,-1,&vlen); 241 | if (ep->typ != T_str) return TYP_ERR(lrep, T_str, ep->typ); 242 | if (vlen > ep->len - 1) 243 | return Ir_error("String too long (max %d): %s (%s)",ep->len,lrep,vp); 244 | (void)strcpy(pchar, vp); 245 | Dbg_print("%s = %s", lrep, pchar); 246 | 247 | } else if (tv == LUA_TBOOLEAN) { 248 | BOOLEAN *pbool = (BOOLEAN *)bp; 249 | if (ep->typ != T_log) return TYP_ERR(lrep, T_log, ep->typ); 250 | *pbool = (BOOLEAN)lua_toboolean(L,-1); 251 | Dbg_print("%s = %c",lrep, ((*pbool) ? 'T' : 'F')); 252 | 253 | } else if (tv == LUA_TNUMBER) { 254 | if (ep->typ!=T_dbl && ep->typ!=T_int) return TYP_ERR(lrep,T_dbl,ep->typ); 255 | double d = lua_tonumber(L,-1); 256 | int isint = ((d - (double)(int)d) == 0.0); 257 | if (ep->typ == T_dbl) { 258 | double *pdbl = (double *)bp; 259 | *pdbl = d; 260 | if (isint) Dbg_print("%s = %d", lrep, (int)(*pdbl)); 261 | else Dbg_print("%s = %25.17e", lrep, *pdbl); 262 | } else if (ep->typ == T_int) { 263 | int *pint = (int *)bp; 264 | if (isint) { 265 | *pint = (int)d; 266 | Dbg_print("%s = %d", lrep, *pint); 267 | } else return Ir_error("Integer value expected: %s: %25.17e", lrep,d); 268 | } 269 | 270 | } else { 271 | return Ir_error("Wrong type: %s (%s): Expected: %s", 272 | lrep, lua_typename(L,tv), s_typ[ep->typ]); 273 | } 274 | return 0; 275 | } 276 | 277 | // If we get here, Lua TOS must be a table. Verify that the corresponding 278 | // IREP element is also a table, or an array. 279 | if (ep->typ != T_tbl && ep->fub == 0) return TYP_ERR(lrep, T_tbl, ep->typ); 280 | 281 | // Process the subtable recursively. 282 | for (lua_pushnil(L); lua_next(L,-2); lua_pop(L,1)) { 283 | char *nlp = lp; 284 | void *nbp = bp; 285 | ir_element *nep = ep; 286 | 287 | if (lua_type(L,-2) == LUA_TSTRING) { // Table has string keys. 288 | const char *s = lua_tostring(L,-2); 289 | nlp += snprintf(lp, BSZ+(lrep-lp), ".%s", s); 290 | i = find_element(s, ir_ta[ep->ti]); 291 | if (i == -1) { 292 | lua_pop(L, 2); 293 | return Ir_error("No such IREP variable: %s (%s)", s, lrep); 294 | } 295 | nep = &ir_ta[ep->ti][i]; 296 | nbp += nep->off; 297 | 298 | } else if (lua_type(L,-2) == LUA_TNUMBER) { // Table has numeric keys. 299 | i = (int)lua_tonumber(L,-2); 300 | nlp += snprintf(lp, BSZ+(lrep-lp), "[%d]", i); 301 | if (iflb || i>ep->fub) { 302 | lua_pop(L, 2); 303 | return Ir_error("Array bounds exceeded: %s[%d] (%d:%d)", 304 | lrep,i,ep->flb,ep->fub); 305 | } 306 | nbp += (i - ep->flb)*ep->sz; 307 | 308 | } else { 309 | lua_pop(L, 2); 310 | return Ir_error("Expected string or integer key: %s", lrep); 311 | } 312 | errcnt += iir_read(L, lrep, nlp, nbp, nep); 313 | *lp = '\0'; // Restore previous trailing null in lrep. 314 | } 315 | return errcnt; 316 | } 317 | 318 | static void newtable_byname(lua_State *L, const char *name) { 319 | // TOS (Lua stack initially has table T.) 320 | // T 321 | lua_newtable(L); // {} T 322 | lua_pushvalue(L,-1); // {} {} T 323 | lua_setfield(L,-3,name); // {} T (Set T[name] = {}, pop 1 value.) 324 | } 325 | 326 | #define lua_swap(L) lua_insert(L,-2) 327 | static void newtable_byindex(lua_State *L, int k) { 328 | // TOS (Lua stack initially has table T.) 329 | // T 330 | lua_newtable(L); // {} T 331 | lua_pushvalue(L,-1); // {} {} T 332 | lua_pushinteger(L,k); // k {} {} T 333 | lua_swap(L); // {} k {} T (x<>y, for HP RPN programmers.) 334 | lua_settable(L,-4); // {} T (Set T[k] = {}, pop 2 values.) 335 | } 336 | 337 | // Push an IREP table back to the lua_State. 338 | static int iir_unread(lua_State *L,char *lrep,char *lp,void *bp,ir_element *ep, int treat_as_scalar) { 339 | int i, j, errcnt = 0; 340 | 341 | if (ep->typ == T_tbl) { // Current IREP element is a struct. 342 | char *nlp; 343 | void *nbp; 344 | ir_element *nep = ep; 345 | 346 | // Scalar struct, or 1 element of an array. 347 | if (ep->fub == 0 || treat_as_scalar) { 348 | for (i=0; ir_ta[ep->ti][i].name; i++) { 349 | nep = &ir_ta[ep->ti][i]; 350 | nlp = lp + snprintf(lp, BSZ+(lrep-lp), ".%s", nep->name); 351 | nbp = bp + nep->off; 352 | if (nep->typ == T_tbl) newtable_byname(L,nep->name); 353 | errcnt += iir_unread(L, lrep, nlp, nbp, nep, 0); 354 | if (nep->typ == T_tbl) lua_pop(L,1); 355 | } 356 | 357 | } else { // Array of structs. 358 | for (j=ep->flb; j<=ep->fub; j++) { 359 | nlp = lp + snprintf(lp, BSZ+(lrep-lp), "[%d]", j); 360 | nbp = bp + (j - ep->flb)*ep->sz; 361 | newtable_byindex(L,j); 362 | errcnt += iir_unread(L, lrep, nlp, nbp, nep, 1); 363 | lua_pop(L,1); 364 | } 365 | } 366 | 367 | // Current IREP element is NOT a struct. (It is either scalar POD, 368 | // or an array thereof.) 369 | } else { 370 | 371 | if (ep->fub > 0) newtable_byname(L,ep->name); 372 | 373 | // Loop below executes at least once, for a scalar. More for array. 374 | i = ep->flb; // For a scalar, flb is always 1, and fub is always 0. 375 | do { 376 | if (ep->fub > 0) lua_pushinteger(L, i); // An array index, 377 | else lua_pushstring(L, ep->name); // Or the element name. 378 | 379 | // Push the element value onto the Lua stack. 380 | if (ep->typ == T_str) { 381 | lua_pushstring(L, (char *)bp); 382 | } else if (ep->typ == T_dbl) { 383 | lua_pushnumber(L, *((double *)bp)); 384 | } else if (ep->typ == T_int) { 385 | lua_pushinteger(L, *((int *)bp)); 386 | } else if (ep->typ == T_log) { 387 | lua_pushboolean(L, *((BOOLEAN *)bp)); 388 | } else { 389 | return Ir_error("IR_UNREAD: bad type: %s (%s)", lrep, s_typ[ep->typ]); 390 | } 391 | lua_settable(L,-3); // Set the table key+value (and pop both.) 392 | } while (++i <= ep->fub); 393 | if (ep->fub > 0) lua_pop(L,1); // If it was an array, pop it: we're done. 394 | } 395 | *lp = '\0'; 396 | return errcnt; 397 | } 398 | 399 | // Empty the Lua stack; load an arbitrary element name onto TOS. 400 | static int ir_elem(lua_State *L, const char *s) { 401 | char buf[BSZ]; 402 | (void)snprintf(buf, sizeof buf, "return %s", s); 403 | lua_settop(L,0); 404 | return luaL_loadstring(L, buf) || lua_pcall(L,0,1,0); 405 | } 406 | 407 | // External entry point: ir_read(L, "table[.subtable...]"). 408 | int ir_read(lua_State *L, const char *table_name) { 409 | int n = strlen(table_name); 410 | if (n > BSZ) return Ir_error("Table name too long: %s", table_name); 411 | 412 | if (ir_elem(L,table_name)) 413 | return Ir_error("Bad Lua table: %s: %s", table_name, lua_tostring(L,-1)); 414 | 415 | irep_debug = getenv("irep_debug") ? atoi(getenv("irep_debug")) : 0; 416 | 417 | // Find the well known table name first. 418 | char *s, tcopy[BSZ], lrep[BSZ]; 419 | (void)strcpy(tcopy, table_name); 420 | s = strtok(tcopy, ".[]"); 421 | int i = find_wkt(s); 422 | if (i == -1) return Ir_error("No such IREP table: %s (%s)", s,table_name); 423 | 424 | ir_wkt_desc *w = &ir_wktt[i]; 425 | void *bp = w->p; 426 | ir_element *ep = &w->e; 427 | 428 | // Walk down any remaining elements after the wkt name. 429 | while ((s = strtok(0, ".[]"))) { 430 | if (isalpha((int)(*s)) || *s == '_') { // string key 431 | int j = find_element(s, ir_ta[ep->ti]); 432 | if (j == -1) return Ir_error("IREP key not found: %s (%s)", s,table_name); 433 | ep = &ir_ta[ep->ti][j]; 434 | bp += ep->off; 435 | 436 | } else if (isdigit((int)(*s))) { // numeric key 437 | int j = atoi(s); 438 | if (jflb || j>ep->fub) 439 | return Ir_error("Array bounds exceeded: %s[%d] (%d:%d)", 440 | table_name, j, ep->flb, ep->fub); 441 | bp += (j - ep->flb)*ep->sz; 442 | 443 | } else { 444 | return Ir_error("Bad table element: %s (%s)", s,table_name); 445 | } 446 | } 447 | (void)strcpy(lrep, table_name); 448 | return iir_read(L, lrep, lrep+n, bp, ep); 449 | } 450 | 451 | // Push an IREP table to the lua_State (reverse of ir_read.) 452 | // For now, can only handle the whole wkt. 453 | int ir_unread(lua_State *L, const char *ir_tbl) { 454 | char lrep[BSZ]; 455 | int n = strlen(ir_tbl); 456 | if (n > BSZ) return Ir_error("Table name too long: %s", ir_tbl); 457 | 458 | // Find the IREP table. 459 | int i = find_wkt(ir_tbl); 460 | if (i == -1) return Ir_error("No such IREP table: %s", ir_tbl); 461 | ir_wkt_desc *w = &ir_wktt[i]; 462 | void *bp = w->p; 463 | ir_element *ep = &w->e; 464 | 465 | // (Re-)create the corresponding Lua table. 466 | lua_settop(L,0); 467 | lua_newtable(L); 468 | lua_pushvalue(L,-1); 469 | lua_setglobal(L,ir_tbl); 470 | (void)strcpy(lrep, ir_tbl); 471 | return iir_unread(L, lrep, lrep+n, bp, ep, 0); 472 | } 473 | 474 | // Check existence of an element. If found, leave it on TOS. 475 | int ir_exists(lua_State *L, const char *s) { 476 | if (ir_elem(L,s)) return 0; 477 | return !lua_isnil(L,-1); 478 | } 479 | 480 | // Return the run time length of a vector. 481 | int ir_rtlen(lua_State *L, const char *s) { 482 | if (ir_elem(L,s)) return -1; 483 | int n = lua_type(L,-1); 484 | return (n==LUA_TNIL) ? -1 : ((n==LUA_TNUMBER) ? 0 : (int)lua_objlen(L,-1)); 485 | } 486 | 487 | // Read an (arbitrarily large) string, stored earlier as an ir_reference. 488 | // The third argument can be NULL if you're not interested in the length. 489 | // The returned string must be copied into the caller's scope, and you 490 | // should call lua_pop(L,-1) after that is done, to allow Lua to garbage 491 | // collect the item. Typical calling sequence: 492 | // if (ir_exists(L, "physics.foo")) { 493 | // int nn; 494 | // std::string foo = ir_get_stringref(L,irep::physics.foo,&nn); 495 | // lua_pop(L,-1); 496 | // } 497 | const char *ir_get_stringref(lua_State *L, int n, int *len) { 498 | if (n != LUA_REFNIL) { 499 | lua_rawgeti(L, LUA_REGISTRYINDEX, n); 500 | int ii = lua_type(L,-1); 501 | if (ii == LUA_TSTRING) return lua_tolstring(L,-1,(size_t *)len); 502 | (void)fprintf(stderr,"ERROR (Lua/IR): IR_GET_STRINGREF: Bad value(%s): " 503 | "ir_reference variable should be a string",lua_typename(L,ii)); 504 | } 505 | return 0; 506 | } 507 | 508 | #if defined(__cplusplus) 509 | } 510 | #endif 511 | -------------------------------------------------------------------------------- /bin/irep-generate: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env lua 2 | -- Copyright 2016-2021 Lawrence Livermore National Security, LLC and other 3 | -- IREP Project Developers. See the top-level LICENSE file for details. 4 | -- 5 | -- SPDX-License-Identifier: MIT 6 | 7 | -- Print usage description and exit with exitcode (or 1 if not provided) 8 | local function usage(exitcode) 9 | print("irep-generate [-h] [--mode PART] HEADERS") 10 | print() 11 | print("This script generates source code for IREP well-known-table (WKT)") 12 | print("libraries.") 13 | print() 14 | print("Generated code is printed to standard out.") 15 | print() 16 | print("Positional Arguments:") 17 | print(" HEADERS wkt_*.h files from which to generate code, e.g.:") 18 | print(" wkt_geometry.h wkt_physics.h ...") 19 | print() 20 | print("Generation options") 21 | print(" --mode index generate only index (default)") 22 | print(" --mode fortran generate fortran module from a single wkt header") 23 | print(" --mode lua generate loadable, nested lua tables") 24 | print(" --mode rst generate restructured text (.rst) documentation") 25 | print() 26 | print(" --module-name name for generated module (fortran mode only,") 27 | print(" inferred from header name by default)") 28 | print() 29 | print("Documentation options (for use with --mode rst)") 30 | print(" --doc-dir DIR documentation directory where we look for") 31 | print(" details/intros for WKTs (default: .)") 32 | print() 33 | print("Environment variables:") 34 | print(" CPP C preprocessor to use (default gcc -E)") 35 | print(" CPPFLAGS flags for the C preprocessor") 36 | print() 37 | print("Help:") 38 | print(" -h, --help display this message") 39 | 40 | os.exit(exitcode or 1) 41 | end 42 | 43 | 44 | -- Lua doesn't have a split function, so use one from 45 | -- http://lua-users.org/wiki/SplitJoin 46 | function split(str, delim, maxNb) 47 | -- Eliminate bad cases... 48 | if string.find(str, delim) == nil then 49 | return { str } 50 | end 51 | if maxNb == nil or maxNb < 1 then 52 | maxNb = 0 -- No limit 53 | end 54 | local result = {} 55 | local pat = "(.-)" .. delim .. "()" 56 | local nb = 0 57 | local lastPos 58 | for part, pos in string.gfind(str, pat) do 59 | nb = nb + 1 60 | result[nb] = part 61 | lastPos = pos 62 | if nb == maxNb then 63 | break 64 | end 65 | end 66 | -- Handle the last field 67 | if nb ~= maxNb then 68 | result[nb + 1] = string.sub(str, lastPos) 69 | end 70 | return result 71 | end 72 | 73 | 74 | -- Trim whitespace from either side of a string 75 | local function trim(str) 76 | return str:match("^%s*(.-)%s*$") 77 | end 78 | 79 | 80 | -- Lua doesn't have a builtin way to test if a file exists 81 | function file_exists(name) 82 | local f=io.open(name,"r") 83 | if f~=nil then 84 | io.close(f) 85 | return true 86 | end 87 | return false 88 | end 89 | 90 | 91 | -- IREP type definitions 92 | local tmap = { 93 | T_int = "int", 94 | T_dbl = "double", 95 | T_log = "_Bool", 96 | T_str = "char", 97 | T_ref = "int", 98 | T_ptr = "void*", 99 | T_cbk = "lua_cb_data", 100 | } 101 | 102 | -- Which parts of the IREP should be in the output: 103 | -- 'index': 104 | -- 'fortran': 105 | local mode_values = { 106 | ["index"] = true, 107 | ["fortran"] = true, 108 | ["lua"] = true, 109 | ["rst"] = true, 110 | } 111 | 112 | -- generate index by default 113 | local mode = "index" 114 | 115 | -- names of WKT headers for which we should generate code 116 | local wkt_headers = {} 117 | 118 | -- fortran module name (for fortran mode) 119 | local fortran_module_name 120 | 121 | -- CPP and CPPFLAGS come from the environment 122 | local cpp = os.getenv("CPP") or "gcc -E" 123 | local cppflags = os.getenv("CPPFLAGS") or "" 124 | 125 | -- location to search for additional rst files for docs 126 | local doc_dir = "." 127 | 128 | 129 | -- sort keys of a table and return them as an integer-indexed table 130 | local function sorted_keys(tab) 131 | keys = {} 132 | for k, _ in pairs(tab) do 133 | table.insert(keys, k) 134 | end 135 | table.sort(keys) 136 | return keys 137 | end 138 | 139 | 140 | -- Parse arguments from the command line, per the description in usage() 141 | local function parse_args() 142 | -- no args just prints usage 143 | if #arg == 0 then 144 | usage() 145 | end 146 | 147 | -- parse any command line options 148 | local i = 1 149 | while i <= #arg and arg[i]:match("^-") do 150 | if arg[i] == "-h" or arg[i] == "--help" then 151 | usage(0) 152 | elseif arg[i] == "--mode" then 153 | i = i + 1 154 | if not mode_values[arg[i]] then 155 | print("bad option for --mode: '" .. arg[i] .. "'") 156 | values = table.concat(sorted_keys(mode_values), "', '") 157 | print(string.format("valid values are: '%s'", values)) 158 | os.exit(1) 159 | end 160 | mode = arg[i] 161 | elseif arg[i] == "--module-name" then 162 | i = i + 1 163 | fortran_module_name = arg[i] 164 | elseif arg[i] == "--doc-dir" then 165 | i = i + 1 166 | doc_dir = arg[i] 167 | else 168 | print("invalid option: '" .. arg[i] .. "'") 169 | os.exit(1) 170 | end 171 | i = i + 1 172 | end 173 | 174 | -- user must provide some headers or we'll just error out here 175 | if i > #arg then 176 | print("error: no headers provided") 177 | os.exit(1) 178 | end 179 | 180 | -- accumulate header names in wkt_headers 181 | while i <= #arg do 182 | local header = arg[i] 183 | 184 | -- header needs to end in .h, as C preprocessors are picky 185 | if not header:match("%.h$") then 186 | print("error: bad header name: '" .. header .. "'") 187 | print("must end with '.h'") 188 | os.exit(1) 189 | end 190 | table.insert(wkt_headers, header) 191 | i = i + 1 192 | end 193 | 194 | -- sort list of headers after accumulating them all 195 | table.sort(wkt_headers) 196 | end 197 | 198 | 199 | -- execute a command and fail if it doesn't succeed. 200 | -- otherwise, return a open file handle to its output, 201 | -- and the name of the output file. 202 | -- 203 | -- caller must close the stream and remove the file. 204 | -- 205 | -- we can't use something simpler like popen in lua 5.1 unless we want to 206 | -- throw out the error code. -- 5.2 has better error reporting but we 207 | -- don't require a lua that new. 208 | function execute_command(command) 209 | local tmpfile = os.tmpname() .. ".out" 210 | local exit = os.execute(command .. ' > ' .. tmpfile) 211 | local failed = ( 212 | (_ENV and exit == nil) or -- lua >= 5.2 213 | (not _ENV and exit ~= 0) -- lua < 5.2 214 | ) 215 | if failed then 216 | print("Command failed: '" .. command .. "'") 217 | os.exit(1) 218 | end 219 | return io.open(tmpfile, "r"), tmpfile 220 | end 221 | 222 | 223 | -- path to dir containing this lua script 224 | function script_dir() 225 | local str = debug.getinfo(2, "S").source:sub(2) 226 | return str:match("(.*/)") 227 | end 228 | 229 | 230 | -- Takes one wkt_*.h and generates the corresponding fortran code 231 | local function generate_fortran() 232 | if #wkt_headers ~= 1 then 233 | print("error: `irep-generate --mode fortran` takes exactly one header") 234 | os.exit(1) 235 | end 236 | 237 | -- infer module name from header if it's not provided 238 | local header = wkt_headers[1] 239 | if not fortran_module_name then 240 | fortran_module_name = header:gsub(".h$", ""):gsub(".*/", "") 241 | end 242 | 243 | -- arguments for the preprocessor 244 | local cpp_args = { 245 | cpp, 246 | "-DIR_WKT_NAME=" .. fortran_module_name, 247 | "-DIREP_LANG_FORTRAN", 248 | cppflags, 249 | "-I" .. script_dir() .. "../", -- always include from irep and . 250 | "-I.", 251 | header, 252 | } 253 | local cpp_cmd = table.concat(cpp_args, " ") 254 | local p, tmpfile = execute_command(cpp_cmd) 255 | 256 | -- read through lines of input and filter out preprocessor artifacts 257 | for line in p:lines() do 258 | line = line:gsub("%s+%+/%+/", " //") 259 | line = line:gsub(' *## *', '') 260 | if not (line:match('^#') or line:match("^%s*$")) then 261 | print(line) 262 | end 263 | end 264 | p:close() 265 | os.remove(tmpfile) 266 | os.exit(0) 267 | end 268 | 269 | 270 | local function generate_preamble() 271 | local c = (mode == "fortran") and "!" or "//" 272 | print(c .. " This file was generated by irep-generate. Do not modify.") 273 | print(c .. " For more on IREP, see https://github.com/LLNL/irep.") 274 | print(c .. "") 275 | print(c .. " Mode: " .. mode) 276 | print(c .. "") 277 | print(c .. " Environment:") 278 | print(c .. " CPP: '" .. cpp .. "'") 279 | print(c .. " CPPFLAGS: '" .. cppflags .. "'") 280 | print(c .. "") 281 | print(c .. "") 282 | end 283 | 284 | 285 | -- generate includes for input headers at top of output 286 | local function generate_includes(ostream) 287 | for _, header in ipairs(wkt_headers) do 288 | -- we want the #include to *just* be the filename, not full path 289 | local filename = header:match("/([^/]+)$") or header 290 | ostream:write(string.format("#include \"%s\"\n", filename)) 291 | end 292 | ostream:write("\n") 293 | end 294 | 295 | 296 | -- print out pairs in the sorted order of their values 297 | local function pairsbyvalues(t,f) 298 | local rev, a = {}, {} 299 | for k,v in pairs(t) do rev[v] = k; a[#a + 1] = v end 300 | table.sort(a,f) 301 | local i = 0 302 | return function() 303 | i=i+1 304 | return a[i],rev[a[i]] 305 | end 306 | end 307 | 308 | -- Stbl is an associative string table: stbl.num_comp = "s0123", etc. 309 | -- It's not really necessary, but makes the output look neater. String 310 | -- numbers count up from 0. 311 | local scnt, stbl = -1, {} 312 | 313 | 314 | -- convenience function for adding to the string table 315 | local function add2stbl(s) 316 | if not stbl[s] then 317 | scnt = scnt + 1 318 | stbl[s] = string.format("s%04d",scnt) 319 | end 320 | end 321 | 322 | 323 | -- These variables hold table state while we parse the input. 324 | -- tbl_list is a 0-based array. Each element corresponds to one of the 325 | -- structs in a wkt_*.h file (See the IREP "Beg_struct" macro.) Each element 326 | -- is itself a table, containing most or all the fields (depending on the 327 | -- element type) of the "ir_element" struct in irep.c. 328 | local tcnt, tbl_list = -1, {} 329 | 330 | -- wkt_list is similar to tbl_list. It records similar information for 331 | -- each well-known-table. Each entry in wkt_list effectively contains an 332 | -- ir_element, plus the base address of the well-known-table instance. 333 | local wcnt, wkt_list = -1, {} 334 | 335 | -- rev_ta is an associative table of type names, such as "irt_method", 336 | -- with that name's index in tbl_list. 337 | local rev_ta = {} 338 | 339 | -- Typename is an array parallel to tbl_list, that lists the structure 340 | -- name for each index. 341 | local typename = {} 342 | 343 | 344 | -- run the C preprocessor over all input WKT heders and collect the 345 | -- things to be generated in the various tables above 346 | local function process_headers() 347 | -- create a temporary file with WKT #includes 348 | -- compilers are picky about names, so we append .h to this name 349 | tmpfile_name = os.tmpname() .. ".h" 350 | tmpfile = io.open(tmpfile_name, "w") 351 | generate_includes(tmpfile) 352 | tmpfile:close() 353 | 354 | -- run the preprocessor on the WKT includes and read its output 355 | local cpp_args = { 356 | cpp, 357 | "-DIREP_GENERATE", 358 | cppflags, 359 | "-I" .. script_dir() .. "../", -- always include from irep and . 360 | "-I.", 361 | tmpfile_name 362 | } 363 | local cpp_cmd = table.concat(cpp_args, " ") 364 | local p, outfile_name = execute_command(cpp_cmd) 365 | 366 | local ct -- type name of the struct being declared, e.g., "irt_sources" 367 | 368 | -- loop invokes this function for each line of input 369 | local function handle_line(line) 370 | local f1,f2,f3,f4 = line:match("%s*(%S+)%s+(%S+)%s+(%S+)%s+(%S+)") 371 | if not f1 then error("Bad input: " .. line) end 372 | 373 | add2stbl(f2) 374 | if f1=="bst" then -- Begin structure declaration. 375 | if ct then error("ct should be nil at " .. f2) end 376 | assert(not rev_ta[f2], "Repeated table: " .. f2) 377 | ct = f2 378 | tcnt = tcnt + 1 379 | tbl_list[tcnt] = {} 380 | typename[tcnt] = f2 381 | rev_ta[f2] = tcnt 382 | 383 | elseif f1=="est" then -- End structure declaration. 384 | assert(ct == f2, "Expected equality: " .. ct .."==" .. f2) 385 | ct = nil 386 | 387 | elseif f1:match("T_[dilprs]") then -- Leaf declaration (POD or pointer). 388 | tbl_list[tcnt][f2] = { 389 | typ = f1, 390 | len = tonumber(f3), 391 | flb = 1, 392 | fub = tonumber(f4), 393 | } 394 | 395 | elseif f1=="T_cbk" then -- Leaf declaration (callback function.) 396 | local nprm,nret = f3:match("([+-]?%d+):([+-]?%d+)") 397 | nprm,nret = tonumber(nprm),tonumber(nret) 398 | -- Strictly speaking, the lower limit for nprm,nret is -9. However, 399 | -- nprm<-1 or nret<-1 is semantically incorrect at this time. 400 | if nprm < -1 or nprm > 1014 then error("Bad nprm: " .. nprm) end 401 | if nret < -1 or nret > 2097142 then error("Bad nret: " .. nret) end 402 | local len = nprm+9 + (nret+9)*1024 403 | tbl_list[tcnt][f2] = { 404 | typ = f1, 405 | len = len, 406 | flb = 1, 407 | fub = tonumber(f4), 408 | } 409 | 410 | elseif f1=="T_tbl" then -- Node declaration. 411 | local flb,fub = f4:match("([+-]?%d+):([+-]?%d+)") 412 | tbl_list[tcnt][f2] = { 413 | typ = f1, 414 | tname = f3, 415 | len = 0, 416 | flb = tonumber(flb), 417 | fub = tonumber(fub), 418 | } 419 | add2stbl(f3) 420 | 421 | elseif f1=="wkt" then -- WKT definition. 422 | assert(rev_ta[f3], "No type declaration for: " .. f3) 423 | local flb,fub = f4:match("([+-]?%d+):([+-]?%d+)") 424 | wcnt = wcnt + 1 425 | wkt_list[wcnt] = { 426 | name = f2, 427 | tname = f3, 428 | flb = tonumber(flb), 429 | fub = tonumber(fub), 430 | ti=rev_ta[f3], 431 | } 432 | add2stbl(f3) 433 | 434 | else 435 | error("Bad input: " .. line) 436 | end 437 | end 438 | 439 | -- main loop over input lines. The + in gmatch skips blank lines 440 | for line in string.gmatch(p:read("*all"), '[^\n]+') do 441 | -- match() here skips preprocessor directives like line numbers 442 | if not line:match("^#") then 443 | handle_line(line) 444 | end 445 | end 446 | 447 | -- make sure every struct was opened and closed 448 | if ct then 449 | error("(At end) ct should be nil: " .. ct) 450 | end 451 | 452 | -- close preprocessor pipe and temporary input file 453 | p:close() 454 | os.remove(tmpfile_name) 455 | os.remove(outfile_name) 456 | end 457 | 458 | 459 | local function generate_string_table() 460 | print("// Part 1: The string table.") 461 | for k,v in pairsbyvalues(stbl) do 462 | print("#define " .. k .. " " .. v) 463 | end 464 | print("") 465 | end 466 | 467 | 468 | local function generate_element_tables() 469 | print("// Part 2: The element tables.") 470 | local f1 = function(ti, tname, t) 471 | print("static ir_element " .. tname .. "[] = { // " .. typename[ti]) 472 | for k,v in pairs(t) do 473 | local idesc = rev_ta[v.tname] or -1 474 | local szo = string.format("S(%s)", stbl[v.tname] or stbl[tmap[v.typ]]) 475 | if v.typ == "T_str" and v.fub > 0 then -- An array of strings. 476 | -- Stride == Fortran "len" of item. 477 | szo = string.format("%8d", v.len) 478 | end 479 | print(string.format( 480 | " { Q(%s),%3d, %s, O(%s,%s), %8d,%3d,%3d, %s },", 481 | stbl[k], idesc, szo, stbl[typename[ti]], stbl[k], 482 | v.len, v.flb, v.fub, v.typ 483 | )) 484 | end 485 | print(" { 0 }\n};\n") 486 | end 487 | 488 | for i=0,tcnt do 489 | f1(i, string.format("ir_tbl%03d", i), tbl_list[i]) 490 | end 491 | end 492 | 493 | 494 | local function generate_table_pointers() 495 | print("// Part 3: A list of pointers to the ir_element tables.") 496 | print("ir_element *ir_ta[] = {") 497 | for i=0,tcnt do 498 | print(string.format(" ir_tbl%03d, // %s",i,typename[i])) 499 | end 500 | print("};\n") 501 | end 502 | 503 | 504 | local function generate_wkt_table() 505 | print("// Part 4: List the well-known tables.") 506 | print("ir_wkt_desc ir_wktt[] = {") 507 | for i=0, wcnt do 508 | local t = wkt_list[i] 509 | local nm = stbl[t.name] 510 | local tnm = stbl[t.tname] 511 | print(string.format( 512 | " { &%s, {Q(%s),%3d,S(%s),0,0,%3d,%3d,T_tbl }}, // %s", 513 | nm,nm,t.ti,tnm,t.flb,t.fub,t.name 514 | )) 515 | end 516 | print("};") 517 | print() 518 | print("// Total number of well-known tables in this index"); 519 | print(string.format("size_t ir_wktt_size = %d;", wcnt + 1)) 520 | end 521 | 522 | 523 | --- 524 | --- Functions for generating lua code. 525 | --- 526 | 527 | -- create a temporary header file with some modifications to preserve 528 | -- newlines. Specifically, escape newlines and spaces in docs so we can 529 | -- recover them later. 530 | -- 531 | -- returns the temporary header name 532 | local function mark_up_whitespace(header) 533 | local tmpfile_name = os.tmpname() .. ".h" 534 | local f = assert(io.open(header, "rb")) 535 | 536 | local tmpfile = assert(io.open(tmpfile_name, "w")) 537 | 538 | in_doc = false 539 | for line in f:lines() do 540 | if line:match("Doc%(%(") then 541 | in_doc = true 542 | end 543 | 544 | if line:match("%)%)") then 545 | in_doc = false 546 | end 547 | 548 | if in_doc then 549 | -- escape newlines and spaces in Doc(()) sections 550 | if not line:match("Doc%(%(") then 551 | line = line:gsub(" ", "\\ ") 552 | end 553 | tmpfile:write(line) 554 | tmpfile:write("\\n") 555 | else 556 | tmpfile:write(line) 557 | tmpfile:write("\n") 558 | end 559 | end 560 | f:close() 561 | tmpfile:close() 562 | 563 | return tmpfile_name 564 | end 565 | 566 | 567 | -- Helper for generate_lua -- filters C preprocessor output 568 | -- into something lua can load before assembling final tables. 569 | local function _wkt_to_loadable_lua(header) 570 | local tmp_header = mark_up_whitespace(header) 571 | 572 | -- run the preprocessor on the WKT header and generate lua 573 | local cpp_args = { 574 | cpp, 575 | "-DIREP_LANG_LUA", 576 | "-I" .. script_dir() .. "../", -- always include from irep and . 577 | "-I" .. script_dir() .. "../include", 578 | "-I.", 579 | cppflags, 580 | tmp_header 581 | } 582 | local cpp_cmd = table.concat(cpp_args, " ") 583 | local p, outfile_name = execute_command(cpp_cmd) 584 | 585 | local text = '' 586 | for line in p:lines() do 587 | if line:match("^%s*[^%s]+%s*=%s*[^%s]") or line:match("^}") then 588 | -- just print assignments and close braces 589 | text = text .. line .. "\n" 590 | 591 | elseif line:find("VDEFINE") then 592 | -- generate a table assignment from VDEFINE 593 | vname, val = line:match("VDEFINE ([%w_]+) ([%w_]+)") 594 | text = text .. string.format("%s={[1]=%s}\n", vname, val) 595 | 596 | elseif line:find("@@@") then 597 | -- convert lines with @@@ to strings for processing later 598 | -- " is converted to ' for nesting in a double-quoted string 599 | name, rest = line:match("([%w_]+) @@@ (.*)$") 600 | rest = rest:gsub("\"", "'") 601 | text = text .. string.format(' %s = "%s",\n', name, rest) 602 | 603 | elseif not (line:match("^%s*$") or line:match("^#")) then 604 | -- skip newline and cpp directives, or error 605 | text = text .. " [[[[ ERROR ]]]]\n" 606 | end 607 | end 608 | 609 | p:close() 610 | os.remove(outfile_name) 611 | os.remove(tmp_header) 612 | 613 | -- lua is ok with it, but luajit does not like "\ ". Convert the 614 | -- escaped spaces back to be compatible with both. 615 | text = text:gsub("\\ ", " ") 616 | 617 | return text 618 | end 619 | 620 | 621 | -- 622 | -- given a wkt header, build lua tables recursively. 623 | -- 624 | local function build_lua_tables(header, field, open, close) 625 | simple_tables = _wkt_to_loadable_lua(header) 626 | assert(loadstring(simple_tables))() 627 | 628 | -- handler functions are no-ops if not provided 629 | field = field or function() end 630 | open = open or function() end 631 | close = close or function() end 632 | 633 | local seen = {} 634 | tdump = function (tname, t, lrep, level) 635 | open(tname, lrep, level) 636 | 637 | -- sort keys in the table so they print out in alphabetical order 638 | local klist = {} 639 | for k, v in pairs(t) do 640 | table.insert(klist, k) 641 | end 642 | table.sort( 643 | klist, 644 | function(a, b) 645 | ta, tb = type(t[a]), type(t[b]) 646 | if ta == tb or (ta ~= "table" and tb ~= "table") then 647 | -- if both or neither is a table, compare by name 648 | return a:lower() < b:lower() 649 | else 650 | -- subtables come after all other elements 651 | return tb == "table" 652 | end 653 | end 654 | ) 655 | 656 | -- iterator over sorted keys and values 657 | for i, k in pairs(klist) do 658 | local v = t[k] 659 | local nlrep 660 | 661 | if type(k) == "string" then 662 | nlrep = lrep .. "." .. k 663 | else 664 | nlrep = lrep .. "[" .. k .. "]" 665 | end 666 | 667 | if type(v) == "table" then 668 | seen[v] = (seen[v] or 0) + 1 669 | -- TODO: why this limit of 6? 670 | if seen[v] <= 6 then -- do not recurse (much) on self-reference. 671 | tdump(k, v, nlrep, level + 1) 672 | end 673 | else 674 | -- parse raw strings like: 675 | -- typ %%% default %%% lo %%% hi %%% docstring 676 | local typecode, default, strlen, nelem, doc = unpack( 677 | split(v, "%%%%%%")) 678 | 679 | doc = trim(doc or '') 680 | doc = doc:match("^%(%s?(.-)%s?%)$") or '' 681 | 682 | local field_tab = { 683 | ["tname"] = tname, 684 | ["name"] = k, 685 | ["value"] = v, 686 | ["lrep"] = lrep, 687 | ["nlrep"] = nlrep, 688 | ["typecode"] = trim(typecode), 689 | ["default"] = trim(default), 690 | ["strlen"] = tonumber(trim(strlen)), 691 | ["nelem"] = tonumber(trim(nelem)), 692 | ["doc"] = doc, 693 | ["level"] = level, 694 | } 695 | field(field_tab) 696 | end 697 | end 698 | close(tname, lrep, level) 699 | end 700 | 701 | local top = assert(header:match("wkt_([^%.]*)%.h")) 702 | tdump(top, _G[top], top, 0) 703 | end 704 | 705 | 706 | local function generate_lua() 707 | if #wkt_headers ~= 1 then 708 | print("error: `irep-generate --mode lua` takes exactly one header") 709 | os.exit(1) 710 | end 711 | local header = wkt_headers[1] 712 | local ostream = io.output() 713 | 714 | local function indent(level) 715 | return string.rep(" ", level * 2) 716 | end 717 | 718 | local function open(tname, lrep, level) 719 | -- construct a string to this table. if tname is a number, this is 720 | -- one table in a vector. 721 | local fmt = (type(tname) == "number") and "%s[%d] = {\n" or "%s%s = {\n" 722 | ostream:write(string.format(fmt, indent(level), tname)) 723 | end 724 | 725 | local function field(f) 726 | -- clean up default after splitting 727 | default = f.default 728 | if default == 'function' then 729 | default = 'function() end' 730 | end 731 | 732 | -- handle multi-line docs with multi-line --[[ ]] comments 733 | doc = f.doc:match("[^%s]+") and (" --[[ " .. f.doc .. " ]]") or '' 734 | ostream:write( 735 | string.format( 736 | "%s %s = %s,%s\n", 737 | indent(f.level), f.name, default, doc 738 | ) 739 | ) 740 | end 741 | 742 | -- close out the table 743 | local function close(tname, lrep, level) 744 | local comma = (level == 0) and '' or ',' 745 | ostream:write(string.format("%s}%s\n", indent(level), comma)) 746 | end 747 | 748 | build_lua_tables(header, field, open, close) 749 | end 750 | 751 | --- 752 | --- Functions for generating rst documentation. 753 | --- 754 | 755 | local rst_types = { 756 | ["sd"] = ":doc:`double <%s/glossary/sdbl>`", 757 | ["si"] = ":doc:`integer <%s/glossary/sint>`", 758 | ["sb"] = ":doc:`boolean <%s/glossary/slog>`", 759 | ["ss"] = ":doc:`string <%s/glossary/sstr>`", 760 | ["vd"] = ":doc:`double vector <%s/glossary/vdbl>`", 761 | ["vi"] = ":doc:`integer vector <%s/glossary/vint>`", 762 | ["vb"] = ":doc:`boolean vector <%s/glossary/vlog>`", 763 | ["vs"] = ":doc:`string vector <%s/glossary/vstr>`", 764 | ["lf"] = ":doc:`function <%s/glossary/callback>`", 765 | } 766 | 767 | 768 | local function rst_type(f) 769 | if f.typecode == "sd" then 770 | return ":ref:`double `" 771 | elseif f.typecode == "si" then 772 | return ":ref:`integer `" 773 | elseif f.typecode == "sb" then 774 | return ":ref:`boolean `" 775 | elseif f.typecode == "ss" then 776 | return string.format(":ref:`string(%d) `", f.strlen) 777 | elseif f.typecode == "vd" then 778 | return string.format(":ref:`double[%d] `", f.nelem) 779 | elseif f.typecode == "vi" then 780 | return string.format(":ref:`integer[%d] `", f.nelem) 781 | elseif f.typecode == "vb" then 782 | return string.format(":ref:`boolean[%d] `", f.nelem) 783 | elseif f.typecode == "vs" then 784 | return string.format( 785 | ":ref:`string(%d)[%d] `", f.strlen, f.nelem) 786 | elseif f.typecode == "lf" then 787 | return string.format( 788 | ":ref:`callback ` /%d |rarr| %d", f.strlen, f.nelem) 789 | else 790 | error("Bad typecode: " .. f.typecode) 791 | end 792 | end 793 | 794 | 795 | -- generate a list of candidate files to search for "details" on a field 796 | -- in a WKT. For a field like material.ceilings.density, the list would 797 | -- look like: 798 | -- 799 | -- material/ceilings-density.rst 800 | -- material/ceilings.rst 801 | -- material/material.rst 802 | -- 803 | local function details_file_candidates(nlrep) 804 | local candidates = {} 805 | local directory, rest = nlrep:match('^([^%.]*)%.?(.*)$') 806 | local last = directory 807 | table.insert( 808 | candidates, 809 | doc_dir .. "/" .. directory .. "/" .. directory .. ".rst" 810 | ) 811 | 812 | local component 813 | while rest ~= '' do 814 | component, rest = rest:match('^([^%.]*)%.?(.*)$') 815 | local sep = (#candidates == 1) and "/" or "-" 816 | last = last .. sep .. component 817 | table.insert(candidates, 1, doc_dir .. "/" .. last .. ".rst") 818 | end 819 | return candidates 820 | end 821 | 822 | 823 | -- Searches for details file associated with a field in a WKT. 824 | -- 825 | -- First generates the candidates list (see details_file_candidates). The 826 | -- first file in this list that exists will be returned and used as the 827 | -- target for a "details" link in the documentation. If no such file 828 | -- exists, this returns nil. 829 | local function find_details_file(nlrep) 830 | local candidates = details_file_candidates(nlrep) 831 | for _, path in pairs(candidates) do 832 | if file_exists(path) then 833 | return path 834 | end 835 | end 836 | return nil 837 | end 838 | 839 | 840 | -- legal title underlines for RST 841 | local rst_title_chars = { "=", "-", '"', "'", "~", "^", "+", "*" } 842 | 843 | 844 | -- build an underline for a level 845 | local function title_line(level, ostream) 846 | ostream:write(string.rep(rst_title_chars[level + 1], 63)) 847 | ostream:write("\n") 848 | end 849 | 850 | 851 | -- print out standard sphinx includes for extras in this doc 852 | local function rst_includes(ostream) 853 | ostream:write("\n\n.. include:: \n\n") 854 | ostream:write(".. |br| raw:: html\n\n") 855 | ostream:write("
\n") 856 | end 857 | 858 | 859 | -- word-wrap text to a character limit 860 | function wrap(str, limit, sep, indent, indent1) 861 | sep = sep or "\n" 862 | indent = indent or "" 863 | indent1 = indent1 or indent 864 | limit = limit or 72 865 | local here = 1-#indent1 866 | local function check(sp, st, word, fi) 867 | if fi - here > limit then 868 | here = st - #indent 869 | return sep..indent..word 870 | end 871 | end 872 | return indent1..str:gsub("(%s+)()(%S+)()", check) 873 | end 874 | 875 | 876 | -- Geneate RST documentation for a wkt file. 877 | -- This is what --mode rst ends up invoking. 878 | local function generate_rst_file(header, ostream) 879 | local top = assert(header:match("wkt_([^%.]*)%.h")) 880 | 881 | -- Track recently opened tables and when to write headers. 882 | -- Note that the way we do this assumes that subtables come 883 | -- after other elements. Needs a rework if field order changes. 884 | local needs_headers = false 885 | local cur_subtable = nil 886 | 887 | local function subtable(top, full_name) 888 | ostream:write(string.format("\n.. _%s.subtable.%s:\n", top, full_name)) 889 | ostream:write(string.format("\nsubtable ``%s``\n", full_name)) 890 | 891 | -- write out a title line for the appropriate level 892 | local _, count = full_name:gsub("%.", "") 893 | title_line((count or 0) + 1, ostream) 894 | 895 | cur_subtable = full_name 896 | end 897 | 898 | -- this begins tables and subtables 899 | local function open(tname, lrep, level) 900 | if level == 0 then 901 | needs_headers = true 902 | -- top level table: print a large title bar 903 | title_line(0, ostream) 904 | ostream:write(string.format("The ``%s`` table\n", tname)) 905 | title_line(0, ostream) 906 | 907 | intro_path = string.format("%s/%s/introduction.rst", doc_dir, tname) 908 | if file_exists(intro_path) then 909 | ostream:write(string.format("\n.. include:: %s\n\n", intro_path)) 910 | end 911 | rst_includes(ostream) 912 | 913 | elseif not lrep:match("%[%d%]$") then 914 | full_name = lrep:gsub("%[%d%]", "") 915 | full_name = full_name:match(string.format("^%s%%.(.*)$", top)) 916 | subtable(top, full_name) 917 | needs_headers = true 918 | end 919 | end 920 | 921 | -- this prints field rows in doc tables 922 | local function field(f) 923 | -- if we're printing variables from a recently opened table, create 924 | -- the list table in rst, including headers 925 | if needs_headers then 926 | ostream:write("\n.. list-table::\n") 927 | ostream:write(" :widths: 15 5 80\n") 928 | ostream:write(" :header-rows: 1\n\n") 929 | ostream:write() 930 | ostream:write(" * - Name / Type\n") 931 | ostream:write(" - Default\n") 932 | ostream:write(" - Description\n") 933 | needs_headers = false 934 | end 935 | 936 | local no_array_nlrep = f.nlrep:gsub("%[%d+%]", "") 937 | local identifier = no_array_nlrep:match(string.format("^%s%%.(.*)$", top)) 938 | if cur_subtable then 939 | identifier = identifier:match( 940 | string.format("^%s%%.(.*)$", cur_subtable)) or identifier 941 | end 942 | 943 | -- print link to type-specific help 944 | default = (f.default == 'function') and 'no-op' or f.default 945 | if string.len(default) > 12 then 946 | default = wrap(default, 12, "`` |br| ``") 947 | end 948 | 949 | -- wrap docstring, unless it's multi-line, in which case we leave it. 950 | local doc = '' 951 | if f.doc ~= '' then 952 | local n 953 | -- ensure multi-line docstrings are properly indented for rst 954 | doc, n = f.doc:gsub("\n", "\n ") 955 | 956 | -- break up and wrap long single-line docstrings 957 | if n == 0 then 958 | doc = wrap(f.doc, 60, " |br| ") 959 | end 960 | end 961 | 962 | -- generate details link if there's a file to link to 963 | type_details = rst_type(f) 964 | details_file = find_details_file(f.nlrep) 965 | if details_file then 966 | type_details = type_details .. " |br| " .. string.format( 967 | ":doc:`(details) <%s>`", details_file:match("^(.*).rst$")) 968 | end 969 | 970 | 971 | ostream:write(string.format(" * - .. _%s:\n\n", no_array_nlrep)) 972 | ostream:write(string.format(" **%s** |br| %s\n", 973 | identifier, type_details)) 974 | ostream:write(string.format(" - ``%s``\n", default)) 975 | ostream:write(string.format(" - %s\n", doc)) 976 | end 977 | 978 | -- run build_lua_tables with a handler, and just accumulate the tables 979 | build_lua_tables(header, field, open) 980 | end 981 | 982 | 983 | local function copy_file(src_filename, dest_filename) 984 | local istream = assert(io.open(src_filename, "r")) 985 | local content = istream:read("*a") 986 | istream:close() 987 | 988 | local ostream = assert(io.open(dest_filename, "w")) 989 | ostream:write(content) 990 | ostream:close() 991 | end 992 | 993 | 994 | -- generate .rst files from wtk headers, and copy in irep-types.rst 995 | -- to go along with them. 996 | local function generate_rst() 997 | for _, header in ipairs(wkt_headers) do 998 | local filename = header:match("/?wkt_([^%.]*)%.h$") 999 | filename = string.format("%s.rst", filename) 1000 | 1001 | local ostream = assert(io.open(filename, "w")) 1002 | print(string.format("Generating %s", filename)) 1003 | generate_rst_file(header, ostream) 1004 | ostream:close() 1005 | end 1006 | 1007 | -- read irep-types.rst from the irep directory 1008 | local types_filename = script_dir().."/../docs/irep_types.rst" 1009 | print(string.format("Creating irep_types.rst")) 1010 | copy_file(types_filename, "irep_types.rst") 1011 | end 1012 | 1013 | --- 1014 | --- Functions for generating wkt-index libraries. 1015 | --- 1016 | local function generate_index() 1017 | generate_includes(io.output()) 1018 | 1019 | -- first elements in the string table are from tmap 1020 | for k,v in pairs(tmap) do 1021 | add2stbl(v) 1022 | end 1023 | process_headers() 1024 | 1025 | -- structures from ir_index.h are used in the tables below. 1026 | print('#include "ir_index.h"') 1027 | print() 1028 | 1029 | -- Q, str, O, S are used only in the generated output, and they 1030 | -- probably shouldn't be exposed outside irep. 1031 | print("#define Q(s) str(s)") 1032 | print("#define str(x) #x") 1033 | print("#define O(a,b) offsetof(a,b)") 1034 | print("#define S(a) sizeof(a)") 1035 | print() 1036 | 1037 | -- offset tables for tables and sub-tables 1038 | generate_string_table() 1039 | generate_element_tables() 1040 | 1041 | -- top-level tables go in this index, which is where ir_read starts 1042 | -- looking when it translates irep expressions. 1043 | generate_table_pointers() 1044 | generate_wkt_table() 1045 | end 1046 | 1047 | 1048 | -- 1049 | -- Main script execution starts here 1050 | -- 1051 | parse_args() 1052 | 1053 | generators = { 1054 | ["index"] = generate_index, 1055 | ["fortran"] = generate_fortran, 1056 | ["lua"] = generate_lua, 1057 | ["rst"] = generate_rst, 1058 | } 1059 | 1060 | -- run the generator for the mode 1061 | generators[mode]() 1062 | --------------------------------------------------------------------------------