├── .gitignore ├── README.md ├── plexlang ├── .gitignore ├── README.md ├── bin │ └── plexl ├── covanalysis ├── docs │ ├── buildhtml │ ├── manual.html │ ├── manual.md │ ├── style.css │ └── template.html ├── example_c │ ├── Makefile │ ├── README.md │ ├── embedded_main.c │ ├── static_build.t │ └── static_call_main.c ├── examples │ ├── geometric.t │ ├── isct_predicates.t │ └── tensor_expressions.t ├── release │ └── plexlang │ │ ├── plexlang.t │ │ ├── plexlib.t │ │ └── src │ │ ├── ast.t │ │ ├── builtins.t │ │ ├── c.t │ │ ├── codegen.t │ │ ├── coverage.t │ │ ├── functions.t │ │ ├── parser.t │ │ ├── pratt.t │ │ ├── shape.t │ │ ├── specializer.t │ │ ├── stats.t │ │ ├── typechecker.t │ │ └── types.t ├── runtests └── tests │ ├── arithmetic.t │ ├── ast_unit.t │ ├── c_build.t │ ├── constants.t │ ├── control.t │ ├── functions.t │ ├── isct_predicates.t │ ├── parse_unit.t │ ├── specialize_unit.t │ ├── tensor.t │ ├── tensor_index.t │ ├── tensorarith.t │ ├── test.lua │ ├── trivial.t │ ├── type_unit.t │ └── typechecker_unit.t ├── simplang ├── .gitignore ├── README.md ├── bin │ └── simpl ├── covanalysis ├── docs │ ├── buildhtml │ ├── manual.html │ ├── manual.md │ ├── style.css │ └── template.html ├── example_c │ ├── Makefile │ ├── README.md │ ├── embedded_main.c │ ├── static_build.t │ └── static_call_main.c ├── examples │ └── isct_predicates.t ├── release │ └── simplang │ │ ├── simplang.t │ │ ├── simplib.t │ │ └── src │ │ ├── ast.t │ │ ├── builtins.t │ │ ├── c.t │ │ ├── codegen.t │ │ ├── coverage.t │ │ ├── functions.t │ │ ├── parser.t │ │ ├── pratt.t │ │ ├── shape.t │ │ ├── specializer.t │ │ ├── stats.t │ │ ├── typechecker.t │ │ └── types.t ├── runtests └── tests │ ├── arithmetic.t │ ├── ast_unit.t │ ├── c_build.t │ ├── constants.t │ ├── control.t │ ├── functions.t │ ├── isct_predicates.t │ ├── parse_unit.t │ ├── specialize_unit.t │ ├── test.lua │ ├── trivial.t │ ├── type_unit.t │ └── typechecker_unit.t ├── tenslang ├── .gitignore ├── README.md ├── bin │ └── tensl ├── covanalysis ├── docs │ ├── buildhtml │ ├── manual.html │ ├── manual.md │ ├── style.css │ └── template.html ├── example_c │ ├── Makefile │ ├── README.md │ ├── embedded_main.c │ ├── static_build.t │ └── static_call_main.c ├── examples │ ├── geometric.t │ ├── isct_predicates.t │ └── tensor_expressions.t ├── release │ └── tenslang │ │ ├── src │ │ ├── ast.t │ │ ├── builtins.t │ │ ├── c.t │ │ ├── codegen.t │ │ ├── coverage.t │ │ ├── functions.t │ │ ├── parser.t │ │ ├── pratt.t │ │ ├── shape.t │ │ ├── specializer.t │ │ ├── stats.t │ │ ├── typechecker.t │ │ └── types.t │ │ ├── tenslang.t │ │ └── tenslib.t ├── runtests └── tests │ ├── arithmetic.t │ ├── ast_unit.t │ ├── c_build.t │ ├── constants.t │ ├── control.t │ ├── functions.t │ ├── isct_predicates.t │ ├── parse_unit.t │ ├── specialize_unit.t │ ├── tensor.t │ ├── tensor_index.t │ ├── tensorarith.t │ ├── test.lua │ ├── trivial.t │ ├── type_unit.t │ └── typechecker_unit.t └── typelang ├── .gitignore ├── README.md ├── bin └── typel ├── covanalysis ├── docs ├── buildhtml ├── manual.html ├── manual.md ├── style.css └── template.html ├── example_c ├── Makefile ├── README.md ├── embedded_main.c ├── static_build.t └── static_call_main.c ├── examples └── isct_predicates.t ├── release └── typelang │ ├── src │ ├── ast.t │ ├── builtins.t │ ├── c.t │ ├── codegen.t │ ├── coverage.t │ ├── functions.t │ ├── parser.t │ ├── pratt.t │ ├── shape.t │ ├── specializer.t │ ├── stats.t │ ├── typechecker.t │ └── types.t │ ├── typelang.t │ └── typelib.t ├── runtests └── tests ├── arithmetic.t ├── ast_unit.t ├── c_build.t ├── constants.t ├── control.t ├── functions.t ├── isct_predicates.t ├── parse_unit.t ├── specialize_unit.t ├── test.lua ├── trivial.t ├── type_unit.t └── typechecker_unit.t /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | .DS_Store 3 | *.txt 4 | 5 | coverage_src 6 | 7 | -------------------------------------------------------------------------------- /plexlang/.gitignore: -------------------------------------------------------------------------------- 1 | 2 | .DS_Store 3 | *.txt 4 | 5 | coverage_src 6 | 7 | -------------------------------------------------------------------------------- /plexlang/README.md: -------------------------------------------------------------------------------- 1 | 2 | # Plexlang 3 | 4 | Plexlang is a demonstration language using the Lua/Terra compiler 5 | infrastructure. 6 | 7 | ## Quick Setup 8 | 9 | Add the [`bin/`](bin) directory to your `PATH` environment variable, so that the [`bin/plexl`](bin/plexl) script is accessible. To run a Plexlang script named `hello42.t`, just execute, 10 | ```plexl hello42.t``` 11 | Here's a listing for `hello42.t` that you can try out. It should print `42` to the console. 12 | ``` 13 | import 'plexlang.plexlang' 14 | 15 | local plexl getanswer() return 21 + 21 end 16 | 17 | print(getanswer()) 18 | ``` 19 | 20 | ## More Details 21 | 22 | See the [full manual](docs/manual.md) for more information. 23 | 24 | ## Examples 25 | 26 | See the [examples](examples) directory for example uses of Plexlang. This is a good way to get a few ideas about how to proceed once you've got some code running. 27 | 28 | ## Tests 29 | 30 | You can run the testing suite by executing 31 | ``` 32 | ./runtests 33 | ``` 34 | -------------------------------------------------------------------------------- /plexlang/bin/plexl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env terra 2 | 3 | -- print the number of and specific arguments (for debugging) 4 | --print(#arg, unpack(arg)) 5 | 6 | -- Extract options 7 | local options = {} 8 | local write_i = 1 9 | for i=1,#arg do 10 | if arg[i] == '--help' then 11 | options.help = true 12 | else 13 | arg[write_i] = arg[i] 14 | write_i = write_i + 1 15 | end 16 | end 17 | for k=write_i,#arg do arg[k] = nil end -- compaction of arg list 18 | 19 | local function printhelp() 20 | print([[ 21 | plexl [options] source_file.t 22 | OPTIONS: 23 | --help print this help message]]) 24 | end 25 | 26 | if options.help then 27 | printhelp() 28 | os.exit(0) 29 | end 30 | 31 | -- Extract script filename to run 32 | local scriptname = nil 33 | if #arg == 0 then 34 | scriptname = nil 35 | elseif #arg == 1 then 36 | scriptname = arg[1] 37 | else 38 | printhelp() 39 | print('Error: found too many arguments') 40 | print('',unpack(arg)) 41 | os.exit(1) 42 | end 43 | 44 | if not scriptname then 45 | printhelp() 46 | print('Error: expected source file to run; got none') 47 | os.exit(1) 48 | end 49 | 50 | -- get the directory of this launcher script file using a Lua trick 51 | local launchbin_path = debug.getinfo(1,"S").source 52 | -- has the form '@path_to_bin_dir/plexl', so... 53 | local bindir_path = launchbin_path:sub(2,-#'plexl' - 1) 54 | local release_path = bindir_path..'../release' 55 | -- For debug 56 | --print(launchbin_path, bindir_path, release_path) 57 | 58 | -- Now that we have the path, we can use it to extend the Terra 59 | -- searchpath to find the plexlang files 60 | package.terrapath = package.terrapath..';'..release_path..'/?.t' 61 | 62 | -- and we can launch the script safely now 63 | local blob, load_err = terralib.loadfile(scriptname) 64 | if load_err then 65 | print(load_err) 66 | os.exit(1) 67 | else 68 | blob() -- actually execute the script 69 | end 70 | 71 | 72 | 73 | -------------------------------------------------------------------------------- /plexlang/covanalysis: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env terra 2 | 3 | local ffi = require 'ffi' 4 | 5 | local SCAN_DIR = 'release/plexlang/src' 6 | local ANALYSIS_DIR = 'coverage_src' 7 | 8 | if ffi.os == 'Windows' then 9 | print('TODO windows') 10 | end 11 | local lscmd = "find "..SCAN_DIR 12 | local mkdircmd = "mkdir -p "..ANALYSIS_DIR 13 | 14 | -- load info 15 | local covinfo = loadfile("coverageinfo.lua")() 16 | 17 | -- ensure that we have somewhere to put the analyzed files 18 | os.execute(mkdircmd) 19 | 20 | local function genReport(filename, lineinfo) 21 | local srcfilename = filename 22 | local dstfilename = filename:gsub(SCAN_DIR, ANALYSIS_DIR) 23 | 24 | local srcfile = io.open(srcfilename, 'r') 25 | local dstfile = io.open(dstfilename, 'w') 26 | 27 | local linenum = 1 28 | for line in srcfile:lines() do 29 | local nvisits = lineinfo[linenum] 30 | linenum = linenum + 1 31 | 32 | -- If we visited this line, then output count 33 | if nvisits then 34 | dstfile:write(string.format("%5d: ",nvisits)) 35 | -- Filter out lines we couldn't possibly visit 36 | elseif 37 | string.match(line,"^ *%-%-") or -- comments 38 | string.match(line,"^ *$") or -- blank lines 39 | -- token only lines 40 | string.match(line,"^ *end *$") or 41 | string.match(line,"^ *} *$") or 42 | string.match(line,"^ *then *$") or 43 | string.match(line,"^ *else *$") or 44 | string.match(line,"^ *local function") or 45 | -- tokens with comments 46 | string.match(line,"^ *end *%-%-$") or 47 | string.match(line,"^ *} *%-%-") or 48 | string.match(line,"^ *then *%-%-") or 49 | string.match(line,"^ *else *%-%-") 50 | -- random other lines 51 | --string.match(line,"^ *end%) *$") 52 | then 53 | dstfile:write(" : ") 54 | else 55 | dstfile:write("#####: ") 56 | end 57 | 58 | dstfile:write(line) 59 | dstfile:write("\n") 60 | end 61 | 62 | srcfile:close() 63 | dstfile:close() 64 | end 65 | 66 | -- Scan all the source files 67 | for line in io.popen(lscmd):lines() do 68 | if ffi.os == "Windows" then error('TODO windows') end 69 | local filename = line:match("^("..SCAN_DIR.."/.*%.t)$") 70 | 71 | -- filter out some files 72 | if filename and filename:match('coverage%.t') then filename = nil end 73 | 74 | if filename then 75 | local lookupname = '@./'..filename 76 | local lineinfo = covinfo[lookupname] 77 | if lineinfo then 78 | genReport(filename, lineinfo) 79 | else 80 | print('NO COVERAGE INFO FOR '..filename) 81 | end 82 | end 83 | end 84 | 85 | 86 | --[[ 87 | local i = 1 88 | for l in terralib:lines() do 89 | local ntimes = lineinfo[i] 90 | 91 | if ntimes then 92 | io.write(string.format("%5d: ",ntimes)) 93 | else 94 | if string.match(l,"^ *%-%-") or 95 | string.match(l,"^ *$") or 96 | string.match(l,"^ *end *$") or 97 | string.match(l,"^ *else *$") or 98 | string.match(l,"^ *local function") then 99 | io.write(" : ") 100 | else 101 | io.write("#####: ") 102 | end 103 | end 104 | io.write(l) 105 | io.write("\n") 106 | i = i + 1 107 | end 108 | ]] -------------------------------------------------------------------------------- /plexlang/docs/buildhtml: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env terra 2 | 3 | local whichhandle = io.popen('which pandoc') 4 | local whichoutput = whichhandle:read("*a") 5 | whichhandle:close() 6 | local pandoc_exists = #whichoutput > 0 7 | 8 | if not pandoc_exists then 9 | print([[ 10 | Could not build html documentation because pandoc is not installed. 11 | ]]) 12 | return -- early exit this script 13 | end 14 | 15 | 16 | os.execute("pandoc manual.md -o manual.html".. 17 | " -f markdown -t html".. 18 | " --standalone --table-of-contents --toc-depth=3".. 19 | " --title-prefix='Plexlang Manual' ".. 20 | " -c style.css".. 21 | " --template=template.html" 22 | ) 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /plexlang/docs/template.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | $for(author-meta)$ 8 | 9 | $endfor$ 10 | $if(date-meta)$ 11 | 12 | $endif$ 13 | $if(title-prefix)$$title-prefix$ - $endif$$pagetitle$ 14 | 15 | $if(quotes)$ 16 | 17 | $endif$ 18 | $if(highlighting-css)$ 19 | 22 | $endif$ 23 | $for(css)$ 24 | 25 | $endfor$ 26 | $if(math)$ 27 | $math$ 28 | $endif$ 29 | $for(header-includes)$ 30 | $header-includes$ 31 | $endfor$ 32 | 33 | 34 |
35 | $for(include-before)$ 36 | $include-before$ 37 | $endfor$ 38 | $if(title)$ 39 |
40 |

$title$

41 | $if(subtitle)$ 42 |

$subtitle$

43 | $endif$ 44 | $for(author)$ 45 |

$author$

46 | $endfor$ 47 | $if(date)$ 48 |

$date$

49 | $endif$ 50 |
51 | $endif$ 52 | $if(toc)$ 53 |
54 | $toc$ 55 |
56 | $endif$ 57 | $body$ 58 | $for(include-after)$ 59 | $include-after$ 60 | $endfor$ 61 |
62 | 63 | -------------------------------------------------------------------------------- /plexlang/example_c/Makefile: -------------------------------------------------------------------------------- 1 | 2 | 3 | # Assume we're on some Linux variant by default 4 | PLATFORM:=Linux 5 | # but try to detect other situations of interest 6 | ifeq ($(OS),Windows_NT) 7 | $(error Windows unsupported) 8 | else 9 | UNAME:=$(shell uname -s) 10 | ifeq ($(UNAME),Darwin) 11 | PLATFORM:=OSX 12 | endif 13 | endif 14 | 15 | $(warning Detected platform as $(PLATFORM)) 16 | 17 | # Try to find Terra 18 | FIND_TERRA_BIN:=$(shell which terra) 19 | TERRA_ROOT:=$(patsubst %/bin/terra,%,$(FIND_TERRA_BIN)) 20 | ifeq ($(FIND_TERRA_BIN),) 21 | $(error Could not find 'terra' executable on your PATH) 22 | endif 23 | 24 | $(warning Detected Terra directory location as $(TERRA_ROOT)) 25 | 26 | 27 | # Tools to use 28 | PLEXL:=../bin/plexl 29 | 30 | 31 | # Setup the flags and options 32 | CFLAGS:= 33 | # add include directory 34 | CFLAGS:=$(CFLAGS) -I$(TERRA_ROOT)/include/ 35 | # add library directory and flags 36 | CFLAGS:=$(CFLAGS) -L$(TERRA_ROOT) -lterra 37 | # add rpath 38 | CFLAGS:=$(CFLAGS) -Wl,-rpath,$(TERRA_ROOT) 39 | 40 | ifeq ($(PLATFORM),OSX) 41 | CFLAGS:=$(CFLAGS) -pagezero_size 10000 -image_base 100000000 42 | endif 43 | 44 | $(warning CFLAGS are $(CFLAGS)) 45 | 46 | 47 | all: embedded static_call 48 | 49 | embedded: embedded_main.c 50 | $(CC) embedded_main.c -o embedded $(CFLAGS) 51 | 52 | static_func.o static_func.h: static_build.t 53 | $(PLEXL) static_build.t 54 | 55 | static_call: static_func.o static_func.h static_call_main.c 56 | $(CC) static_call_main.c static_func.o -o static_call 57 | 58 | 59 | 60 | clean: 61 | -@rm embedded 62 | -@rm static_call 63 | -@rm static_func.o 64 | -@rm static_func.h 65 | 66 | -------------------------------------------------------------------------------- /plexlang/example_c/README.md: -------------------------------------------------------------------------------- 1 | Thanks to being built on the Terra/Lua infrastructure, Plexlang comes equipped with some easy ways to be used in new or existing C/C++ programs. The examples in this directory demonstrate the two primary strategies for running DSL code from a host C program. 2 | 3 | 4 | # Embedding an interpreter for Plexlang in a C program 5 | 6 | This strategy is entirely based on Lua/Terra facilities for embedding. So it doesn't really involve Plexlang specifically. At program initialization, we build a Lua environment using 7 | 8 | ``` 9 | lua_State * L = luaL_newstate(); 10 | luaL_openlibs(L); 11 | terra_init(L); 12 | ``` 13 | 14 | (more details and context are in `embedded_main.c`) 15 | 16 | The only slight detail is that as always, the `package.terrapath` variable must be extended with the location of the plexlang [`release/`](../release) directory. For example, this can be accomplished by simply executing an appropriate string command. 17 | ``` 18 | const char *pathextend = 19 | "package.terrapath = package.terrapath..';release/?.t'"; 20 | if(terra_dostring(L, pathextend)) { 21 | fprintf(stderr, "%s\n", lua_tostring(L, -1)); 22 | lua_close(L); 23 | exit(1); 24 | } 25 | ``` 26 | 27 | To read more about how to manage the Lua/Terra interpreter from C, see the appropriate documentation, for instance the overview of the [Lua C API](http://www.lua.org/pil/24.html) and overview of the [Terra C API](http://terralang.org/api.html#c-api). 28 | 29 | 30 | 31 | # Generating Object files for a static build process 32 | 33 | This strategy is very similar to a built in Terra facility, but is part of the Plexlang API proper. Rather than embed a whole Lua/Terra interpreter into a project, we generate object files that can be statically compiled into the project. This does mean that we have to give up on using any Lua meta-programming, but may be much easier to incoporate, since there are fewer runtime dependencies involved. 34 | 35 | In this strategy, we execute a Lua/Terra Plexlang script during the build process. For instance, if we want to generate code for our hello42 function, we would use a build script like: 36 | 37 | ``` 38 | import 'plexlang.plexlang' 39 | local PL = require 'plexlang.plexlib' 40 | 41 | local plexl hello42() return 21 + 21 end 42 | 43 | -- Save out the functions 44 | PL.compiletofile("hello42.o","hello42.h",{ hello42 = hello42 }) 45 | ``` 46 | 47 | This seems very much like our existing scripts, except we end it with a call to `PL.compiletofile(...)`. This function call is passed the path for an object file to generate, path for a header file to generate (can be left `nil` to omit header file generation) and a table of Plexlang functions you want to expose. 48 | 49 | Running this script will produce a header file like the following 50 | 51 | ``` 52 | #ifndef _HELLO_42_H_ 53 | #define _HELLO_42_H_ 54 | 55 | #include "stdbool.h" 56 | #include "stdint.h" 57 | 58 | 59 | int32_t hello42(); 60 | 61 | #endif 62 | ``` 63 | 64 | Then, we can use this function from a C program like 65 | 66 | ``` 67 | #include 68 | #include "hello42.h" 69 | 70 | int main(int argc, char ** argv) { 71 | int answer = hello42(); 72 | printf("%d\n", answer); 73 | 74 | return 0; 75 | } 76 | ``` 77 | 78 | If we compile this source together with the generated `hello42.o` object code, then we'll get a program that prints `42`. 79 | 80 | In the event that more complicated types like `PL.vec3d` or `PL.mat2x3f` are used, the header file will include the necessary `struct` declarations, with data implicitly laid out in row-major order. 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | -------------------------------------------------------------------------------- /plexlang/example_c/embedded_main.c: -------------------------------------------------------------------------------- 1 | 2 | // embedded_main.c 3 | #include 4 | #include "terra/terra.h" 5 | 6 | int main(int argc, char ** argv) { 7 | lua_State * L = luaL_newstate(); //create a plain lua state 8 | luaL_openlibs(L); //initialize its libraries 9 | //initialize the terra state in lua 10 | terra_init(L); 11 | 12 | // we can execute terra scripts from within the C program now 13 | // First, let's extend the terrapath 14 | const char *pathextend = 15 | "package.terrapath = package.terrapath.." 16 | // extend the path so that we can run both in the example directory 17 | "';../release/?.t'.." 18 | // and in the directory one up which tests are run from 19 | "';release/?.t'"; 20 | if(terra_dostring(L, pathextend)) { 21 | printf("pathextend failed\n"); 22 | fprintf(stderr, "%s\n", lua_tostring(L, -1)); 23 | lua_pop(L, 1); // pop error message from the stack 24 | lua_close(L); 25 | return 1; 26 | } 27 | 28 | // then, we can go ahead and execute the hello42 program 29 | const char *scriptstring = 30 | "import 'plexlang.plexlang'\n" 31 | "\n" 32 | "plexl hello42() return 21 + 21 end\n" 33 | "\n" 34 | "print(hello42())\n" 35 | "assert(hello42() == 42)\n" 36 | "\n"; 37 | if(terra_dostring(L, scriptstring)) { 38 | printf("script failed\n"); 39 | fprintf(stderr, "%s\n", lua_tostring(L, -1)); 40 | lua_pop(L, 1); // pop error message from the stack 41 | } 42 | 43 | // finally, let's check to make sure that error reporting 44 | // is working ok 45 | const char *errscript = "error('causing an intentional error')"; 46 | if(terra_dostring(L, errscript)) { 47 | fprintf(stderr, "%s\n", lua_tostring(L, -1)); 48 | lua_pop(L, 1); // pop error message from the stack 49 | } 50 | 51 | lua_close(L); 52 | return 0; 53 | } -------------------------------------------------------------------------------- /plexlang/example_c/static_build.t: -------------------------------------------------------------------------------- 1 | 2 | import 'plexlang.plexlang' 3 | 4 | local PL = require 'plexlang.plexlib' 5 | 6 | local plexl getanswer() 7 | return 21 + 21 8 | end 9 | 10 | local plexl signedfrac( x : PL.double ) 11 | return x - PL.double(PL.int32(x)) 12 | end 13 | 14 | local plexl len2( x : PL.vec3d ) 15 | return +[i] x[i]*x[i] 16 | end 17 | 18 | local plexl diag3( x : PL.double ) 19 | return {{x,0,0},{0,x,0},{0,0,x}} 20 | end 21 | 22 | -- here, we save out the functions 23 | PL.compiletofile("static_func.o","static_func.h",{ 24 | getanswer = getanswer, 25 | signedfrac = signedfrac, 26 | len2 = len2, 27 | diag3 = diag3, 28 | }) 29 | 30 | -------------------------------------------------------------------------------- /plexlang/example_c/static_call_main.c: -------------------------------------------------------------------------------- 1 | 2 | // static_call_main.c 3 | #include 4 | #include "static_func.h" 5 | 6 | int main(int argc, char ** argv) { 7 | 8 | int answer = getanswer(); 9 | 10 | double fraca = signedfrac(3.2); 11 | double fracb = signedfrac(-2.3); 12 | 13 | tensor_double_3 vecx; 14 | vecx.d[0] = 1.0; 15 | vecx.d[1] = 2.0; 16 | vecx.d[2] = 3.0; 17 | double xlen = len2(vecx); 18 | 19 | tensor_double_3_3 diag42 = diag3(42); 20 | 21 | printf("answer should be 42; was %d\n", answer); 22 | printf("signedfrac 3.2 should yield 0.2; was %f\n", fraca); 23 | printf("signedfrac -2.3 should yield -0.3; was %f\n", fracb); 24 | printf("len2 {1.0,2.0,3.0} should yield 14.0; was %f\n", xlen); 25 | printf("diag3(42) should yield 42.0 and 0; were\n" 26 | " %f %f %f\n" 27 | " %f %f %f\n" 28 | " %f %f %f\n", 29 | diag42.d[0], diag42.d[1], diag42.d[2], 30 | diag42.d[3], diag42.d[4], diag42.d[5], 31 | diag42.d[6], diag42.d[7], diag42.d[8]); 32 | 33 | return 0; 34 | } -------------------------------------------------------------------------------- /plexlang/examples/geometric.t: -------------------------------------------------------------------------------- 1 | 2 | import 'plexlang.plexlang' 3 | local PL = require 'plexlang.plexlib' 4 | 5 | local cmath = terralib.includecstring [[#include "math.h"]] 6 | local sqrt = PL.extern('sqrt', PL.arrow(PL.double, PL.double), cmath.sqrt) 7 | 8 | local maximpl = rawget(cmath,'fmax') or terra( a:double, b:double) 9 | if a > b then return a else return b end 10 | end 11 | local max = PL.extern('max', PL.arrow({PL.double,PL.double},PL.double), 12 | maximpl) 13 | 14 | local double = PL.double 15 | local vec2d = PL.vec2d 16 | 17 | ------------------------------------------------------------------------------- 18 | 19 | -- helper functions 20 | local plexl len2d( x : vec2d ) return sqrt(+[i] x[i]*x[i]) end 21 | 22 | local EPS = 1e-5 23 | 24 | ------------------------------------------------------------------------------- 25 | 26 | -- example geometric constraints 27 | 28 | local plexl bzSample( 29 | t:double, -- this is constant 30 | p0:vec2d, p1:vec2d, p2:vec2d, p3:vec2d, 31 | samp:vec2d 32 | ) 33 | -- These can be cached because they're a function of a constant 34 | var p = {p0,p1,p2,p3} 35 | var t1 = 1.0-t 36 | var c = { t1*t1*t1, 3*t1*t1*t, 3*t1*t*t, t*t*t } 37 | 38 | var interp = +[i] :[j] c[i]*p[i,j] 39 | var diff = interp - samp 40 | 41 | return diff 42 | end 43 | 44 | -- mid must lie on the bisector line 45 | local plexl bisector( p0:vec2d, p1:vec2d, mid:vec2d ) 46 | var ebase = p1-p0 47 | var e0 = p0-mid 48 | var e1 = p1-mid 49 | 50 | var projdiff = (+[i] ebase[i] * e0[i]) + (+[i] ebase[i] * e1[i]) 51 | var lenbase = max(len2d(ebase), EPS) -- limit divisor 52 | 53 | return projdiff / lenbase 54 | end 55 | 56 | local plexl colinear( p0:vec2d, p1:vec2d, p2:vec2d ) 57 | var e01 = p1-p0 58 | var e02 = p2-p0 59 | return e01[0] * e02[1] - e01[1] * e02[0] 60 | end 61 | 62 | local plexl triheight( base0:vec2d, base1:vec2d, apex:vec2d, h:double ) 63 | var b = base1-base0 64 | var e0 = apex-base0 65 | var e1 = apex-base1 66 | 67 | var eavg = e0 + e1 68 | var bunit = (1.0/max(len2d(b), EPS)) * b 69 | 70 | var det = bunit[0]*eavg[1] - bunit[1]*eavg[0] 71 | var diff = det - h 72 | end 73 | 74 | local plexl equalpt( p0:vec2d, p1:vec2d ) 75 | return p0-p1 76 | end 77 | 78 | local plexl equalnum( a:double, b:double ) 79 | return a-b 80 | end 81 | 82 | local plexl oncircle( pt:vec2d, center:vec2d, radius:double ) 83 | var c_pt = pt-center 84 | var ptlen = max(len2d(c_pt), EPS) 85 | return ptlen - radius 86 | end 87 | 88 | 89 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /plexlang/examples/tensor_expressions.t: -------------------------------------------------------------------------------- 1 | 2 | import 'plexlang.plexlang' 3 | local PL = require 'plexlang.plexlib' 4 | 5 | local cmath = terralib.includecstring [[#include "math.h"]] 6 | 7 | local sqrt = PL.extern('sqrt', PL.arrow(PL.double, PL.double), cmath.sqrt) 8 | 9 | ------------------------------------------------------------------------------- 10 | 11 | local plexl dot2d( x:PL.vec2d, y:PL.vec2d ) 12 | return +[i] x[i] * y[i] 13 | end 14 | 15 | local plexl vec2dlen( x:PL.vec2d ) 16 | return sqrt( +[i] x[i]*x[i] ) 17 | end 18 | 19 | local plexl matvec3dmult( m:PL.mat3d, x:PL.vec3d ) 20 | return :[i] +[j] m[i,j] * x[j] 21 | end 22 | 23 | local plexl transpose2f( m:PL.mat2f ) 24 | return :[i,j] m[j,i] 25 | end 26 | 27 | local plexl qform4d( A:PL.mat4d, x:PL.vec4d ) 28 | return +[i,j] x[i] * A[i,j] * x[j] 29 | end 30 | 31 | local plexl innerprod3d( x:PL.vec3d, A:PL.mat3d, y:PL.vec3d) 32 | return +[i,j] x[i] * A[i,j] * y[j] 33 | end 34 | 35 | local plexl outerprod3i( x:PL.vec3i, y:PL.vec3i ) 36 | return :[i,j] x[i] * y[j] 37 | end 38 | 39 | local plexl matmat3dmult( A:PL.mat3d, B:PL.mat3d ) 40 | return :[i,j] +[k] A[i,k] * B[k,j] 41 | end 42 | 43 | local plexl trace2f( M:PL.mat2f ) 44 | return +[i] M[i,i] 45 | end 46 | 47 | local plexl frobenius3d( M:PL.mat3d ) 48 | return +[i,j] M[i,j] * M[i,j] 49 | end 50 | 51 | ------------------------------------------------------------------------------- 52 | 53 | 54 | 55 | ------------------------------------------------------------------------------- 56 | -------------------------------------------------------------------------------- /plexlang/release/plexlang/plexlang.t: -------------------------------------------------------------------------------- 1 | -- File to be imported, defining the language 2 | -- put import 'plexlang.plexlang' at the top of files 3 | 4 | -- shim in the coverage analysis 5 | --require 'plexlang.src.coverage' 6 | 7 | local P = require 'plexlang.src.parser' 8 | local Specializer = require 'plexlang.src.specializer' 9 | local F = require 'plexlang.src.functions' 10 | local Lib = require 'plexlang.plexlib' 11 | -- other passes and things? 12 | 13 | local function handleStatement(self, lexer) 14 | local ast, assigntuple = P.ParseStatement(lexer) 15 | local constructor = function(env_fn) 16 | if Lib._UNIT_TEST_PARSER then 17 | return ast 18 | elseif Lib._UNIT_TEST_SPECIALIZER then 19 | return Specializer.specialize(ast, env_fn()) 20 | else 21 | local decl_ast = Specializer.specialize(ast, env_fn()) 22 | return F.NewFunction { decl_ast = decl_ast } 23 | end 24 | end 25 | return constructor, assigntuple 26 | end 27 | 28 | local function handleExpression(self, lexer) 29 | local ast = P.ParseExpression(lexer) 30 | local constructor = function(env_fn) 31 | if Lib._UNIT_TEST_PARSER then 32 | return ast 33 | elseif Lib._UNIT_TEST_SPECIALIZER then 34 | return Specializer.specialize(ast, env_fn()) 35 | else 36 | local decl_ast = Specializer.specialize(ast, env_fn()) 37 | return F.NewFunction { decl_ast = decl_ast } 38 | end 39 | end 40 | return constructor 41 | end 42 | 43 | 44 | local plexlanguage = { 45 | name = 'plexlang', 46 | entrypoints = {'plexl'}, 47 | keywords = { 48 | '_', -- always good to reserve the underscore for language use 49 | 'var', 50 | }, 51 | 52 | expression = handleExpression, 53 | statement = handleStatement, 54 | localstatement = handleStatement, 55 | } 56 | 57 | return plexlanguage 58 | -------------------------------------------------------------------------------- /plexlang/release/plexlang/plexlib.t: -------------------------------------------------------------------------------- 1 | local Lib = {} 2 | package.loaded["plexlang.plexlib"] = Lib 3 | 4 | ------------------------------------------------------------------------------- 5 | -- Policy: 6 | -- This file is responsible for the Lua <--> Plexlang interface 7 | -- as exposed via the plexlib object. 8 | -- As such, it's generally preferrable to define most functions 9 | -- elsewhere in the compiler and then just export them here. Though that's 10 | -- hardly a strict policy. 11 | -- However, it's definitely a bad idea to define functions in this file 12 | -- that are only for use internally by the compiler. 13 | ------------------------------------------------------------------------------- 14 | 15 | local T = require 'plexlang.src.types' 16 | local B = require 'plexlang.src.builtins' 17 | local F = require 'plexlang.src.functions' 18 | 19 | ------------------------------------------------------------------------------- 20 | 21 | local function deep_array_copy(tbl) 22 | if type(tbl) ~= 'table' then return tbl 23 | else 24 | local cp = {} 25 | for i=1,#tbl do cp[i] = deep_array_copy(tbl[i]) end 26 | return cp 27 | end 28 | end 29 | 30 | local ConstantProto = {} 31 | ConstantProto.__index = ConstantProto 32 | 33 | function Lib.isconstant(obj) return getmetatable(obj) == ConstantProto end 34 | 35 | function Lib.Constant(typ, val) 36 | if not T.istype(typ) or not typ:isvalue() then 37 | error('first argument to Constant must be a value type', 2) 38 | end 39 | if not T.checkluaval(val, typ) then 40 | error('second argument to Constant must be a value of the given type', 2) 41 | end 42 | 43 | local c = setmetatable({ 44 | _type = typ, 45 | _value = deep_array_copy(val), 46 | }, ConstantProto) 47 | return c 48 | end 49 | 50 | function ConstantProto:get() 51 | return deep_array_copy(self._value) 52 | end 53 | 54 | function ConstantProto:gettype() 55 | return self._type 56 | end 57 | 58 | ------------------------------------------------------------------------------- 59 | 60 | -- selectively expose some parts of the type system to the user 61 | for _,name in ipairs({ 62 | "int32", 63 | "uint64", 64 | "bool", 65 | "float", 66 | "double", 67 | 68 | "tensor", 69 | "matrix", 70 | "vector", 71 | "arrow", 72 | 73 | "istype", 74 | }) do 75 | Lib[name] = T[name] 76 | end 77 | -- vec and mat shorthands 78 | for _,tchar in ipairs({ 'f', 'd', 'i', 'b' }) do 79 | for i=2,4 do 80 | local n = tostring(i) 81 | Lib['vec'..n..tchar] = T['vec'..n..tchar] 82 | Lib['mat'..n..tchar] = T['mat'..n..tchar] 83 | for j=2,4 do 84 | local m = tostring(j) 85 | Lib['mat'..n..'x'..m..tchar] = T['mat'..n..'x'..m..tchar] 86 | end 87 | end 88 | end 89 | 90 | ------------------------------------------------------------------------------- 91 | 92 | -- selectively expose some of the built-ins 93 | Lib.assert = B.assert 94 | 95 | -- generic way for clients to extend the language with external 96 | -- functions as a kind of custom built-in 97 | Lib.extern = B.extern 98 | 99 | ------------------------------------------------------------------------------- 100 | 101 | Lib.isfunction = F.isfunction 102 | Lib.compiletofile = F.compiletofile 103 | 104 | 105 | 106 | 107 | -------------------------------------------------------------------------------- /plexlang/release/plexlang/src/builtins.t: -------------------------------------------------------------------------------- 1 | local B = {} 2 | package.loaded["plexlang.src.builtins"] = B 3 | 4 | local A = require 'plexlang.src.ast' 5 | local T = require 'plexlang.src.types' 6 | 7 | local LangLib = require 'plexlang.plexlib' 8 | 9 | local C = require 'plexlang.src.c' 10 | 11 | ------------------------------------------------------------------------------- 12 | --[[ Builtin Prototype ]]-- 13 | ------------------------------------------------------------------------------- 14 | 15 | local Builtin = {} 16 | Builtin.__index = Builtin 17 | 18 | local function isbuiltin(obj) return getmetatable(obj) == Builtin end 19 | B.isbuiltin = isbuiltin 20 | 21 | -- only define builtins in this file 22 | local function NewBuiltin(name, typ, genfunc) 23 | assert(type(name) == 'string') 24 | assert(T.istype(typ) and typ:isarrow()) 25 | 26 | local bi = setmetatable({ 27 | name = name, 28 | type = typ, 29 | genfunc = genfunc, 30 | }, Builtin) 31 | 32 | return bi 33 | end 34 | 35 | ------------------------------------------------------------------------------- 36 | --[[ Generic Built-in mechanism for Terra code ]]-- 37 | ------------------------------------------------------------------------------- 38 | 39 | function B.extern(name, typesig, func) 40 | return NewBuiltin(name, typesig, function(callast, ...) 41 | local args = {...} 42 | return `func( [args] ) 43 | end) 44 | end 45 | 46 | ------------------------------------------------------------------------------- 47 | --[[ Specific Builtins ]]-- 48 | ------------------------------------------------------------------------------- 49 | 50 | local function terror_body (file, line) 51 | local prelude = file..':'..tostring(line)..': Assert Failed\n' 52 | return quote do 53 | C.fprintf(C.stderr, prelude) 54 | C.exit(1) 55 | end end 56 | end 57 | 58 | B.assert = NewBuiltin('assert', T.arrow(T.bool,{}), 59 | function(callast, test) 60 | return quote 61 | if not test then 62 | [terror_body(callast.filename, callast.linenumber)] 63 | end 64 | end 65 | end) 66 | 67 | -- type could be difficult here? 68 | --B.print = NewBuiltin('print', T.arrow()) 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | -------------------------------------------------------------------------------- /plexlang/release/plexlang/src/c.t: -------------------------------------------------------------------------------- 1 | 2 | 3 | local c_blob = terralib.includecstring [[ 4 | #include "stdlib.h" 5 | #include "stdio.h" 6 | 7 | FILE * __get__stdout() { return stdout; } 8 | FILE * __get__stdin() { return stdin; } 9 | FILE * __get__stderr() { return stderr; } 10 | 11 | ]] 12 | 13 | rawset(c_blob, 'stdout', c_blob.__get__stdout()) 14 | rawset(c_blob, 'stdin', c_blob.__get__stdin()) 15 | rawset(c_blob, 'stderr', c_blob.__get__stderr()) 16 | 17 | package.loaded["plexlang.src.c"] = c_blob 18 | return c_blob 19 | -------------------------------------------------------------------------------- /plexlang/release/plexlang/src/coverage.t: -------------------------------------------------------------------------------- 1 | 2 | if not rawget(_G,'COVERAGE_MODULE_HAS_BEEN_INSTALLED') then 3 | local ffi = require 'ffi' 4 | 5 | _G['COVERAGE_MODULE_HAS_BEEN_INSTALLED'] = true 6 | local coverageloader, err = loadfile("coverageinfo.lua") 7 | --print('FOUND COVERAGE ', coverageloader, err) 8 | 9 | local filetable = coverageloader and coverageloader() or {} 10 | local function dumplineinfo() 11 | local F = io.open("coverageinfo.lua","w") 12 | F:write("return {\n") 13 | for filename,linetable in pairs(filetable) do 14 | F:write("['"..filename.."']={\n") 15 | for linenum,count in pairs(linetable) do 16 | F:write("["..linenum.."]="..count..",\n") 17 | end 18 | F:write("},\n") 19 | end 20 | F:write("}\n") 21 | F:close() 22 | end 23 | local function debughook(event) 24 | local info = debug.getinfo(2,"Sl") 25 | -- exclude for instance, metaprogrammed lua code 26 | if string.sub(info.source, 1,1) == '@' then 27 | local linetable = filetable[info.source] 28 | if not linetable then 29 | linetable = {} 30 | filetable[info.source] = linetable 31 | end 32 | linetable[info.currentline] = (linetable[info.currentline] or 0) + 1 33 | end 34 | end 35 | debug.sethook(debughook,"l") 36 | -- make a fake ffi object that causes dumplineinfo to be called when 37 | -- the lua state is removed 38 | ffi.cdef [[ 39 | typedef struct {} __linecoverage; 40 | ]] 41 | ffi.metatype("__linecoverage", { __gc = dumplineinfo } ) 42 | _G[{}] = ffi.new("__linecoverage") 43 | end 44 | -------------------------------------------------------------------------------- /plexlang/release/plexlang/src/pratt.t: -------------------------------------------------------------------------------- 1 | 2 | local P = {} 3 | 4 | --same tokentypes as lexer, duplicated here for convience 5 | P.name = terralib.languageextension.name 6 | P.string = terralib.languageextension.string 7 | P.number = terralib.languageextension.number 8 | P.eof = terralib.languageextension.eof 9 | P.default = terralib.languageextension.default 10 | 11 | --a parser for a language is defined by a table of functions, one for each non-termina 12 | --in the language (e.g expression,statement, etc.) 13 | 14 | --these functions are either (1) raw recursive decent parsers 15 | -- or (2) Pratt parser objects 16 | 17 | --pratt parser objects behave like functions 18 | --but their behavior is defined by adding rules for what to do when 19 | --a prefix or suffix is found 20 | --futhermore, when using a pratt parser object, you can specify 21 | --a precedence such that the parser will only parse expressions 22 | --with precedence _higher_ than that. 23 | --see tests/lib/pratttest.t for examples of how to use this interface 24 | --to parse common patters 25 | 26 | P.pratt = {} 27 | function P.Pratt() 28 | return setmetatable({ 29 | infixtable = {}; 30 | prefixtable = {}; 31 | },{ __index = P.pratt, __call = P.pratt.__call }) 32 | end 33 | 34 | 35 | --define a rule for infix operators like '+' 36 | --precidence is a numeric precedence 37 | --tokentype is a lexer token type (see embeddinglanguages.html) 38 | --rule is a function: function(parser,lhs) ... end 39 | --it is given the parser object and the AST value for the lhs of the expression. 40 | --it should parse and return the AST value for current expression 41 | --P.default can be used to define a rule that fires when no other rule applies 42 | function P.pratt:infix(tokentype,prec,rule) 43 | if self.infixtable[tokentype] then 44 | error("infix rule for "..tostring(tokentype).." already defined") 45 | end 46 | self.infixtable[tokentype] = { 47 | prec = prec; 48 | rule = rule; 49 | } 50 | return self 51 | end 52 | 53 | --define a prefix rule 54 | --rule is a function: function(parser) ... end, that takes the parser object 55 | --and returns an AST for the expression 56 | 57 | function P.pratt:prefix(tokentype,rule) 58 | if self.prefixtable[tokentype] then 59 | error("prefix rule for "..tostring(tokentype).." already defined") 60 | end 61 | self.prefixtable[tokentype] = rule 62 | return self 63 | end 64 | 65 | P.defaultprefix = function(parser) 66 | parser:error("unexpected symbol") 67 | end 68 | --table-driven implementation, invoked when you call the Pratt parser object 69 | P.pratt.__call = function(pratt,parser,precortoken,fixity) 70 | local isleft = fixity == nil or fixity == "left" 71 | local limit 72 | if not precortoken then 73 | limit,isleft = 0,false 74 | elseif type(precortoken) == "number" then 75 | limit = precortoken 76 | else 77 | if not pratt.infixtable[precortoken] then 78 | error("precidence not defined for "..tostring(precortoken)) 79 | end 80 | limit = pratt.infixtable[precortoken].prec 81 | end 82 | local tt = parser:cur().type 83 | local prefixrule = pratt.prefixtable[tt] or pratt.prefixtable[P.default] or P.defaultprefix 84 | local results = { prefixrule(parser) } 85 | while true do 86 | tt = parser:cur().type 87 | local op = pratt.infixtable[tt] or pratt.infixtable[P.default] 88 | if not op or (isleft and op.prec <= limit) or (not isleft and op.prec < limit) then 89 | break 90 | end 91 | results = { op.rule(parser,unpack(results)) } 92 | end 93 | return unpack(results) 94 | end 95 | 96 | 97 | --create a parser 98 | --langtable is the table of non-terminal functions and/or pratt parser objects 99 | --lexer is the lexer object given by the language extension interface 100 | function P.Parser(langtable,lexer) 101 | local instance = {} 102 | for k,v in pairs(lexer) do 103 | if type(v) == "function" then 104 | instance[k] = function(self,...) return v(lexer,...) end 105 | elseif string.sub(k,1,1) ~= "_" then 106 | instance[k] = v 107 | end 108 | end 109 | for k,v in pairs(langtable) do 110 | if instance[k] then error("language nonterminal overlaps with lexer function "..k) end 111 | instance[k] = v 112 | end 113 | return instance 114 | end 115 | 116 | --create a parser and run a non-terminal in one go 117 | --nonterminal is the name (e.g. "expression") of the non-terminal to use as the starting point in the langtable 118 | function P.Parse(langtable,lexer,nonterminal) 119 | local self = P.Parser(langtable,lexer) 120 | return self[nonterminal](self) 121 | end 122 | 123 | 124 | return P -------------------------------------------------------------------------------- /plexlang/release/plexlang/src/stats.t: -------------------------------------------------------------------------------- 1 | 2 | local Stats = {} 3 | package.loaded["plexlang.src.stats"] = Stats 4 | 5 | 6 | -- NOTE: I'm currently measuring 1 us overhead on wrapping a simple 7 | -- timer around function launches 8 | 9 | ------------------------------------------------------------------------------ 10 | --[[ Counters ]]-- 11 | ------------------------------------------------------------------------------ 12 | 13 | local Counter = {} 14 | Counter.__index = Counter 15 | 16 | function Stats.NewCounter(name) 17 | local counter = setmetatable({ 18 | _val = 0, 19 | _name = tostring(name), 20 | }, Counter) 21 | return counter 22 | end 23 | 24 | function Counter:increment() 25 | self._val = self._val + 1 26 | end 27 | 28 | function Counter:get() 29 | return self._val 30 | end 31 | function Counter:print(prefix) 32 | prefix = (prefix or self._name) .. ': ' 33 | print(prefix, self._val) 34 | end 35 | 36 | 37 | ------------------------------------------------------------------------------ 38 | --[[ Timers ]]-- 39 | ------------------------------------------------------------------------------ 40 | 41 | local Timer = {} 42 | Timer.__index = Timer 43 | 44 | function Stats.NewTimer(name) 45 | local timer = setmetatable({ 46 | _name = tostring(name), 47 | _start = nil, 48 | 49 | _count = 0, 50 | _min_ms = math.huge, 51 | _max_ms = 0, 52 | _sum_ms = 0, 53 | }, Timer) 54 | return timer 55 | end 56 | 57 | function Timer:setName(name) 58 | self._name = tostring(name) 59 | end 60 | -- prefix should be supplied if printed 61 | function Timer.__add(lhs, rhs) 62 | if getmetatable(lhs) ~= Timer or getmetatable(rhs) ~= Timer then 63 | error('cannot add a Timer to a non-Timer') 64 | end 65 | local sumtimer = setmetatable({ 66 | _name = '', 67 | _start = nil, 68 | 69 | _count = lhs._count + rhs._count, 70 | _min_ms = math.min(lhs._min_ms, rhs._min_ms), 71 | _max_ms = math.max(lhs._max_ms, rhs._max_ms), 72 | _sum_ms = lhs._sum_ms + rhs._sum_ms, 73 | }, Timer) 74 | return sumtimer 75 | end 76 | 77 | function Timer:start(timestamp_in_ms) 78 | if not timestamp_in_ms then 79 | timestamp_in_ms = terralib.currenttimeinseconds() * 1.0e3 80 | end 81 | self._start = timestamp_in_ms 82 | return timestamp_in_ms 83 | end 84 | function Timer:stop(timestamp_in_ms) 85 | if not timestamp_in_ms then 86 | timestamp_in_ms = terralib.currenttimeinseconds() * 1.0e3 87 | end 88 | if not self._start then error('must match timer stops with starts') end 89 | local dt = timestamp_in_ms - self._start 90 | self._start = nil 91 | 92 | self._count = self._count + 1 93 | self._min_ms = math.min(self._min_ms, dt) 94 | self._max_ms = math.max(self._max_ms, dt) 95 | self._sum_ms = self._sum_ms + dt 96 | 97 | return timestamp_in_ms 98 | end 99 | 100 | -- Bulk routines for convenience 101 | function Stats.StopAndStart(stop_counters, start_counters) 102 | local timestamp_in_ms = terralib.currenttimeinseconds() * 1.0e3 103 | for _,timer in ipairs(stop_counters) do 104 | timer:stop(timestamp_in_ms) 105 | end 106 | for _,timer in ipairs(start_counters) do 107 | timer:start(timestamp_in_ms) 108 | end 109 | return timestamp_in_ms 110 | end 111 | function Stats.Start(counters) 112 | return Stats.StopAndStart({},counters) 113 | end 114 | function Stats.Stop(counters) 115 | return Stats.StopAndStart(counters,{}) 116 | end 117 | 118 | function Timer:getcount() return self._count end 119 | function Timer:getmin() return self._min_ms end 120 | function Timer:getmax() return self._max_ms end 121 | function Timer:getsum() return self._sum_ms end 122 | function Timer:getavg() return self._sum_ms / self._count end 123 | 124 | function Timer:print(prefix) 125 | prefix = (prefix or self._name) .. ':' 126 | print(prefix) 127 | print(' avg: '..tostring(self:getavg())) 128 | print(' min: '..tostring(self._min_ms)) 129 | print(' max: '..tostring(self._max_ms)) 130 | print(' sum: '..tostring(self._sum_ms)) 131 | print(' count: '..tostring(self._count)) 132 | end 133 | 134 | 135 | 136 | ------------------------------------------------------------------------------ 137 | --[[ Global Statistics ]]-- 138 | ------------------------------------------------------------------------------ 139 | 140 | local global_stat_table = {} 141 | 142 | function Stats.NewGlobalCounter(name) 143 | name = tostring(name) 144 | if global_stat_table[name] then 145 | error("stat name '"..name.."' is already being used") end 146 | local counter = Stats.NewCounter(name) 147 | global_stat_table[name] = counter 148 | return counter 149 | end 150 | 151 | function Stats.NewGlobalTimer(name) 152 | name = tostring(name) 153 | if global_stat_table[name] then 154 | error("stat name '"..name.."' is already being used") end 155 | local timer = Stats.NewTimer(name) 156 | global_stat_table[name] = timer 157 | return timer 158 | end 159 | 160 | -- get statistic 161 | function Stats.GetGlobalStat(name) 162 | local lookup = global_stat_table[name] 163 | if not lookup then error("could not find global stat '"..name.."'") end 164 | return lookup 165 | end 166 | 167 | -------------------------------------------------------------------------------- /plexlang/runtests: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env terra 2 | local ffi = require 'ffi' 3 | 4 | if ffi.os == 'Windows' then 5 | print('TODO windows') 6 | end 7 | local lscmd = "find tests" 8 | 9 | local color = { 10 | DEFAULT = '\27[39m', 11 | OKBLUE = '\27[34m', -- Blue 12 | OKGREEN = '\27[32m', -- Green 13 | WARNING = '\27[33m', -- Yellow 14 | FAIL = '\27[31m', -- Red 15 | } 16 | 17 | local passed = {} 18 | local badpassed = {} 19 | local failed = {} 20 | local disabled = {} 21 | 22 | local exclude = { 23 | ['tests/test.lua'] = true, 24 | } 25 | 26 | local disable_str = '--DISABLE-TEST' 27 | local function prefixmatch(str, prefix) 28 | return string.sub(str,1,#prefix) == prefix 29 | end 30 | local function is_disabled(filename) 31 | local handle = io.open(filename, "r") 32 | local line1 = handle:read() 33 | handle:close() 34 | 35 | local is_disabled = false 36 | if prefixmatch(line1,disable_str) then is_disabled = true end 37 | return is_disabled 38 | end 39 | 40 | 41 | 42 | print([[ 43 | ============================================================================== 44 | = Running tests... = 45 | ============================================================================]] 46 | ..'==') 47 | 48 | for line in io.popen(lscmd):lines() do 49 | if ffi.os == "Windows" then error('TODO windows') end 50 | local filename = line:match("^(tests/.*%.t)$") 51 | if filename then 52 | if is_disabled(filename) then 53 | table.insert(disabled, filename) 54 | else 55 | --print('running '..filename) 56 | local execstring = "./bin/plexl " 57 | execstring = execstring..filename 58 | 59 | print(execstring) 60 | local success = os.execute(execstring) 61 | 62 | if success ~= 0 then 63 | table.insert(failed, filename) 64 | print(filename .. " "..color.FAIL.."FAILED"..color.DEFAULT) 65 | else 66 | table.insert(passed, filename) 67 | print(filename .. " "..color.OKGREEN.."PASSED"..color.DEFAULT) 68 | end 69 | end 70 | end 71 | end 72 | print() 73 | 74 | -- test whether the coverageanalysis exists 75 | local coverage_on = os.execute('test -f coverageinfo.lua') == 0 76 | if coverage_on then 77 | print('-- Assembling Coverage Analysis Report --') 78 | os.execute('./covanalysis') 79 | os.execute('rm coverageinfo.lua') 80 | end 81 | 82 | 83 | print([[ 84 | ----------------------------- Disabled tests -----------------------------]] 85 | ..'--') 86 | for i,e in ipairs(disabled) do print(e) end 87 | print() 88 | 89 | print([[ 90 | ------------------------------ Passed tests ------------------------------]] 91 | ..'--') 92 | for i,e in ipairs(passed) do print(e) end 93 | print() 94 | 95 | print([[ 96 | ------------------------------ Failed tests ------------------------------]] 97 | ..'--') 98 | for i,e in ipairs(failed) do print(e) end 99 | print() 100 | 101 | print('tests disabled: '..tostring(#disabled)) 102 | print('tests passed: '..tostring(#passed)) 103 | if #failed + #badpassed > 0 then 104 | print(color.FAIL..'tests failed: '..tostring(#failed + #badpassed).. 105 | color.DEFAULT) 106 | else 107 | print(color.OKGREEN..'All tests passed!'..color.DEFAULT) 108 | end 109 | -------------------------------------------------------------------------------- /plexlang/tests/arithmetic.t: -------------------------------------------------------------------------------- 1 | local test = require 'tests.test' 2 | 3 | import 'plexlang.plexlang' 4 | 5 | local Lib = require 'plexlang.plexlib' 6 | 7 | ------------------------------------------------------------------------------ 8 | 9 | local plexl int32arith() 10 | var x = 42 11 | var y : Lib.int32 = x 12 | Lib.assert(x-y == 0) 13 | Lib.assert(x+y == 84) 14 | 15 | x = 3 16 | y = 5 17 | Lib.assert(x*y == 15) 18 | Lib.assert(x/y == 0) 19 | Lib.assert(y/x == 1) 20 | 21 | Lib.assert((-x) * (-y) == x * y) 22 | Lib.assert(x <= y) 23 | Lib.assert(x < y) 24 | Lib.assert(y > x) 25 | Lib.assert(y >= x) 26 | Lib.assert(x ~= y) 27 | end 28 | 29 | int32arith() 30 | 31 | local plexl uint64arith() 32 | var x : Lib.uint64 = 42 33 | var y : Lib.uint64 = x 34 | Lib.assert(x-y == 0) 35 | Lib.assert(x+y == 84) 36 | 37 | x = 3 38 | y = 5 39 | Lib.assert(x*y == 15) 40 | Lib.assert(x/y == 0) 41 | Lib.assert(y/x == 1) 42 | 43 | Lib.assert((-x) * (-y) == x * y) 44 | Lib.assert(x <= y) 45 | Lib.assert(x < y) 46 | Lib.assert(y > x) 47 | Lib.assert(y >= x) 48 | Lib.assert(x ~= y) 49 | 50 | Lib.assert(-x > 0) -- weird but true cause unsigned 51 | end 52 | 53 | uint64arith() 54 | 55 | local plexl floatarith() 56 | var x = 42.0f 57 | var y : Lib.float = x 58 | Lib.assert(x-y == 0f) 59 | Lib.assert(x+y == 84f) 60 | 61 | x = 3.0f 62 | y = 5.0f 63 | Lib.assert(x*y == 15f) 64 | Lib.assert(x/y == 3.0f/5.0f) 65 | Lib.assert(y/x == 5.0f/3.0f) 66 | 67 | Lib.assert((-x) * (-y) == x * y) 68 | Lib.assert(x <= y) 69 | Lib.assert(x < y) 70 | Lib.assert(y > x) 71 | Lib.assert(y >= x) 72 | Lib.assert(x ~= y) 73 | end 74 | 75 | floatarith() 76 | 77 | local plexl doublearith() 78 | var x = 42.0 79 | var y : Lib.double = x 80 | Lib.assert(x-y == 0) 81 | Lib.assert(x+y == 84) 82 | 83 | x = 3.0 84 | y = 5.0 85 | Lib.assert(x*y == 15) 86 | Lib.assert(x/y == 3.0/5.0) 87 | Lib.assert(y/x == 5.0/3.0) 88 | 89 | Lib.assert((-x) * (-y) == x * y) 90 | Lib.assert(x <= y) 91 | Lib.assert(x < y) 92 | Lib.assert(y > x) 93 | Lib.assert(y >= x) 94 | Lib.assert(x ~= y) 95 | end 96 | 97 | doublearith() 98 | 99 | local plexl coercearith() 100 | var x = 42 101 | var y : Lib.double = x 102 | Lib.assert(x-y == 0) 103 | Lib.assert(x+y == 84) 104 | 105 | x = 3 106 | y = 5 107 | Lib.assert(x*y == 15) 108 | Lib.assert(x/y == 3.0/5.0) 109 | Lib.assert(y/x == 5.0/3.0) 110 | 111 | Lib.assert((-x) * (-y) == x * y) 112 | Lib.assert(x <= y) 113 | Lib.assert(x < y) 114 | Lib.assert(y > x) 115 | Lib.assert(y >= x) 116 | Lib.assert(x ~= y) 117 | end 118 | 119 | coercearith() 120 | 121 | local plexl booltests() 122 | var x = true 123 | var y : Lib.bool = x 124 | Lib.assert(x and y == true) 125 | Lib.assert(x or y == true) 126 | Lib.assert(x == y) 127 | 128 | x = true 129 | y = false 130 | Lib.assert( (not x and not y) == not (x or y) ) 131 | Lib.assert(x ~= y) 132 | end 133 | 134 | booltests() 135 | 136 | --int32arith:printstats() 137 | --uint64arith:printstats() 138 | --floatarith:printstats() 139 | --doublearith:printstats() 140 | 141 | ------------------------------------------------------------------------------ 142 | 143 | ------------------------------------------------------------------------------ 144 | -------------------------------------------------------------------------------- /plexlang/tests/ast_unit.t: -------------------------------------------------------------------------------- 1 | local test = require 'tests.test' 2 | -- call to maybe install coverage 3 | require 'plexlang.plexlang' 4 | 5 | local A = require 'plexlang.src.ast' 6 | 7 | 8 | ------------------------------------ 9 | -- Cannot create abstract AST nodes 10 | test.fail(function() 11 | local exp = A.Expression:New() 12 | end, 'cannot create new abstract AST') 13 | 14 | --------------------------------------- 15 | -- Test creation of various AST nodes 16 | 17 | local somesym = A.NewSymbol('somesym') 18 | local somename = A.Name:New { value=somesym } 19 | local somenumber = A.Number:New { value=42 } 20 | local somestring = A.String:New { value='foobar' } 21 | local somebool = A.Bool:New { value=true } 22 | 23 | local someuop = A.UnaryOp:New { op='-', expr=somenumber } 24 | local somebop = A.BinaryOp:New { op='+', lhs=somenumber, rhs=somenumber } 25 | 26 | local somelist = A.List:New { exprs={somename,somename} } 27 | local somelookup = A.Lookup:New { 28 | base = somename, args = { somename, somename }, 29 | } 30 | local somecall = A.Call:New { base=somename, args={} } 31 | 32 | 33 | local someexpr = A.ExprStmt:New { expr = somebop } 34 | local someargdecl = A.ArgDecl:New { name=somesym } 35 | 36 | local somedo = A.DoStmt:New { body={someexpr} } 37 | local someasgn = A.Assignment:New { lvalues={somename}, rvalues={somebop} } 38 | local somedecl = A.DeclStmt:New { name=somesym } 39 | local somelet = A.Let:New { block={someexpr}, expr=someuop } 40 | local somefunc = A.Function:New { 41 | name='foo', 42 | args={someargdecl}, 43 | body={somedo}, 44 | rets={somebool}, 45 | } 46 | 47 | test.fail(function() 48 | local dummy = A.Function:New { args={someargdecl}, body={somedo} } 49 | end, 'Could not create AST because of bad shape') 50 | 51 | somefunc:printpretty() 52 | test.eq(somefunc:depth(), 5) 53 | test.eq(somefunc:size(), 8) 54 | 55 | A.NewCopyPass { 56 | passname = "typecheck", 57 | copymembers = {"annotation1"}, 58 | defaultvals = { 59 | node_type = 3, 60 | }, 61 | --verbose=true, 62 | } 63 | A.NewInertPass { 64 | passname='analysis', 65 | --verbose = true, 66 | } 67 | 68 | 69 | -------------------------------------------------------------------------------- /plexlang/tests/c_build.t: -------------------------------------------------------------------------------- 1 | local test = require 'tests.test' 2 | 3 | import 'plexlang.plexlang' 4 | 5 | local Lib = require 'plexlang.plexlib' 6 | 7 | ------------------------------------------------------------------------------ 8 | 9 | -- This test is more complicated because it needs to invoke a Makefile build 10 | 11 | 12 | print("** ENSURE DIRECTORY IS CLEAN") 13 | assert(os.execute('cd example_c; make clean') == 0) 14 | print("** DO BUILD") 15 | assert(os.execute('cd example_c; make') == 0) 16 | print("** BUILD COMPLETED") 17 | 18 | -- now we need to check that the build produced working executables 19 | -- with the correct output 20 | 21 | local embedded = assert(io.popen('./example_c/embedded 2>&1', 'r')) 22 | local embedded_output = embedded:read('*all') 23 | test.eq(embedded_output,[[ 24 | [string ""]:1: causing an intentional error 25 | 42 26 | ]]) 27 | -- print anyway if test didn't fail 28 | print() 29 | print(embedded_output) 30 | embedded:close() 31 | 32 | local static_call = assert(io.popen('./example_c/static_call 2>&1', 'r')) 33 | local static_output = static_call:read('*all') 34 | test.eq(static_output,[[ 35 | answer should be 42; was 42 36 | signedfrac 3.2 should yield 0.2; was 0.200000 37 | signedfrac -2.3 should yield -0.3; was -0.300000 38 | len2 {1.0,2.0,3.0} should yield 14.0; was 14.000000 39 | diag3(42) should yield 42.0 and 0; were 40 | 42.000000 0.000000 0.000000 41 | 0.000000 42.000000 0.000000 42 | 0.000000 0.000000 42.000000 43 | ]]) 44 | -- print anyway if test didn't fail 45 | print() 46 | print(static_output) 47 | static_call:close() 48 | 49 | 50 | 51 | print("** CLEANING UP") 52 | assert(os.execute('cd example_c; make clean') == 0) 53 | print("** CLEANED UP") -------------------------------------------------------------------------------- /plexlang/tests/constants.t: -------------------------------------------------------------------------------- 1 | local test = require 'tests.test' 2 | 3 | import 'plexlang.plexlang' 4 | 5 | local Lib = require 'plexlang.plexlib' 6 | 7 | ------------------------------------------------------------------------------ 8 | 9 | local vecconst = Lib.Constant(Lib.vec3d,{1,2,3}) 10 | local plexl retvec() 11 | return vecconst 12 | end 13 | 14 | test.eq(Lib.arrow({}, Lib.vec3d), retvec:gettype()) 15 | test.aeq( retvec(), {1,2,3}) 16 | 17 | local matconst = Lib.Constant(Lib.mat3d,{{1,0,0},{0,1,0},{0,0,1}}) 18 | local plexl retmatid() 19 | return matconst 20 | end 21 | 22 | test.eq(Lib.arrow({}, Lib.mat3d), retmatid:gettype()) 23 | test.rec_aeq( retmatid(), {{1,0,0},{0,1,0},{0,0,1}}) 24 | 25 | ------------------------------------------------------------------------------ 26 | 27 | local ci = Lib.Constant(Lib.int32,1) 28 | local cf = Lib.Constant(Lib.float,1) 29 | local cd = Lib.Constant(Lib.double,1) 30 | local cb = Lib.Constant(Lib.bool,false) 31 | local plexl retcs() 32 | return ci, cf, cd, cb 33 | end 34 | 35 | test.eq(Lib.arrow({},{Lib.int32, Lib.float, Lib.double, Lib.bool}), 36 | retcs:gettype()) 37 | test.aeq( {retcs()}, {1,1,1,false}) 38 | -------------------------------------------------------------------------------- /plexlang/tests/control.t: -------------------------------------------------------------------------------- 1 | local test = require 'tests.test' 2 | 3 | import 'plexlang.plexlang' 4 | 5 | local Lib = require 'plexlang.plexlib' 6 | 7 | ------------------------------------------------------------------------------ 8 | 9 | local plexl doblock() 10 | var x = 3 11 | do 12 | var x = 4 13 | x = 5 14 | end 15 | return x 16 | end 17 | 18 | test.eq(Lib.arrow({},Lib.int32), doblock:gettype()) 19 | test.eq( doblock(), 3 ) 20 | 21 | ------------------------------------------------------------------------------ 22 | 23 | local plexl decltest() 24 | var x : Lib.double 25 | x = 1 26 | return x 27 | end 28 | 29 | test.eq(Lib.arrow({},Lib.double), decltest:gettype()) 30 | test.eq( decltest(), 1 ) 31 | 32 | ------------------------------------------------------------------------------ 33 | 34 | local plexl repeatdecl() 35 | var x = 1 36 | var x = 2 37 | return x 38 | end 39 | 40 | test.eq(repeatdecl(), 2) 41 | 42 | ------------------------------------------------------------------------------ 43 | 44 | local plexl multiassgn() 45 | var x : Lib.double 46 | var y : Lib.double 47 | x,y = 1,2 48 | return y 49 | end 50 | 51 | test.eq(multiassgn(), 2) 52 | 53 | ------------------------------------------------------------------------------ 54 | -------------------------------------------------------------------------------- /plexlang/tests/functions.t: -------------------------------------------------------------------------------- 1 | local test = require 'tests.test' 2 | 3 | import 'plexlang.plexlang' 4 | 5 | local Lib = require 'plexlang.plexlib' 6 | 7 | ------------------------------------------------------------------------------ 8 | 9 | -- simple call with simple return value 10 | 11 | local plexl ret0() 12 | return 0 13 | end 14 | 15 | local plexl ret1using0() 16 | return ret0() + 1 17 | end 18 | 19 | test.eq(Lib.arrow({}, Lib.int32), ret0:gettype()) 20 | test.eq(Lib.arrow({}, Lib.int32), ret1using0:gettype()) 21 | test.eq( ret1using0(), 1) 22 | test.eq( ret0(), 0) 23 | test.eq( ret1using0(), 1) 24 | 25 | ------------------------------------------------------------------------------ 26 | 27 | local plexl transpose2d( m : Lib.mat2d ) 28 | return { { m[0,0], m[1,0] }, 29 | { m[0,1], m[1,1] } } 30 | end 31 | 32 | local plexl testtranspose() 33 | Lib.assert( transpose2d({{1,2},{3,4}}) == {{1,3},{2,4}} ) 34 | end 35 | 36 | test.eq(Lib.arrow(Lib.mat2d, Lib.mat2d), transpose2d:gettype()) 37 | test.eq(Lib.arrow({},{}), testtranspose:gettype()) 38 | testtranspose() 39 | 40 | local plexl retpair() 41 | return 1.2, 3.4 42 | end 43 | 44 | local plexl pair_to_vec() 45 | var x : Lib.vec2d 46 | x[0], x[1] = retpair() 47 | return x 48 | end 49 | 50 | test.eq(Lib.arrow({}, {Lib.double, Lib.double}), retpair:gettype()) 51 | test.eq(Lib.arrow({},Lib.vec2d), pair_to_vec:gettype()) 52 | test.aeq(pair_to_vec(), {1.2, 3.4}) 53 | 54 | ------------------------------------------------------------------------------ 55 | 56 | local plexl reti() 57 | return 10,10 58 | end 59 | 60 | local plexl castret() 61 | var x : Lib.double 62 | var y : Lib.int32 63 | x,y = reti() 64 | return x/3,y/3 65 | end 66 | 67 | test.eq(Lib.arrow({}, {Lib.int32, Lib.int32}), reti:gettype()) 68 | test.eq(Lib.arrow({},{Lib.double,Lib.int32}), castret:gettype()) 69 | test.aeq({castret()}, {10.0/3.0, 3}) 70 | 71 | 72 | 73 | -------------------------------------------------------------------------------- /plexlang/tests/isct_predicates.t: -------------------------------------------------------------------------------- 1 | require 'examples.isct_predicates' -------------------------------------------------------------------------------- /plexlang/tests/tensor.t: -------------------------------------------------------------------------------- 1 | local test = require 'tests.test' 2 | 3 | import 'plexlang.plexlang' 4 | 5 | local Lib = require 'plexlang.plexlib' 6 | 7 | ------------------------------------------------------------------------------ 8 | 9 | local plexl retvec() 10 | return { 1.0,2.0,3.0,4.0 } 11 | end 12 | 13 | test.eq(Lib.arrow({}, Lib.vec4d), retvec:gettype()) 14 | test.aeq( retvec(), {1,2,3,4}) 15 | 16 | 17 | local plexl retid3() 18 | return { {1,0,0}, {0,1,0}, {0,0,1} } 19 | end 20 | 21 | local plexl retid3d() 22 | return Lib.mat3d({ {1,0,0}, {0,1,0}, {0,0,1} }) 23 | end 24 | 25 | test.eq(Lib.arrow({}, Lib.matrix(Lib.int32, 3,3)), retid3:gettype()) 26 | test.eq(Lib.arrow({}, Lib.mat3d), retid3d:gettype()) 27 | 28 | test.rec_aeq( retid3(), {{1,0,0},{0,1,0},{0,0,1}}) 29 | test.rec_aeq( retid3d(), {{1,0,0},{0,1,0},{0,0,1}}) 30 | 31 | ------------------------------------------------------------------------------ 32 | 33 | ------------------------------------------------------------------------------ 34 | 35 | local plexl getmat_entry( m : Lib.mat2d, i:Lib.int32, j:Lib.int32 ) 36 | return m[i,j] 37 | end 38 | 39 | test.eq(Lib.arrow({Lib.mat2d, Lib.int32, Lib.int32},Lib.double), 40 | getmat_entry:gettype()) 41 | test.eq(getmat_entry({{1,2},{3,4}}, 0, 0), 1) 42 | test.eq(getmat_entry({{1,2},{3,4}}, 0, 1), 2) 43 | test.eq(getmat_entry({{1,2},{3,4}}, 1, 0), 3) 44 | test.eq(getmat_entry({{1,2},{3,4}}, 1, 1), 4) 45 | 46 | local plexl slicecol( m : Lib.mat2d, i:Lib.int32 ) 47 | return { m[0,i], m[1,i] } 48 | end 49 | 50 | test.eq(Lib.arrow({Lib.mat2d, Lib.int32},Lib.vec2d), slicecol:gettype()) 51 | test.aeq(slicecol({{1,2},{3,4}}, 0), {1,3}) 52 | test.aeq(slicecol({{1,2},{3,4}}, 1), {2,4}) 53 | 54 | 55 | ------------------------------------------------------------------------------ 56 | 57 | ------------------------------------------------------------------------------ 58 | -------------------------------------------------------------------------------- /plexlang/tests/tensor_index.t: -------------------------------------------------------------------------------- 1 | local test = require 'tests.test' 2 | 3 | import 'plexlang.plexlang' 4 | 5 | local Lib = require 'plexlang.plexlib' 6 | 7 | ------------------------------------------------------------------------------ 8 | 9 | local idmat3 = Lib.Constant(Lib.mat3d, {{1,0,0},{0,1,0},{0,0,1}}) 10 | local idmat2 = Lib.Constant(Lib.mat2d, {{1,0},{0,1}}) 11 | 12 | local plexl tarith() 13 | var x = {1.0,2.0,3.0} 14 | var y = {0.2,0.4,0.6} 15 | 16 | -- scaling 17 | var s1 = :[i] 0.2*x[i] 18 | Lib.assert(s1 == 0.2*x) 19 | 20 | -- vec vec dot 21 | var dot = +[i] x[i] * y[i] 22 | Lib.assert(dot == 0.2 + 0.8 + 1.8) 23 | 24 | -- mat vec mult 25 | var multx = :[i] +[j] idmat3[i,j] * x[j] 26 | var multy = :[i] +[j] idmat3[i,j] * y[j] 27 | Lib.assert(multx == x) 28 | Lib.assert(multy == y) 29 | 30 | var M = {{1,2,3},{4,5,6}} 31 | -- transpose 32 | var Mt = :[i,j] M[j,i] 33 | Lib.assert(Mt == {{1,4},{2,5},{3,6}}) 34 | 35 | -- inner product 36 | var p = {1,2} 37 | var inner = +[i,j] p[i] * M[i,j] * x[j] 38 | var innerT = +[i,j] x[i] * Mt[i,j] * p[j] 39 | Lib.assert(inner == innerT) 40 | Lib.assert(inner == 78) 41 | 42 | -- triple product? (can't do without shuffle) 43 | 44 | -- outer product 45 | var b1 = { 1,1} 46 | var b2 = {-1,1} 47 | var prod1 = (:[i,j] b1[i]*b1[j]) + (:[i,j] b2[i]*b2[j]) 48 | var prod2 = :[i,j] b1[i]*b1[j] + b2[i]*b2[j] 49 | Lib.assert(prod1 == prod2) 50 | Lib.assert(prod2 == 2.0*idmat2) 51 | 52 | -- mat mat mult 53 | var mmult = :[i,j] +[k] idmat2[i,k] * M[k,j] 54 | Lib.assert(mmult == M) 55 | 56 | -- trace 57 | var outerX = :[i,j] x[i]*x[j] 58 | var traceX = +[i] outerX[i,i] 59 | var normX = +[i] x[i]*x[i] 60 | Lib.assert(traceX == normX) 61 | 62 | var A = :[i,j] +[k] Mt[i,k] * M[k,j] 63 | 64 | -- matrix 2-norm 65 | var frobenius = +[i,j] A[i,j]*A[i,j] 66 | 67 | -- collapse columns 68 | var v = +[i] :[j] M[j,i] 69 | Lib.assert(v == {6,15}) 70 | 71 | -- replicate matrix in a funny way 72 | var R = :[i] :[j] A[i,j] 73 | Lib.assert(R == A) 74 | end 75 | 76 | tarith() 77 | 78 | local plexl indexintoexpr() 79 | var x = {1.0,2.0,3.0} 80 | var y = {0.2,0.4,0.6} 81 | 82 | return (x + 5*y)[1] 83 | end 84 | 85 | test.eq(indexintoexpr(), 4) 86 | 87 | 88 | -------------------------------------------------------------------------------- /plexlang/tests/tensorarith.t: -------------------------------------------------------------------------------- 1 | local test = require 'tests.test' 2 | 3 | import 'plexlang.plexlang' 4 | 5 | local Lib = require 'plexlang.plexlib' 6 | 7 | ------------------------------------------------------------------------------ 8 | 9 | local plexl darith() 10 | var id = {{1.0,0},{0,1}} 11 | Lib.assert(1.5*id == {{1.5,0},{0,1.5}}) 12 | 13 | var ones = {{1,1},{1,1}} 14 | Lib.assert( id + ones == {{2,1},{1,2}} ) 15 | 16 | Lib.assert(id/2 == {{0.5,0},{0,0.5}}) 17 | Lib.assert(-id ~= id) 18 | Lib.assert(-id == {{-1,0},{0,-1}}) 19 | 20 | var abool = {true,true,false,false} 21 | var bbool = {true,false,true,false} 22 | Lib.assert( (not abool and not bbool) == not(abool or bbool) ) 23 | Lib.assert( not not abool == abool) 24 | Lib.assert( (abool and bbool) == {true, false, false, false}) 25 | Lib.assert( (abool or bbool) == {true, true, true, false}) 26 | end 27 | 28 | darith() 29 | 30 | 31 | test.fail(function() 32 | local plexl complex_coercion() 33 | var id = {{1.0,0},{0,1}} 34 | var x : Lib.int32 = id[1,1] 35 | end 36 | end, "Could not coerce expression of type 'double' into type 'int32'") -------------------------------------------------------------------------------- /plexlang/tests/trivial.t: -------------------------------------------------------------------------------- 1 | local test = require 'tests.test' 2 | 3 | import 'plexlang.plexlang' 4 | 5 | local Lib = require 'plexlang.plexlib' 6 | 7 | ------------------------------------------------------------------------------ 8 | 9 | local plexl noop() end 10 | 11 | test.eq(Lib.arrow({},{}), noop:gettype()) 12 | test.eq(nil, noop()) 13 | 14 | ------------------------------------------------------------------------------ 15 | 16 | local plexl ret0() 17 | return 0 18 | end 19 | 20 | test.eq(Lib.arrow({},Lib.int32), ret0:gettype()) 21 | test.eq(ret0(), 0) 22 | 23 | ------------------------------------------------------------------------------ 24 | 25 | local plexl ret01() 26 | return 0, 1 27 | end 28 | 29 | test.eq(Lib.arrow({},{Lib.int32, Lib.int32}), ret01:gettype()) 30 | test.aeq( {ret01()} , {0,1} ) 31 | 32 | ------------------------------------------------------------------------------ 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /simplang/.gitignore: -------------------------------------------------------------------------------- 1 | 2 | .DS_Store 3 | *.txt 4 | 5 | coverage_src 6 | 7 | -------------------------------------------------------------------------------- /simplang/README.md: -------------------------------------------------------------------------------- 1 | 2 | # Simplang 3 | 4 | Simplang is a demonstration language using the Lua/Terra compiler 5 | infrastructure. 6 | 7 | ## Quick Setup 8 | 9 | Add the [`bin/`](bin) directory to your `PATH` environment variable, so that the [`bin/simpl`](bin/simpl) script is accessible. To run a Simplang script named `hello42.t`, just execute, 10 | ```simpl hello42.t``` 11 | Here's a listing for `hello42.t` that you can try out. It should print `42` to the console. 12 | ``` 13 | import 'simplang.simplang' 14 | 15 | local simpl getanswer() return 21 + 21 end 16 | 17 | print(getanswer()) 18 | ``` 19 | 20 | ## More Details 21 | 22 | See the [full manual](docs/manual.md) for more information. 23 | 24 | ## Examples 25 | 26 | See the [examples](examples) directory for example uses of Simplang. This is a good way to get a few ideas about how to proceed once you've got some code running. 27 | 28 | ## Tests 29 | 30 | You can run the testing suite by executing 31 | ``` 32 | ./runtests 33 | ``` 34 | -------------------------------------------------------------------------------- /simplang/bin/simpl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env terra 2 | 3 | -- print the number of and specific arguments (for debugging) 4 | --print(#arg, unpack(arg)) 5 | 6 | -- Extract options 7 | local options = {} 8 | local write_i = 1 9 | for i=1,#arg do 10 | if arg[i] == '--help' then 11 | options.help = true 12 | else 13 | arg[write_i] = arg[i] 14 | write_i = write_i + 1 15 | end 16 | end 17 | for k=write_i,#arg do arg[k] = nil end -- compaction of arg list 18 | 19 | local function printhelp() 20 | print([[ 21 | simpl [options] source_file.t 22 | OPTIONS: 23 | --help print this help message]]) 24 | end 25 | 26 | if options.help then 27 | printhelp() 28 | os.exit(0) 29 | end 30 | 31 | -- Extract script filename to run 32 | local scriptname = nil 33 | if #arg == 0 then 34 | scriptname = nil 35 | elseif #arg == 1 then 36 | scriptname = arg[1] 37 | else 38 | printhelp() 39 | print('Error: found too many arguments') 40 | print('',unpack(arg)) 41 | os.exit(1) 42 | end 43 | 44 | if not scriptname then 45 | printhelp() 46 | print('Error: expected source file to run; got none') 47 | os.exit(1) 48 | end 49 | 50 | -- get the directory of this launcher script file using a Lua trick 51 | local launchbin_path = debug.getinfo(1,"S").source 52 | -- has the form '@path_to_bin_dir/simpl', so... 53 | local bindir_path = launchbin_path:sub(2,-#'simpl' - 1) 54 | local release_path = bindir_path..'../release' 55 | -- For debug 56 | --print(launchbin_path, bindir_path, release_path) 57 | 58 | -- Now that we have the path, we can use it to extend the Terra 59 | -- searchpath to find the simplang files 60 | package.terrapath = package.terrapath..';'..release_path..'/?.t' 61 | 62 | -- and we can launch the script safely now 63 | local blob, load_err = terralib.loadfile(scriptname) 64 | if load_err then 65 | print(load_err) 66 | os.exit(1) 67 | else 68 | blob() -- actually execute the script 69 | end 70 | 71 | 72 | 73 | -------------------------------------------------------------------------------- /simplang/covanalysis: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env terra 2 | 3 | local ffi = require 'ffi' 4 | 5 | local SCAN_DIR = 'release/simplang/src' 6 | local ANALYSIS_DIR = 'coverage_src' 7 | 8 | if ffi.os == 'Windows' then 9 | print('TODO windows') 10 | end 11 | local lscmd = "find "..SCAN_DIR 12 | local mkdircmd = "mkdir -p "..ANALYSIS_DIR 13 | 14 | -- load info 15 | local covinfo = loadfile("coverageinfo.lua")() 16 | 17 | -- ensure that we have somewhere to put the analyzed files 18 | os.execute(mkdircmd) 19 | 20 | local function genReport(filename, lineinfo) 21 | local srcfilename = filename 22 | local dstfilename = filename:gsub(SCAN_DIR, ANALYSIS_DIR) 23 | 24 | local srcfile = io.open(srcfilename, 'r') 25 | local dstfile = io.open(dstfilename, 'w') 26 | 27 | local linenum = 1 28 | for line in srcfile:lines() do 29 | local nvisits = lineinfo[linenum] 30 | linenum = linenum + 1 31 | 32 | -- If we visited this line, then output count 33 | if nvisits then 34 | dstfile:write(string.format("%5d: ",nvisits)) 35 | -- Filter out lines we couldn't possibly visit 36 | elseif 37 | string.match(line,"^ *%-%-") or -- comments 38 | string.match(line,"^ *$") or -- blank lines 39 | -- token only lines 40 | string.match(line,"^ *end *$") or 41 | string.match(line,"^ *} *$") or 42 | string.match(line,"^ *then *$") or 43 | string.match(line,"^ *else *$") or 44 | string.match(line,"^ *local function") or 45 | -- tokens with comments 46 | string.match(line,"^ *end *%-%-$") or 47 | string.match(line,"^ *} *%-%-") or 48 | string.match(line,"^ *then *%-%-") or 49 | string.match(line,"^ *else *%-%-") 50 | -- random other lines 51 | --string.match(line,"^ *end%) *$") 52 | then 53 | dstfile:write(" : ") 54 | else 55 | dstfile:write("#####: ") 56 | end 57 | 58 | dstfile:write(line) 59 | dstfile:write("\n") 60 | end 61 | 62 | srcfile:close() 63 | dstfile:close() 64 | end 65 | 66 | -- Scan all the source files 67 | for line in io.popen(lscmd):lines() do 68 | if ffi.os == "Windows" then error('TODO windows') end 69 | local filename = line:match("^("..SCAN_DIR.."/.*%.t)$") 70 | 71 | -- filter out some files 72 | if filename and filename:match('coverage%.t') then filename = nil end 73 | 74 | if filename then 75 | local lookupname = '@./'..filename 76 | local lineinfo = covinfo[lookupname] 77 | if lineinfo then 78 | genReport(filename, lineinfo) 79 | else 80 | print('NO COVERAGE INFO FOR '..filename) 81 | end 82 | end 83 | end 84 | 85 | 86 | --[[ 87 | local i = 1 88 | for l in terralib:lines() do 89 | local ntimes = lineinfo[i] 90 | 91 | if ntimes then 92 | io.write(string.format("%5d: ",ntimes)) 93 | else 94 | if string.match(l,"^ *%-%-") or 95 | string.match(l,"^ *$") or 96 | string.match(l,"^ *end *$") or 97 | string.match(l,"^ *else *$") or 98 | string.match(l,"^ *local function") then 99 | io.write(" : ") 100 | else 101 | io.write("#####: ") 102 | end 103 | end 104 | io.write(l) 105 | io.write("\n") 106 | i = i + 1 107 | end 108 | ]] -------------------------------------------------------------------------------- /simplang/docs/buildhtml: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env terra 2 | 3 | local whichhandle = io.popen('which pandoc') 4 | local whichoutput = whichhandle:read("*a") 5 | whichhandle:close() 6 | local pandoc_exists = #whichoutput > 0 7 | 8 | if not pandoc_exists then 9 | print([[ 10 | Could not build html documentation because pandoc is not installed. 11 | ]]) 12 | return -- early exit this script 13 | end 14 | 15 | 16 | os.execute("pandoc manual.md -o manual.html".. 17 | " -f markdown -t html".. 18 | " --standalone --table-of-contents --toc-depth=3".. 19 | " --title-prefix='Simplang Manual' ".. 20 | " -c style.css".. 21 | " --template=template.html" 22 | ) 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /simplang/docs/template.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | $for(author-meta)$ 8 | 9 | $endfor$ 10 | $if(date-meta)$ 11 | 12 | $endif$ 13 | $if(title-prefix)$$title-prefix$ - $endif$$pagetitle$ 14 | 15 | $if(quotes)$ 16 | 17 | $endif$ 18 | $if(highlighting-css)$ 19 | 22 | $endif$ 23 | $for(css)$ 24 | 25 | $endfor$ 26 | $if(math)$ 27 | $math$ 28 | $endif$ 29 | $for(header-includes)$ 30 | $header-includes$ 31 | $endfor$ 32 | 33 | 34 |
35 | $for(include-before)$ 36 | $include-before$ 37 | $endfor$ 38 | $if(title)$ 39 |
40 |

$title$

41 | $if(subtitle)$ 42 |

$subtitle$

43 | $endif$ 44 | $for(author)$ 45 |

$author$

46 | $endfor$ 47 | $if(date)$ 48 |

$date$

49 | $endif$ 50 |
51 | $endif$ 52 | $if(toc)$ 53 |
54 | $toc$ 55 |
56 | $endif$ 57 | $body$ 58 | $for(include-after)$ 59 | $include-after$ 60 | $endfor$ 61 |
62 | 63 | -------------------------------------------------------------------------------- /simplang/example_c/Makefile: -------------------------------------------------------------------------------- 1 | 2 | 3 | # Assume we're on some Linux variant by default 4 | PLATFORM:=Linux 5 | # but try to detect other situations of interest 6 | ifeq ($(OS),Windows_NT) 7 | $(error Windows unsupported) 8 | else 9 | UNAME:=$(shell uname -s) 10 | ifeq ($(UNAME),Darwin) 11 | PLATFORM:=OSX 12 | endif 13 | endif 14 | 15 | $(warning Detected platform as $(PLATFORM)) 16 | 17 | # Try to find Terra 18 | FIND_TERRA_BIN:=$(shell which terra) 19 | TERRA_ROOT:=$(patsubst %/bin/terra,%,$(FIND_TERRA_BIN)) 20 | ifeq ($(FIND_TERRA_BIN),) 21 | $(error Could not find 'terra' executable on your PATH) 22 | endif 23 | 24 | $(warning Detected Terra directory location as $(TERRA_ROOT)) 25 | 26 | 27 | # Tools to use 28 | SIMPL:=../bin/simpl 29 | 30 | 31 | # Setup the flags and options 32 | CFLAGS:= 33 | # add include directory 34 | CFLAGS:=$(CFLAGS) -I$(TERRA_ROOT)/include/ 35 | # add library directory and flags 36 | CFLAGS:=$(CFLAGS) -L$(TERRA_ROOT) -lterra 37 | # add rpath 38 | CFLAGS:=$(CFLAGS) -Wl,-rpath,$(TERRA_ROOT) 39 | 40 | ifeq ($(PLATFORM),OSX) 41 | CFLAGS:=$(CFLAGS) -pagezero_size 10000 -image_base 100000000 42 | endif 43 | 44 | $(warning CFLAGS are $(CFLAGS)) 45 | 46 | 47 | all: embedded static_call 48 | 49 | embedded: embedded_main.c 50 | $(CC) embedded_main.c -o embedded $(CFLAGS) 51 | 52 | static_func.o static_func.h: static_build.t 53 | $(SIMPL) static_build.t 54 | 55 | static_call: static_func.o static_func.h static_call_main.c 56 | $(CC) static_call_main.c static_func.o -o static_call 57 | 58 | 59 | 60 | clean: 61 | -@rm embedded 62 | -@rm static_call 63 | -@rm static_func.o 64 | -@rm static_func.h 65 | 66 | -------------------------------------------------------------------------------- /simplang/example_c/README.md: -------------------------------------------------------------------------------- 1 | Thanks to being built on the Terra/Lua infrastructure, Simplang comes equipped with some easy ways to be used in new or existing C/C++ programs. The examples in this directory demonstrate the two primary strategies for running DSL code from a host C program. 2 | 3 | 4 | # Embedding an interpreter for Simplang in a C program 5 | 6 | This strategy is entirely based on Lua/Terra facilities for embedding. So it doesn't really involve Simplang specifically. At program initialization, we build a Lua environment using 7 | 8 | ``` 9 | lua_State * L = luaL_newstate(); 10 | luaL_openlibs(L); 11 | terra_init(L); 12 | ``` 13 | 14 | (more details and context are in `embedded_main.c`) 15 | 16 | The only slight detail is that as always, the `package.terrapath` variable must be extended with the location of the simplang [`release/`](../release) directory. For example, this can be accomplished by simply executing an appropriate string command. 17 | ``` 18 | const char *pathextend = 19 | "package.terrapath = package.terrapath..';release/?.t'"; 20 | if(terra_dostring(L, pathextend)) { 21 | fprintf(stderr, "%s\n", lua_tostring(L, -1)); 22 | lua_close(L); 23 | exit(1); 24 | } 25 | ``` 26 | 27 | To read more about how to manage the Lua/Terra interpreter from C, see the appropriate documentation, for instance the overview of the [Lua C API](http://www.lua.org/pil/24.html) and overview of the [Terra C API](http://terralang.org/api.html#c-api). 28 | 29 | 30 | 31 | # Generating Object files for a static build process 32 | 33 | This strategy is very similar to a built in Terra facility, but is part of the Simplang API proper. Rather than embed a whole Lua/Terra interpreter into a project, we generate object files that can be statically compiled into the project. This does mean that we have to give up on using any Lua meta-programming, but may be much easier to incoporate, since there are fewer runtime dependencies involved. 34 | 35 | In this strategy, we execute a Lua/Terra Simplang script during the build process. For instance, if we want to generate code for our hello42 function, we would use a build script like: 36 | 37 | ``` 38 | import 'simplang.simplang' 39 | local SL = require 'simplang.simplib' 40 | 41 | local simpl hello42() return 21 + 21 end 42 | 43 | -- Save out the functions 44 | SL.compiletofile("hello42.o","hello42.h",{ hello42 = hello42 }) 45 | ``` 46 | 47 | This seems very much like our existing scripts, except we end it with a call to `SL.compiletofile(...)`. This function call is passed the path for an object file to generate, path for a header file to generate (can be left `nil` to omit header file generation) and a table of Simplang functions you want to expose. 48 | 49 | Running this script will produce a header file like the following 50 | 51 | ``` 52 | #ifndef _HELLO_42_H_ 53 | #define _HELLO_42_H_ 54 | 55 | #include "stdbool.h" 56 | #include "stdint.h" 57 | 58 | 59 | int32_t hello42(); 60 | 61 | #endif 62 | ``` 63 | 64 | Then, we can use this function from a C program like 65 | 66 | ``` 67 | #include 68 | #include "hello42.h" 69 | 70 | int main(int argc, char ** argv) { 71 | int answer = hello42(); 72 | printf("%d\n", answer); 73 | 74 | return 0; 75 | } 76 | ``` 77 | 78 | If we compile this source together with the generated `hello42.o` object code, then we'll get a program that prints `42`. 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | -------------------------------------------------------------------------------- /simplang/example_c/embedded_main.c: -------------------------------------------------------------------------------- 1 | 2 | // embedded_main.c 3 | #include 4 | #include "terra/terra.h" 5 | 6 | int main(int argc, char ** argv) { 7 | lua_State * L = luaL_newstate(); //create a plain lua state 8 | luaL_openlibs(L); //initialize its libraries 9 | //initialize the terra state in lua 10 | terra_init(L); 11 | 12 | // we can execute terra scripts from within the C program now 13 | // First, let's extend the terrapath 14 | const char *pathextend = 15 | "package.terrapath = package.terrapath.." 16 | // extend the path so that we can run both in the example directory 17 | "';../release/?.t'.." 18 | // and in the directory one up which tests are run from 19 | "';release/?.t'"; 20 | if(terra_dostring(L, pathextend)) { 21 | printf("pathextend failed\n"); 22 | fprintf(stderr, "%s\n", lua_tostring(L, -1)); 23 | lua_pop(L, 1); // pop error message from the stack 24 | lua_close(L); 25 | return 1; 26 | } 27 | 28 | // then, we can go ahead and execute the hello42 program 29 | const char *scriptstring = 30 | "import 'simplang.simplang'\n" 31 | "\n" 32 | "simpl hello42() return 21 + 21 end\n" 33 | "\n" 34 | "print(hello42())\n" 35 | "assert(hello42() == 42)\n" 36 | "\n"; 37 | if(terra_dostring(L, scriptstring)) { 38 | printf("script failed\n"); 39 | fprintf(stderr, "%s\n", lua_tostring(L, -1)); 40 | lua_pop(L, 1); // pop error message from the stack 41 | } 42 | 43 | // finally, let's check to make sure that error reporting 44 | // is working ok 45 | const char *errscript = "error('causing an intentional error')"; 46 | if(terra_dostring(L, errscript)) { 47 | fprintf(stderr, "%s\n", lua_tostring(L, -1)); 48 | lua_pop(L, 1); // pop error message from the stack 49 | } 50 | 51 | lua_close(L); 52 | return 0; 53 | } -------------------------------------------------------------------------------- /simplang/example_c/static_build.t: -------------------------------------------------------------------------------- 1 | 2 | import 'simplang.simplang' 3 | 4 | local SL = require 'simplang.simplib' 5 | 6 | local round = SL.extern('round',SL.arrow(SL.num,SL.num), 7 | terra(x : double) : double 8 | return double(int(x)) 9 | end) 10 | 11 | local simpl getanswer() 12 | return 21 + 21 13 | end 14 | 15 | local simpl signedfrac( x : SL.num ) 16 | return x - round(x) 17 | end 18 | 19 | -- here, we save out the functions 20 | SL.compiletofile("static_func.o","static_func.h",{ 21 | getanswer = getanswer, 22 | signedfrac = signedfrac, 23 | }) 24 | 25 | -------------------------------------------------------------------------------- /simplang/example_c/static_call_main.c: -------------------------------------------------------------------------------- 1 | 2 | // static_call_main.c 3 | #include 4 | #include "static_func.h" 5 | 6 | int main(int argc, char ** argv) { 7 | 8 | int answer = getanswer(); 9 | 10 | double fraca = signedfrac(3.2); 11 | double fracb = signedfrac(-2.3); 12 | 13 | printf("answer should be 42; was %d\n", answer); 14 | printf("signedfrac 3.2 should yield 0.2; was %f\n", fraca); 15 | printf("signedfrac -2.3 should yield -0.3; was %f\n", fracb); 16 | 17 | return 0; 18 | } -------------------------------------------------------------------------------- /simplang/release/simplang/simplang.t: -------------------------------------------------------------------------------- 1 | -- File to be imported, defining the language 2 | -- put import 'simplang.simplang' at the top of files 3 | 4 | -- shim in the coverage analysis 5 | --require 'simplang.src.coverage' 6 | 7 | local P = require 'simplang.src.parser' 8 | local Specializer = require 'simplang.src.specializer' 9 | local F = require 'simplang.src.functions' 10 | local Lib = require 'simplang.simplib' 11 | -- other passes and things? 12 | 13 | local function handleStatement(self, lexer) 14 | local ast, assigntuple = P.ParseStatement(lexer) 15 | local constructor = function(env_fn) 16 | if Lib._UNIT_TEST_PARSER then 17 | return ast 18 | elseif Lib._UNIT_TEST_SPECIALIZER then 19 | return Specializer.specialize(ast, env_fn()) 20 | else 21 | local decl_ast = Specializer.specialize(ast, env_fn()) 22 | return F.NewFunction { decl_ast = decl_ast } 23 | end 24 | end 25 | return constructor, assigntuple 26 | end 27 | 28 | local function handleExpression(self, lexer) 29 | local ast = P.ParseExpression(lexer) 30 | local constructor = function(env_fn) 31 | if Lib._UNIT_TEST_PARSER then 32 | return ast 33 | elseif Lib._UNIT_TEST_SPECIALIZER then 34 | return Specializer.specialize(ast, env_fn()) 35 | else 36 | local decl_ast = Specializer.specialize(ast, env_fn()) 37 | return F.NewFunction { decl_ast = decl_ast } 38 | end 39 | end 40 | return constructor 41 | end 42 | 43 | 44 | local simplanguage = { 45 | name = 'simplang', 46 | entrypoints = {'simpl'}, 47 | keywords = { 48 | '_', -- always good to reserve the underscore for language use 49 | 'var', 50 | }, 51 | 52 | expression = handleExpression, 53 | statement = handleStatement, 54 | localstatement = handleStatement, 55 | } 56 | 57 | return simplanguage 58 | -------------------------------------------------------------------------------- /simplang/release/simplang/simplib.t: -------------------------------------------------------------------------------- 1 | local Lib = {} 2 | package.loaded["simplang.simplib"] = Lib 3 | 4 | ------------------------------------------------------------------------------- 5 | -- Policy: 6 | -- This file is responsible for the Lua <--> Simplang interface 7 | -- as exposed via the simplib object. 8 | -- As such, it's generally preferrable to define most functions 9 | -- elsewhere in the compiler and then just export them here. Though that's 10 | -- hardly a strict policy. 11 | -- However, it's definitely a bad idea to define functions in this file 12 | -- that are only for use internally by the compiler. 13 | ------------------------------------------------------------------------------- 14 | 15 | local T = require 'simplang.src.types' 16 | local B = require 'simplang.src.builtins' 17 | local F = require 'simplang.src.functions' 18 | 19 | ------------------------------------------------------------------------------- 20 | 21 | local ConstantProto = {} 22 | ConstantProto.__index = ConstantProto 23 | 24 | function Lib.isconstant(obj) return getmetatable(obj) == ConstantProto end 25 | 26 | function Lib.Constant(typ, val) 27 | if not T.istype(typ) or not typ:isvalue() then 28 | error('first argument to Constant must be a value type', 2) 29 | end 30 | if not T.checkluaval(val, typ) then 31 | error('second argument to Constant must be a value of the given type', 2) 32 | end 33 | 34 | local c = setmetatable({ 35 | _type = typ, 36 | _value = val, 37 | }, ConstantProto) 38 | return c 39 | end 40 | 41 | function ConstantProto:get() 42 | return self._value 43 | end 44 | 45 | function ConstantProto:gettype() 46 | return self._type 47 | end 48 | 49 | ------------------------------------------------------------------------------- 50 | 51 | -- selectively expose some parts of the type system to the user 52 | for _,name in ipairs({ 53 | "num", 54 | "bool", 55 | 56 | "arrow", 57 | 58 | "istype", 59 | }) do 60 | Lib[name] = T[name] 61 | end 62 | 63 | ------------------------------------------------------------------------------- 64 | 65 | -- selectively expose some of the built-ins 66 | Lib.assert = B.assert 67 | 68 | -- generic way for clients to extend the language with external 69 | -- functions as a kind of custom built-in 70 | Lib.extern = B.extern 71 | 72 | ------------------------------------------------------------------------------- 73 | 74 | Lib.isfunction = F.isfunction 75 | Lib.compiletofile = F.compiletofile 76 | 77 | 78 | 79 | 80 | -------------------------------------------------------------------------------- /simplang/release/simplang/src/builtins.t: -------------------------------------------------------------------------------- 1 | local B = {} 2 | package.loaded["simplang.src.builtins"] = B 3 | 4 | local A = require 'simplang.src.ast' 5 | local T = require 'simplang.src.types' 6 | 7 | local LangLib = require 'simplang.simplib' 8 | 9 | local C = require 'simplang.src.c' 10 | 11 | ------------------------------------------------------------------------------- 12 | --[[ Builtin Prototype ]]-- 13 | ------------------------------------------------------------------------------- 14 | 15 | local Builtin = {} 16 | Builtin.__index = Builtin 17 | 18 | local function isbuiltin(obj) return getmetatable(obj) == Builtin end 19 | B.isbuiltin = isbuiltin 20 | 21 | -- only define builtins in this file 22 | local function NewBuiltin(name, typ, genfunc) 23 | assert(type(name) == 'string') 24 | assert(T.istype(typ) and typ:isarrow()) 25 | 26 | local bi = setmetatable({ 27 | name = name, 28 | type = typ, 29 | genfunc = genfunc, 30 | }, Builtin) 31 | 32 | return bi 33 | end 34 | 35 | ------------------------------------------------------------------------------- 36 | --[[ Generic Built-in mechanism for Terra code ]]-- 37 | ------------------------------------------------------------------------------- 38 | 39 | function B.extern(name, typesig, func) 40 | return NewBuiltin(name, typesig, function(callast, ...) 41 | local args = {...} 42 | return `func( [args] ) 43 | end) 44 | end 45 | 46 | ------------------------------------------------------------------------------- 47 | --[[ Specific Builtins ]]-- 48 | ------------------------------------------------------------------------------- 49 | 50 | local function terror_body (file, line) 51 | local prelude = file..':'..tostring(line)..': Assert Failed\n' 52 | return quote do 53 | C.fprintf(C.stderr, prelude) 54 | C.exit(1) 55 | end end 56 | end 57 | 58 | B.assert = NewBuiltin('assert', T.arrow(T.bool,{}), 59 | function(callast, test) 60 | return quote 61 | if not test then 62 | [terror_body(callast.filename, callast.linenumber)] 63 | end 64 | end 65 | end) 66 | 67 | -- type could be difficult here? 68 | --B.print = NewBuiltin('print', T.arrow()) 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | -------------------------------------------------------------------------------- /simplang/release/simplang/src/c.t: -------------------------------------------------------------------------------- 1 | 2 | 3 | local c_blob = terralib.includecstring [[ 4 | #include "stdlib.h" 5 | #include "stdio.h" 6 | 7 | FILE * __get__stdout() { return stdout; } 8 | FILE * __get__stdin() { return stdin; } 9 | FILE * __get__stderr() { return stderr; } 10 | 11 | ]] 12 | 13 | rawset(c_blob, 'stdout', c_blob.__get__stdout()) 14 | rawset(c_blob, 'stdin', c_blob.__get__stdin()) 15 | rawset(c_blob, 'stderr', c_blob.__get__stderr()) 16 | 17 | package.loaded["simplang.src.c"] = c_blob 18 | return c_blob 19 | -------------------------------------------------------------------------------- /simplang/release/simplang/src/coverage.t: -------------------------------------------------------------------------------- 1 | 2 | if not rawget(_G,'COVERAGE_MODULE_HAS_BEEN_INSTALLED') then 3 | local ffi = require 'ffi' 4 | 5 | _G['COVERAGE_MODULE_HAS_BEEN_INSTALLED'] = true 6 | local coverageloader, err = loadfile("coverageinfo.lua") 7 | --print('FOUND COVERAGE ', coverageloader, err) 8 | 9 | local filetable = coverageloader and coverageloader() or {} 10 | local function dumplineinfo() 11 | local F = io.open("coverageinfo.lua","w") 12 | F:write("return {\n") 13 | for filename,linetable in pairs(filetable) do 14 | F:write("['"..filename.."']={\n") 15 | for linenum,count in pairs(linetable) do 16 | F:write("["..linenum.."]="..count..",\n") 17 | end 18 | F:write("},\n") 19 | end 20 | F:write("}\n") 21 | F:close() 22 | end 23 | local function debughook(event) 24 | local info = debug.getinfo(2,"Sl") 25 | -- exclude for instance, metaprogrammed lua code 26 | if string.sub(info.source, 1,1) == '@' then 27 | local linetable = filetable[info.source] 28 | if not linetable then 29 | linetable = {} 30 | filetable[info.source] = linetable 31 | end 32 | linetable[info.currentline] = (linetable[info.currentline] or 0) + 1 33 | end 34 | end 35 | debug.sethook(debughook,"l") 36 | -- make a fake ffi object that causes dumplineinfo to be called when 37 | -- the lua state is removed 38 | ffi.cdef [[ 39 | typedef struct {} __linecoverage; 40 | ]] 41 | ffi.metatype("__linecoverage", { __gc = dumplineinfo } ) 42 | _G[{}] = ffi.new("__linecoverage") 43 | end 44 | -------------------------------------------------------------------------------- /simplang/release/simplang/src/pratt.t: -------------------------------------------------------------------------------- 1 | 2 | local P = {} 3 | 4 | --same tokentypes as lexer, duplicated here for convience 5 | P.name = terralib.languageextension.name 6 | P.string = terralib.languageextension.string 7 | P.number = terralib.languageextension.number 8 | P.eof = terralib.languageextension.eof 9 | P.default = terralib.languageextension.default 10 | 11 | --a parser for a language is defined by a table of functions, one for each non-termina 12 | --in the language (e.g expression,statement, etc.) 13 | 14 | --these functions are either (1) raw recursive decent parsers 15 | -- or (2) Pratt parser objects 16 | 17 | --pratt parser objects behave like functions 18 | --but their behavior is defined by adding rules for what to do when 19 | --a prefix or suffix is found 20 | --futhermore, when using a pratt parser object, you can specify 21 | --a precedence such that the parser will only parse expressions 22 | --with precedence _higher_ than that. 23 | --see tests/lib/pratttest.t for examples of how to use this interface 24 | --to parse common patters 25 | 26 | P.pratt = {} 27 | function P.Pratt() 28 | return setmetatable({ 29 | infixtable = {}; 30 | prefixtable = {}; 31 | },{ __index = P.pratt, __call = P.pratt.__call }) 32 | end 33 | 34 | 35 | --define a rule for infix operators like '+' 36 | --precidence is a numeric precedence 37 | --tokentype is a lexer token type (see embeddinglanguages.html) 38 | --rule is a function: function(parser,lhs) ... end 39 | --it is given the parser object and the AST value for the lhs of the expression. 40 | --it should parse and return the AST value for current expression 41 | --P.default can be used to define a rule that fires when no other rule applies 42 | function P.pratt:infix(tokentype,prec,rule) 43 | if self.infixtable[tokentype] then 44 | error("infix rule for "..tostring(tokentype).." already defined") 45 | end 46 | self.infixtable[tokentype] = { 47 | prec = prec; 48 | rule = rule; 49 | } 50 | return self 51 | end 52 | 53 | --define a prefix rule 54 | --rule is a function: function(parser) ... end, that takes the parser object 55 | --and returns an AST for the expression 56 | 57 | function P.pratt:prefix(tokentype,rule) 58 | if self.prefixtable[tokentype] then 59 | error("prefix rule for "..tostring(tokentype).." already defined") 60 | end 61 | self.prefixtable[tokentype] = rule 62 | return self 63 | end 64 | 65 | P.defaultprefix = function(parser) 66 | parser:error("unexpected symbol") 67 | end 68 | --table-driven implementation, invoked when you call the Pratt parser object 69 | P.pratt.__call = function(pratt,parser,precortoken,fixity) 70 | local isleft = fixity == nil or fixity == "left" 71 | local limit 72 | if not precortoken then 73 | limit,isleft = 0,false 74 | elseif type(precortoken) == "number" then 75 | limit = precortoken 76 | else 77 | if not pratt.infixtable[precortoken] then 78 | error("precidence not defined for "..tostring(precortoken)) 79 | end 80 | limit = pratt.infixtable[precortoken].prec 81 | end 82 | local tt = parser:cur().type 83 | local prefixrule = pratt.prefixtable[tt] or pratt.prefixtable[P.default] or P.defaultprefix 84 | local results = { prefixrule(parser) } 85 | while true do 86 | tt = parser:cur().type 87 | local op = pratt.infixtable[tt] or pratt.infixtable[P.default] 88 | if not op or (isleft and op.prec <= limit) or (not isleft and op.prec < limit) then 89 | break 90 | end 91 | results = { op.rule(parser,unpack(results)) } 92 | end 93 | return unpack(results) 94 | end 95 | 96 | 97 | --create a parser 98 | --langtable is the table of non-terminal functions and/or pratt parser objects 99 | --lexer is the lexer object given by the language extension interface 100 | function P.Parser(langtable,lexer) 101 | local instance = {} 102 | for k,v in pairs(lexer) do 103 | if type(v) == "function" then 104 | instance[k] = function(self,...) return v(lexer,...) end 105 | elseif string.sub(k,1,1) ~= "_" then 106 | instance[k] = v 107 | end 108 | end 109 | for k,v in pairs(langtable) do 110 | if instance[k] then error("language nonterminal overlaps with lexer function "..k) end 111 | instance[k] = v 112 | end 113 | return instance 114 | end 115 | 116 | --create a parser and run a non-terminal in one go 117 | --nonterminal is the name (e.g. "expression") of the non-terminal to use as the starting point in the langtable 118 | function P.Parse(langtable,lexer,nonterminal) 119 | local self = P.Parser(langtable,lexer) 120 | return self[nonterminal](self) 121 | end 122 | 123 | 124 | return P -------------------------------------------------------------------------------- /simplang/release/simplang/src/stats.t: -------------------------------------------------------------------------------- 1 | 2 | local Stats = {} 3 | package.loaded["simplang.src.stats"] = Stats 4 | 5 | 6 | -- NOTE: I'm currently measuring 1 us overhead on wrapping a simple 7 | -- timer around function launches 8 | 9 | ------------------------------------------------------------------------------ 10 | --[[ Counters ]]-- 11 | ------------------------------------------------------------------------------ 12 | 13 | local Counter = {} 14 | Counter.__index = Counter 15 | 16 | function Stats.NewCounter(name) 17 | local counter = setmetatable({ 18 | _val = 0, 19 | _name = tostring(name), 20 | }, Counter) 21 | return counter 22 | end 23 | 24 | function Counter:increment() 25 | self._val = self._val + 1 26 | end 27 | 28 | function Counter:get() 29 | return self._val 30 | end 31 | function Counter:print(prefix) 32 | prefix = (prefix or self._name) .. ': ' 33 | print(prefix, self._val) 34 | end 35 | 36 | 37 | ------------------------------------------------------------------------------ 38 | --[[ Timers ]]-- 39 | ------------------------------------------------------------------------------ 40 | 41 | local Timer = {} 42 | Timer.__index = Timer 43 | 44 | function Stats.NewTimer(name) 45 | local timer = setmetatable({ 46 | _name = tostring(name), 47 | _start = nil, 48 | 49 | _count = 0, 50 | _min_ms = math.huge, 51 | _max_ms = 0, 52 | _sum_ms = 0, 53 | }, Timer) 54 | return timer 55 | end 56 | 57 | function Timer:setName(name) 58 | self._name = tostring(name) 59 | end 60 | -- prefix should be supplied if printed 61 | function Timer.__add(lhs, rhs) 62 | if getmetatable(lhs) ~= Timer or getmetatable(rhs) ~= Timer then 63 | error('cannot add a Timer to a non-Timer') 64 | end 65 | local sumtimer = setmetatable({ 66 | _name = '', 67 | _start = nil, 68 | 69 | _count = lhs._count + rhs._count, 70 | _min_ms = math.min(lhs._min_ms, rhs._min_ms), 71 | _max_ms = math.max(lhs._max_ms, rhs._max_ms), 72 | _sum_ms = lhs._sum_ms + rhs._sum_ms, 73 | }, Timer) 74 | return sumtimer 75 | end 76 | 77 | function Timer:start(timestamp_in_ms) 78 | if not timestamp_in_ms then 79 | timestamp_in_ms = terralib.currenttimeinseconds() * 1.0e3 80 | end 81 | self._start = timestamp_in_ms 82 | return timestamp_in_ms 83 | end 84 | function Timer:stop(timestamp_in_ms) 85 | if not timestamp_in_ms then 86 | timestamp_in_ms = terralib.currenttimeinseconds() * 1.0e3 87 | end 88 | if not self._start then error('must match timer stops with starts') end 89 | local dt = timestamp_in_ms - self._start 90 | self._start = nil 91 | 92 | self._count = self._count + 1 93 | self._min_ms = math.min(self._min_ms, dt) 94 | self._max_ms = math.max(self._max_ms, dt) 95 | self._sum_ms = self._sum_ms + dt 96 | 97 | return timestamp_in_ms 98 | end 99 | 100 | -- Bulk routines for convenience 101 | function Stats.StopAndStart(stop_counters, start_counters) 102 | local timestamp_in_ms = terralib.currenttimeinseconds() * 1.0e3 103 | for _,timer in ipairs(stop_counters) do 104 | timer:stop(timestamp_in_ms) 105 | end 106 | for _,timer in ipairs(start_counters) do 107 | timer:start(timestamp_in_ms) 108 | end 109 | return timestamp_in_ms 110 | end 111 | function Stats.Start(counters) 112 | return Stats.StopAndStart({},counters) 113 | end 114 | function Stats.Stop(counters) 115 | return Stats.StopAndStart(counters,{}) 116 | end 117 | 118 | function Timer:getcount() return self._count end 119 | function Timer:getmin() return self._min_ms end 120 | function Timer:getmax() return self._max_ms end 121 | function Timer:getsum() return self._sum_ms end 122 | function Timer:getavg() return self._sum_ms / self._count end 123 | 124 | function Timer:print(prefix) 125 | prefix = (prefix or self._name) .. ':' 126 | print(prefix) 127 | print(' avg: '..tostring(self:getavg())) 128 | print(' min: '..tostring(self._min_ms)) 129 | print(' max: '..tostring(self._max_ms)) 130 | print(' sum: '..tostring(self._sum_ms)) 131 | print(' count: '..tostring(self._count)) 132 | end 133 | 134 | 135 | 136 | ------------------------------------------------------------------------------ 137 | --[[ Global Statistics ]]-- 138 | ------------------------------------------------------------------------------ 139 | 140 | local global_stat_table = {} 141 | 142 | function Stats.NewGlobalCounter(name) 143 | name = tostring(name) 144 | if global_stat_table[name] then 145 | error("stat name '"..name.."' is already being used") end 146 | local counter = Stats.NewCounter(name) 147 | global_stat_table[name] = counter 148 | return counter 149 | end 150 | 151 | function Stats.NewGlobalTimer(name) 152 | name = tostring(name) 153 | if global_stat_table[name] then 154 | error("stat name '"..name.."' is already being used") end 155 | local timer = Stats.NewTimer(name) 156 | global_stat_table[name] = timer 157 | return timer 158 | end 159 | 160 | -- get statistic 161 | function Stats.GetGlobalStat(name) 162 | local lookup = global_stat_table[name] 163 | if not lookup then error("could not find global stat '"..name.."'") end 164 | return lookup 165 | end 166 | 167 | -------------------------------------------------------------------------------- /simplang/runtests: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env terra 2 | local ffi = require 'ffi' 3 | 4 | if ffi.os == 'Windows' then 5 | print('TODO windows') 6 | end 7 | local lscmd = "find tests" 8 | 9 | local color = { 10 | DEFAULT = '\27[39m', 11 | OKBLUE = '\27[34m', -- Blue 12 | OKGREEN = '\27[32m', -- Green 13 | WARNING = '\27[33m', -- Yellow 14 | FAIL = '\27[31m', -- Red 15 | } 16 | 17 | local passed = {} 18 | local badpassed = {} 19 | local failed = {} 20 | local disabled = {} 21 | 22 | local exclude = { 23 | ['tests/test.lua'] = true, 24 | } 25 | 26 | local disable_str = '--DISABLE-TEST' 27 | local function prefixmatch(str, prefix) 28 | return string.sub(str,1,#prefix) == prefix 29 | end 30 | local function is_disabled(filename) 31 | local handle = io.open(filename, "r") 32 | local line1 = handle:read() 33 | handle:close() 34 | 35 | local is_disabled = false 36 | if prefixmatch(line1,disable_str) then is_disabled = true end 37 | return is_disabled 38 | end 39 | 40 | 41 | 42 | print([[ 43 | ============================================================================== 44 | = Running tests... = 45 | ============================================================================]] 46 | ..'==') 47 | 48 | for line in io.popen(lscmd):lines() do 49 | if ffi.os == "Windows" then error('TODO windows') end 50 | local filename = line:match("^(tests/.*%.t)$") 51 | if filename then 52 | if is_disabled(filename) then 53 | table.insert(disabled, filename) 54 | else 55 | --print('running '..filename) 56 | local execstring = "./bin/simpl " 57 | execstring = execstring..filename 58 | 59 | print(execstring) 60 | local success = os.execute(execstring) 61 | 62 | if success ~= 0 then 63 | table.insert(failed, filename) 64 | print(filename .. " "..color.FAIL.."FAILED"..color.DEFAULT) 65 | else 66 | table.insert(passed, filename) 67 | print(filename .. " "..color.OKGREEN.."PASSED"..color.DEFAULT) 68 | end 69 | end 70 | end 71 | end 72 | print() 73 | 74 | -- test whether the coverageanalysis exists 75 | local coverage_on = os.execute('test -f coverageinfo.lua') == 0 76 | if coverage_on then 77 | print('-- Assembling Coverage Analysis Report --') 78 | os.execute('./covanalysis') 79 | os.execute('rm coverageinfo.lua') 80 | end 81 | 82 | 83 | print([[ 84 | ----------------------------- Disabled tests -----------------------------]] 85 | ..'--') 86 | for i,e in ipairs(disabled) do print(e) end 87 | print() 88 | 89 | print([[ 90 | ------------------------------ Passed tests ------------------------------]] 91 | ..'--') 92 | for i,e in ipairs(passed) do print(e) end 93 | print() 94 | 95 | print([[ 96 | ------------------------------ Failed tests ------------------------------]] 97 | ..'--') 98 | for i,e in ipairs(failed) do print(e) end 99 | print() 100 | 101 | print('tests disabled: '..tostring(#disabled)) 102 | print('tests passed: '..tostring(#passed)) 103 | if #failed + #badpassed > 0 then 104 | print(color.FAIL..'tests failed: '..tostring(#failed + #badpassed).. 105 | color.DEFAULT) 106 | else 107 | print(color.OKGREEN..'All tests passed!'..color.DEFAULT) 108 | end 109 | -------------------------------------------------------------------------------- /simplang/tests/arithmetic.t: -------------------------------------------------------------------------------- 1 | local test = require 'tests.test' 2 | 3 | import 'simplang.simplang' 4 | 5 | local Lib = require 'simplang.simplib' 6 | 7 | ------------------------------------------------------------------------------ 8 | 9 | local simpl arith() 10 | var x = 42.0 11 | var y : Lib.num = x 12 | Lib.assert(x-y == 0) 13 | Lib.assert(x+y == 84) 14 | 15 | x = 3.0 16 | y = 5.0 17 | Lib.assert(x*y == 15) 18 | Lib.assert(x/y == 3.0/5.0) 19 | Lib.assert(y/x == 5.0/3.0) 20 | 21 | Lib.assert((-x) * (-y) == x * y) 22 | Lib.assert(x <= y) 23 | Lib.assert(x < y) 24 | Lib.assert(y > x) 25 | Lib.assert(y >= x) 26 | Lib.assert(x ~= y) 27 | end 28 | 29 | arith() 30 | 31 | local simpl booltests() 32 | var x = true 33 | var y : Lib.bool = x 34 | Lib.assert(x and y == true) 35 | Lib.assert(x or y == true) 36 | Lib.assert(x == y) 37 | 38 | x = true 39 | y = false 40 | Lib.assert( (not x and not y) == not (x or y) ) 41 | Lib.assert(x ~= y) 42 | end 43 | 44 | booltests() 45 | 46 | ------------------------------------------------------------------------------ 47 | 48 | ------------------------------------------------------------------------------ 49 | -------------------------------------------------------------------------------- /simplang/tests/ast_unit.t: -------------------------------------------------------------------------------- 1 | local test = require 'tests.test' 2 | -- call to maybe install coverage 3 | require 'simplang.simplang' 4 | 5 | local A = require 'simplang.src.ast' 6 | 7 | 8 | ------------------------------------ 9 | -- Cannot create abstract AST nodes 10 | test.fail(function() 11 | local exp = A.Expression:New() 12 | end, 'cannot create new abstract AST') 13 | 14 | --------------------------------------- 15 | -- Test creation of various AST nodes 16 | 17 | local somesym = A.NewSymbol('somesym') 18 | local somename = A.Name:New { value=somesym } 19 | local somenumber = A.Number:New { value=42 } 20 | local somestring = A.String:New { value='foobar' } 21 | local somebool = A.Bool:New { value=true } 22 | 23 | local someuop = A.UnaryOp:New { op='-', expr=somenumber } 24 | local somebop = A.BinaryOp:New { op='+', lhs=somenumber, rhs=somenumber } 25 | 26 | local somelookup = A.Lookup:New { 27 | base = somename, arg = somename, 28 | } 29 | local somecall = A.Call:New { base=somename, args={} } 30 | 31 | 32 | local someexpr = A.ExprStmt:New { expr = somebop } 33 | local someargdecl = A.ArgDecl:New { name=somesym } 34 | 35 | local somedo = A.DoStmt:New { body={someexpr} } 36 | local someasgn = A.Assignment:New { lvalues={somename}, rvalues={somebop} } 37 | local somedecl = A.DeclStmt:New { name=somesym } 38 | local somelet = A.Let:New { block={someexpr}, expr=someuop } 39 | local somefunc = A.Function:New { 40 | name='foo', 41 | args={someargdecl}, 42 | body={somedo}, 43 | rets={somebool}, 44 | } 45 | 46 | test.fail(function() 47 | local dummy = A.Function:New { args={someargdecl}, body={somedo} } 48 | end, 'Could not create AST because of bad shape') 49 | 50 | somefunc:printpretty() 51 | test.eq(somefunc:depth(), 5) 52 | test.eq(somefunc:size(), 8) 53 | 54 | A.NewCopyPass { 55 | passname = "typecheck", 56 | copymembers = {"annotation1"}, 57 | defaultvals = { 58 | node_type = 3, 59 | }, 60 | --verbose=true, 61 | } 62 | A.NewInertPass { 63 | passname='analysis', 64 | --verbose = true, 65 | } 66 | 67 | 68 | -------------------------------------------------------------------------------- /simplang/tests/c_build.t: -------------------------------------------------------------------------------- 1 | local test = require 'tests.test' 2 | 3 | import 'simplang.simplang' 4 | 5 | local Lib = require 'simplang.simplib' 6 | 7 | ------------------------------------------------------------------------------ 8 | 9 | -- This test is more complicated because it needs to invoke a Makefile build 10 | 11 | 12 | print("** ENSURE DIRECTORY IS CLEAN") 13 | assert(os.execute('cd example_c; make clean') == 0) 14 | print("** DO BUILD") 15 | assert(os.execute('cd example_c; make') == 0) 16 | print("** BUILD COMPLETED") 17 | 18 | -- now we need to check that the build produced working executables 19 | -- with the correct output 20 | 21 | local embedded = assert(io.popen('./example_c/embedded 2>&1', 'r')) 22 | local embedded_output = embedded:read('*all') 23 | test.eq(embedded_output,[[ 24 | [string ""]:1: causing an intentional error 25 | 42 26 | ]]) 27 | -- print anyway if test didn't fail 28 | print() 29 | print(embedded_output) 30 | embedded:close() 31 | 32 | local static_call = assert(io.popen('./example_c/static_call 2>&1', 'r')) 33 | local static_output = static_call:read('*all') 34 | test.eq(static_output,[[ 35 | answer should be 42; was 42 36 | signedfrac 3.2 should yield 0.2; was 0.200000 37 | signedfrac -2.3 should yield -0.3; was -0.300000 38 | ]]) 39 | -- print anyway if test didn't fail 40 | print() 41 | print(static_output) 42 | static_call:close() 43 | 44 | 45 | 46 | print("** CLEANING UP") 47 | assert(os.execute('cd example_c; make clean') == 0) 48 | print("** CLEANED UP") -------------------------------------------------------------------------------- /simplang/tests/constants.t: -------------------------------------------------------------------------------- 1 | local test = require 'tests.test' 2 | 3 | import 'simplang.simplang' 4 | 5 | local Lib = require 'simplang.simplib' 6 | 7 | ------------------------------------------------------------------------------ 8 | 9 | local cn = Lib.Constant(Lib.num,1) 10 | local cb = Lib.Constant(Lib.bool,false) 11 | local simpl retcs() 12 | return cn, cb 13 | end 14 | 15 | test.eq(Lib.arrow({},{Lib.num, Lib.bool}), 16 | retcs:gettype()) 17 | test.aeq( {retcs()}, {1,false}) 18 | -------------------------------------------------------------------------------- /simplang/tests/control.t: -------------------------------------------------------------------------------- 1 | local test = require 'tests.test' 2 | 3 | import 'simplang.simplang' 4 | 5 | local Lib = require 'simplang.simplib' 6 | 7 | ------------------------------------------------------------------------------ 8 | 9 | local simpl doblock() 10 | var x = 3 11 | do 12 | var x = 4 13 | x = 5 14 | end 15 | return x 16 | end 17 | 18 | test.eq(Lib.arrow({},Lib.num), doblock:gettype()) 19 | test.eq( doblock(), 3 ) 20 | 21 | ------------------------------------------------------------------------------ 22 | 23 | local simpl decltest() 24 | var x : Lib.num 25 | x = 1 26 | return x 27 | end 28 | 29 | test.eq(Lib.arrow({},Lib.num), decltest:gettype()) 30 | test.eq( decltest(), 1 ) 31 | 32 | ------------------------------------------------------------------------------ 33 | 34 | local simpl repeatdecl() 35 | var x = 1 36 | var x = 2 37 | return x 38 | end 39 | 40 | test.eq(repeatdecl(), 2) 41 | 42 | ------------------------------------------------------------------------------ 43 | 44 | local simpl multiassgn() 45 | var x : Lib.num 46 | var y : Lib.num 47 | x,y = 1,2 48 | return y 49 | end 50 | 51 | test.eq(multiassgn(), 2) 52 | 53 | ------------------------------------------------------------------------------ 54 | -------------------------------------------------------------------------------- /simplang/tests/functions.t: -------------------------------------------------------------------------------- 1 | local test = require 'tests.test' 2 | 3 | import 'simplang.simplang' 4 | 5 | local Lib = require 'simplang.simplib' 6 | 7 | ------------------------------------------------------------------------------ 8 | 9 | -- simple call with simple return value 10 | 11 | local simpl ret0() 12 | return 0 13 | end 14 | 15 | local simpl ret1using0() 16 | return ret0() + 1 17 | end 18 | 19 | test.eq(Lib.arrow({}, Lib.num), ret0:gettype()) 20 | test.eq(Lib.arrow({}, Lib.num), ret1using0:gettype()) 21 | test.eq( ret1using0(), 1) 22 | test.eq( ret0(), 0) 23 | test.eq( ret1using0(), 1) 24 | 25 | ------------------------------------------------------------------------------ 26 | 27 | local simpl reti() 28 | return 10,10 29 | end 30 | 31 | local simpl castret() 32 | var x : Lib.num 33 | var y : Lib.num 34 | x,y = reti() 35 | return x/3,y/3 36 | end 37 | 38 | test.eq(Lib.arrow({}, {Lib.num, Lib.num}), reti:gettype()) 39 | test.eq(Lib.arrow({},{Lib.num,Lib.num}), castret:gettype()) 40 | test.aeq({castret()}, {10.0/3.0, 10.0/3.0}) 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /simplang/tests/isct_predicates.t: -------------------------------------------------------------------------------- 1 | require 'examples.isct_predicates' -------------------------------------------------------------------------------- /simplang/tests/trivial.t: -------------------------------------------------------------------------------- 1 | local test = require 'tests.test' 2 | 3 | import 'simplang.simplang' 4 | 5 | local Lib = require 'simplang.simplib' 6 | 7 | ------------------------------------------------------------------------------ 8 | 9 | local simpl noop() end 10 | 11 | test.eq(Lib.arrow({},{}), noop:gettype()) 12 | test.eq(nil, noop()) 13 | 14 | ------------------------------------------------------------------------------ 15 | 16 | local simpl ret0() 17 | return 0 18 | end 19 | 20 | test.eq(Lib.arrow({},Lib.num), ret0:gettype()) 21 | test.eq(ret0(), 0) 22 | 23 | ------------------------------------------------------------------------------ 24 | 25 | local simpl ret01() 26 | return 0, 1 27 | end 28 | 29 | test.eq(Lib.arrow({},{Lib.num, Lib.num}), ret01:gettype()) 30 | test.aeq( {ret01()} , {0,1} ) 31 | 32 | ------------------------------------------------------------------------------ 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /simplang/tests/type_unit.t: -------------------------------------------------------------------------------- 1 | local test = require 'tests.test' 2 | -- call to maybe install coverage analysis 3 | require 'simplang.simplang' 4 | 5 | local T = require 'simplang.src.types' 6 | 7 | 8 | --------------------------------------- 9 | -- Test creation of various AST nodes 10 | 11 | -- dummy test 12 | test.eq(T.istype('foo'), false) 13 | 14 | -- test that the primitive types are present 15 | test.eq(T.istype(T.bool), true) 16 | test.eq(T.istype(T.num), true) 17 | 18 | local prims = { 19 | ['bool'] = bool, 20 | ['num' ] = double, 21 | } 22 | 23 | for name,terra_type in pairs(prims) do 24 | -- test that type is there 25 | local typ = T[name] 26 | test.eq(T.istype(typ), true) 27 | 28 | -- properties of type 29 | test.eq(typ:isprimitive(), true) 30 | test.eq(typ:isvalue(), true) 31 | 32 | -- check various sub-properties 33 | if name == 'bool' then 34 | test.eq(typ:islogical(), true) 35 | test.eq(typ:isnumeric(), false) 36 | else 37 | test.eq(typ:islogical(), false) 38 | test.eq(typ:isnumeric(), true) 39 | end 40 | 41 | -- check simple derivations 42 | test.eq(typ:terratype(), terra_type) 43 | test.eq(tostring(typ), name) 44 | 45 | end 46 | 47 | 48 | -- smattering of string tests 49 | test.eq(tostring(T.error), 'error') 50 | test.eq(tostring(T.internal('foo')), 'internal(foo)') 51 | 52 | 53 | -- weird tests for error and internal 54 | test.eq(T.error:terratype(), T.internal({}):terratype()) -- both {} 55 | 56 | 57 | -- lua <--> terra conversions 58 | test.eq(T.terratoluaval(T.luatoterraval(42, T.num), T.num), 42) 59 | test.eq(T.terratoluaval(T.luatoterraval(true, T.bool), T.bool), true) 60 | test.eq(T.terratoluaval(T.luatoterraval(false, T.bool), T.bool), false) 61 | -- weird edge case for booleans... 62 | local btval = terralib.new(bool, true) 63 | local bfval = terralib.new(bool, false) 64 | test.eq(T.terratoluaval(btval, T.bool), true) 65 | test.eq(T.terratoluaval(bfval, T.bool), false) 66 | 67 | test.eq(T.checkluaval(1, T.bool), false) 68 | test.eq(T.checkluaval(true, T.num), false) 69 | test.eq(T.checkluaval(true, T.error), false) 70 | 71 | 72 | 73 | -- Arrows 74 | local voidarrow = T.arrow({},{}) 75 | test.eq(voidarrow, T.arrow({},{})) 76 | test.eq(voidarrow, T.arrow({},{})) -- double check cause of caching 77 | test.eq(T.istype(voidarrow), true) 78 | test.eq(voidarrow:isarrow(), true) 79 | test.eq(voidarrow:isvalue(), false) 80 | test.eq(tostring(voidarrow), "{}->{}") 81 | test.aeq(voidarrow:argtypes(),{}) 82 | test.aeq(voidarrow:rettypes(),{}) 83 | 84 | local itoi = T.arrow({T.num},{T.num}) 85 | test.eq(itoi,T.arrow({T.num},{T.num})) 86 | test.eq(T.istype(itoi), true) 87 | test.eq(itoi:isarrow(), true) 88 | test.eq(itoi:isvalue(), false) 89 | test.eq(tostring(itoi), "{num}->{num}") 90 | test.aeq(itoi:argtypes(),{T.num}) 91 | test.aeq(itoi:rettypes(),{T.num}) 92 | 93 | local retpairs = T.arrow({},{T.num,T.num}) 94 | test.eq(T.istype(retpairs), true) 95 | test.eq(retpairs:isarrow(), true) 96 | test.eq(retpairs:isvalue(), false) 97 | test.eq(tostring(retpairs), "{}->{num,num}") 98 | test.aeq(retpairs:argtypes(),{}) 99 | test.aeq(retpairs:rettypes(),{T.num,T.num}) 100 | 101 | local argtriple = T.arrow({T.num,T.bool,T.num},{}) 102 | test.eq(T.istype(argtriple), true) 103 | test.eq(argtriple:isarrow(), true) 104 | test.eq(argtriple:isvalue(), false) 105 | test.eq(tostring(argtriple), "{num,bool,num}->{}") 106 | test.aeq(argtriple:argtypes(),{T.num,T.bool,T.num}) 107 | test.aeq(argtriple:rettypes(),{}) 108 | 109 | 110 | test.fail(function() 111 | T.arrow(24, T.num) 112 | end, 'invalid arg; expecting 2 types or lists') 113 | 114 | test.fail(function() 115 | T.arrow({1}, {}) 116 | end, 'invalid type at tuple position 1') 117 | 118 | test.fail(function() 119 | T.arrow({T.num,1}, {}) 120 | end, 'invalid type at tuple position 2') 121 | 122 | test.fail(function() 123 | T.arrow({}, {T.error}) 124 | end, 'invalid type at tuple position 1') 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | -------------------------------------------------------------------------------- /tenslang/.gitignore: -------------------------------------------------------------------------------- 1 | 2 | .DS_Store 3 | *.txt 4 | 5 | coverage_src 6 | 7 | -------------------------------------------------------------------------------- /tenslang/README.md: -------------------------------------------------------------------------------- 1 | 2 | # Tenslang 3 | 4 | Tenslang is a demonstration language using the Lua/Terra compiler 5 | infrastructure. 6 | 7 | ## Quick Setup 8 | 9 | Add the [`bin/`](bin) directory to your `PATH` environment variable, so that the [`bin/tensl`](bin/tensl) script is accessible. To run a Tenslang script named `hello42.t`, just execute, 10 | ```tensl hello42.t``` 11 | Here's a listing for `hello42.t` that you can try out. It should print `42` to the console. 12 | ``` 13 | import 'tenslang.tenslang' 14 | 15 | local tensl getanswer() return 21 + 21 end 16 | 17 | print(getanswer()) 18 | ``` 19 | 20 | ## More Details 21 | 22 | See the [full manual](docs/manual.md) for more information. 23 | 24 | ## Examples 25 | 26 | See the [examples](examples) directory for example uses of Tenslang. This is a good way to get a few ideas about how to proceed once you've got some code running. 27 | 28 | ## Tests 29 | 30 | You can run the testing suite by executing 31 | ``` 32 | ./runtests 33 | ``` 34 | -------------------------------------------------------------------------------- /tenslang/bin/tensl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env terra 2 | 3 | -- print the number of and specific arguments (for debugging) 4 | --print(#arg, unpack(arg)) 5 | 6 | -- Extract options 7 | local options = {} 8 | local write_i = 1 9 | for i=1,#arg do 10 | if arg[i] == '--help' then 11 | options.help = true 12 | else 13 | arg[write_i] = arg[i] 14 | write_i = write_i + 1 15 | end 16 | end 17 | for k=write_i,#arg do arg[k] = nil end -- compaction of arg list 18 | 19 | local function printhelp() 20 | print([[ 21 | tensl [options] source_file.t 22 | OPTIONS: 23 | --help print this help message]]) 24 | end 25 | 26 | if options.help then 27 | printhelp() 28 | os.exit(0) 29 | end 30 | 31 | -- Extract script filename to run 32 | local scriptname = nil 33 | if #arg == 0 then 34 | scriptname = nil 35 | elseif #arg == 1 then 36 | scriptname = arg[1] 37 | else 38 | printhelp() 39 | print('Error: found too many arguments') 40 | print('',unpack(arg)) 41 | os.exit(1) 42 | end 43 | 44 | if not scriptname then 45 | printhelp() 46 | print('Error: expected source file to run; got none') 47 | os.exit(1) 48 | end 49 | 50 | -- get the directory of this launcher script file using a Lua trick 51 | local launchbin_path = debug.getinfo(1,"S").source 52 | -- has the form '@path_to_bin_dir/tensl', so... 53 | local bindir_path = launchbin_path:sub(2,-#'tensl' - 1) 54 | local release_path = bindir_path..'../release' 55 | -- For debug 56 | --print(launchbin_path, bindir_path, release_path) 57 | 58 | -- Now that we have the path, we can use it to extend the Terra 59 | -- searchpath to find the tenslang files 60 | package.terrapath = package.terrapath..';'..release_path..'/?.t' 61 | 62 | -- and we can launch the script safely now 63 | local blob, load_err = terralib.loadfile(scriptname) 64 | if load_err then 65 | print(load_err) 66 | os.exit(1) 67 | else 68 | blob() -- actually execute the script 69 | end 70 | 71 | 72 | 73 | -------------------------------------------------------------------------------- /tenslang/covanalysis: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env terra 2 | 3 | local ffi = require 'ffi' 4 | 5 | local SCAN_DIR = 'release/tenslang/src' 6 | local ANALYSIS_DIR = 'coverage_src' 7 | 8 | if ffi.os == 'Windows' then 9 | print('TODO windows') 10 | end 11 | local lscmd = "find "..SCAN_DIR 12 | local mkdircmd = "mkdir -p "..ANALYSIS_DIR 13 | 14 | -- load info 15 | local covinfo = loadfile("coverageinfo.lua")() 16 | 17 | -- ensure that we have somewhere to put the analyzed files 18 | os.execute(mkdircmd) 19 | 20 | local function genReport(filename, lineinfo) 21 | local srcfilename = filename 22 | local dstfilename = filename:gsub(SCAN_DIR, ANALYSIS_DIR) 23 | 24 | local srcfile = io.open(srcfilename, 'r') 25 | local dstfile = io.open(dstfilename, 'w') 26 | 27 | local linenum = 1 28 | for line in srcfile:lines() do 29 | local nvisits = lineinfo[linenum] 30 | linenum = linenum + 1 31 | 32 | -- If we visited this line, then output count 33 | if nvisits then 34 | dstfile:write(string.format("%5d: ",nvisits)) 35 | -- Filter out lines we couldn't possibly visit 36 | elseif 37 | string.match(line,"^ *%-%-") or -- comments 38 | string.match(line,"^ *$") or -- blank lines 39 | -- token only lines 40 | string.match(line,"^ *end *$") or 41 | string.match(line,"^ *} *$") or 42 | string.match(line,"^ *then *$") or 43 | string.match(line,"^ *else *$") or 44 | string.match(line,"^ *local function") or 45 | -- tokens with comments 46 | string.match(line,"^ *end *%-%-$") or 47 | string.match(line,"^ *} *%-%-") or 48 | string.match(line,"^ *then *%-%-") or 49 | string.match(line,"^ *else *%-%-") 50 | -- random other lines 51 | --string.match(line,"^ *end%) *$") 52 | then 53 | dstfile:write(" : ") 54 | else 55 | dstfile:write("#####: ") 56 | end 57 | 58 | dstfile:write(line) 59 | dstfile:write("\n") 60 | end 61 | 62 | srcfile:close() 63 | dstfile:close() 64 | end 65 | 66 | -- Scan all the source files 67 | for line in io.popen(lscmd):lines() do 68 | if ffi.os == "Windows" then error('TODO windows') end 69 | local filename = line:match("^("..SCAN_DIR.."/.*%.t)$") 70 | 71 | -- filter out some files 72 | if filename and filename:match('coverage%.t') then filename = nil end 73 | 74 | if filename then 75 | local lookupname = '@./'..filename 76 | local lineinfo = covinfo[lookupname] 77 | if lineinfo then 78 | genReport(filename, lineinfo) 79 | else 80 | print('NO COVERAGE INFO FOR '..filename) 81 | end 82 | end 83 | end 84 | 85 | 86 | --[[ 87 | local i = 1 88 | for l in terralib:lines() do 89 | local ntimes = lineinfo[i] 90 | 91 | if ntimes then 92 | io.write(string.format("%5d: ",ntimes)) 93 | else 94 | if string.match(l,"^ *%-%-") or 95 | string.match(l,"^ *$") or 96 | string.match(l,"^ *end *$") or 97 | string.match(l,"^ *else *$") or 98 | string.match(l,"^ *local function") then 99 | io.write(" : ") 100 | else 101 | io.write("#####: ") 102 | end 103 | end 104 | io.write(l) 105 | io.write("\n") 106 | i = i + 1 107 | end 108 | ]] -------------------------------------------------------------------------------- /tenslang/docs/buildhtml: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env terra 2 | 3 | local whichhandle = io.popen('which pandoc') 4 | local whichoutput = whichhandle:read("*a") 5 | whichhandle:close() 6 | local pandoc_exists = #whichoutput > 0 7 | 8 | if not pandoc_exists then 9 | print([[ 10 | Could not build html documentation because pandoc is not installed. 11 | ]]) 12 | return -- early exit this script 13 | end 14 | 15 | 16 | os.execute("pandoc manual.md -o manual.html".. 17 | " -f markdown -t html".. 18 | " --standalone --table-of-contents --toc-depth=3".. 19 | " --title-prefix='Tenslang Manual' ".. 20 | " -c style.css".. 21 | " --template=template.html" 22 | ) 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /tenslang/docs/template.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | $for(author-meta)$ 8 | 9 | $endfor$ 10 | $if(date-meta)$ 11 | 12 | $endif$ 13 | $if(title-prefix)$$title-prefix$ - $endif$$pagetitle$ 14 | 15 | $if(quotes)$ 16 | 17 | $endif$ 18 | $if(highlighting-css)$ 19 | 22 | $endif$ 23 | $for(css)$ 24 | 25 | $endfor$ 26 | $if(math)$ 27 | $math$ 28 | $endif$ 29 | $for(header-includes)$ 30 | $header-includes$ 31 | $endfor$ 32 | 33 | 34 |
35 | $for(include-before)$ 36 | $include-before$ 37 | $endfor$ 38 | $if(title)$ 39 |
40 |

$title$

41 | $if(subtitle)$ 42 |

$subtitle$

43 | $endif$ 44 | $for(author)$ 45 |

$author$

46 | $endfor$ 47 | $if(date)$ 48 |

$date$

49 | $endif$ 50 |
51 | $endif$ 52 | $if(toc)$ 53 |
54 | $toc$ 55 |
56 | $endif$ 57 | $body$ 58 | $for(include-after)$ 59 | $include-after$ 60 | $endfor$ 61 |
62 | 63 | -------------------------------------------------------------------------------- /tenslang/example_c/Makefile: -------------------------------------------------------------------------------- 1 | 2 | 3 | # Assume we're on some Linux variant by default 4 | PLATFORM:=Linux 5 | # but try to detect other situations of interest 6 | ifeq ($(OS),Windows_NT) 7 | $(error Windows unsupported) 8 | else 9 | UNAME:=$(shell uname -s) 10 | ifeq ($(UNAME),Darwin) 11 | PLATFORM:=OSX 12 | endif 13 | endif 14 | 15 | $(warning Detected platform as $(PLATFORM)) 16 | 17 | # Try to find Terra 18 | FIND_TERRA_BIN:=$(shell which terra) 19 | TERRA_ROOT:=$(patsubst %/bin/terra,%,$(FIND_TERRA_BIN)) 20 | ifeq ($(FIND_TERRA_BIN),) 21 | $(error Could not find 'terra' executable on your PATH) 22 | endif 23 | 24 | $(warning Detected Terra directory location as $(TERRA_ROOT)) 25 | 26 | 27 | # Tools to use 28 | TENSL:=../bin/tensl 29 | 30 | 31 | # Setup the flags and options 32 | CFLAGS:= 33 | # add include directory 34 | CFLAGS:=$(CFLAGS) -I$(TERRA_ROOT)/include/ 35 | # add library directory and flags 36 | CFLAGS:=$(CFLAGS) -L$(TERRA_ROOT) -lterra 37 | # add rpath 38 | CFLAGS:=$(CFLAGS) -Wl,-rpath,$(TERRA_ROOT) 39 | 40 | ifeq ($(PLATFORM),OSX) 41 | CFLAGS:=$(CFLAGS) -pagezero_size 10000 -image_base 100000000 42 | endif 43 | 44 | $(warning CFLAGS are $(CFLAGS)) 45 | 46 | 47 | all: embedded static_call 48 | 49 | embedded: embedded_main.c 50 | $(CC) embedded_main.c -o embedded $(CFLAGS) 51 | 52 | static_func.o static_func.h: static_build.t 53 | $(TENSL) static_build.t 54 | 55 | static_call: static_func.o static_func.h static_call_main.c 56 | $(CC) static_call_main.c static_func.o -o static_call 57 | 58 | 59 | 60 | clean: 61 | -@rm embedded 62 | -@rm static_call 63 | -@rm static_func.o 64 | -@rm static_func.h 65 | 66 | -------------------------------------------------------------------------------- /tenslang/example_c/README.md: -------------------------------------------------------------------------------- 1 | Thanks to being built on the Terra/Lua infrastructure, Tenslang comes equipped with some easy ways to be used in new or existing C/C++ programs. The examples in this directory demonstrate the two primary strategies for running DSL code from a host C program. 2 | 3 | 4 | # Embedding an interpreter for Tenslang in a C program 5 | 6 | This strategy is entirely based on Lua/Terra facilities for embedding. So it doesn't really involve Tenslang specifically. At program initialization, we build a Lua environment using 7 | 8 | ``` 9 | lua_State * L = luaL_newstate(); 10 | luaL_openlibs(L); 11 | terra_init(L); 12 | ``` 13 | 14 | (more details and context are in `embedded_main.c`) 15 | 16 | The only slight detail is that as always, the `package.terrapath` variable must be extended with the location of the tenslang [`release/`](../release) directory. For example, this can be accomplished by simply executing an appropriate string command. 17 | ``` 18 | const char *pathextend = 19 | "package.terrapath = package.terrapath..';release/?.t'"; 20 | if(terra_dostring(L, pathextend)) { 21 | fprintf(stderr, "%s\n", lua_tostring(L, -1)); 22 | lua_close(L); 23 | exit(1); 24 | } 25 | ``` 26 | 27 | To read more about how to manage the Lua/Terra interpreter from C, see the appropriate documentation, for instance the overview of the [Lua C API](http://www.lua.org/pil/24.html) and overview of the [Terra C API](http://terralang.org/api.html#c-api). 28 | 29 | 30 | 31 | # Generating Object files for a static build process 32 | 33 | This strategy is very similar to a built in Terra facility, but is part of the Tenslang API proper. Rather than embed a whole Lua/Terra interpreter into a project, we generate object files that can be statically compiled into the project. This does mean that we have to give up on using any Lua meta-programming, but may be much easier to incoporate, since there are fewer runtime dependencies involved. 34 | 35 | In this strategy, we execute a Lua/Terra Tenslang script during the build process. For instance, if we want to generate code for our hello42 function, we would use a build script like: 36 | 37 | ``` 38 | import 'tenslang.tenslang' 39 | local TL = require 'tenslang.tenslib' 40 | 41 | local tensl hello42() return 21 + 21 end 42 | 43 | -- Save out the functions 44 | TL.compiletofile("hello42.o","hello42.h",{ hello42 = hello42 }) 45 | ``` 46 | 47 | This seems very much like our existing scripts, except we end it with a call to `TL.compiletofile(...)`. This function call is passed the path for an object file to generate, path for a header file to generate (can be left `nil` to omit header file generation) and a table of Tenslang functions you want to expose. 48 | 49 | Running this script will produce a header file like the following 50 | 51 | ``` 52 | #ifndef _HELLO_42_H_ 53 | #define _HELLO_42_H_ 54 | 55 | #include "stdbool.h" 56 | #include "stdint.h" 57 | 58 | 59 | int32_t hello42(); 60 | 61 | #endif 62 | ``` 63 | 64 | Then, we can use this function from a C program like 65 | 66 | ``` 67 | #include 68 | #include "hello42.h" 69 | 70 | int main(int argc, char ** argv) { 71 | int answer = hello42(); 72 | printf("%d\n", answer); 73 | 74 | return 0; 75 | } 76 | ``` 77 | 78 | If we compile this source together with the generated `hello42.o` object code, then we'll get a program that prints `42`. 79 | 80 | In the event that more complicated types like `TL.vec3` or `TL.mat2x3` are used, the header file will include the necessary `struct` declarations, with data implicitly laid out in row-major order. 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | -------------------------------------------------------------------------------- /tenslang/example_c/embedded_main.c: -------------------------------------------------------------------------------- 1 | 2 | // embedded_main.c 3 | #include 4 | #include "terra/terra.h" 5 | 6 | int main(int argc, char ** argv) { 7 | lua_State * L = luaL_newstate(); //create a plain lua state 8 | luaL_openlibs(L); //initialize its libraries 9 | //initialize the terra state in lua 10 | terra_init(L); 11 | 12 | // we can execute terra scripts from within the C program now 13 | // First, let's extend the terrapath 14 | const char *pathextend = 15 | "package.terrapath = package.terrapath.." 16 | // extend the path so that we can run both in the example directory 17 | "';../release/?.t'.." 18 | // and in the directory one up which tests are run from 19 | "';release/?.t'"; 20 | if(terra_dostring(L, pathextend)) { 21 | printf("pathextend failed\n"); 22 | fprintf(stderr, "%s\n", lua_tostring(L, -1)); 23 | lua_pop(L, 1); // pop error message from the stack 24 | lua_close(L); 25 | return 1; 26 | } 27 | 28 | // then, we can go ahead and execute the hello42 program 29 | const char *scriptstring = 30 | "import 'tenslang.tenslang'\n" 31 | "\n" 32 | "tensl hello42() return 21 + 21 end\n" 33 | "\n" 34 | "print(hello42())\n" 35 | "assert(hello42() == 42)\n" 36 | "\n"; 37 | if(terra_dostring(L, scriptstring)) { 38 | printf("script failed\n"); 39 | fprintf(stderr, "%s\n", lua_tostring(L, -1)); 40 | lua_pop(L, 1); // pop error message from the stack 41 | } 42 | 43 | // finally, let's check to make sure that error reporting 44 | // is working ok 45 | const char *errscript = "error('causing an intentional error')"; 46 | if(terra_dostring(L, errscript)) { 47 | fprintf(stderr, "%s\n", lua_tostring(L, -1)); 48 | lua_pop(L, 1); // pop error message from the stack 49 | } 50 | 51 | lua_close(L); 52 | return 0; 53 | } -------------------------------------------------------------------------------- /tenslang/example_c/static_build.t: -------------------------------------------------------------------------------- 1 | 2 | import 'tenslang.tenslang' 3 | 4 | local TL = require 'tenslang.tenslib' 5 | 6 | local round = TL.extern('round',TL.arrow(TL.num,TL.num), 7 | terra(x : double) : double 8 | return double(int(x)) 9 | end) 10 | 11 | local tensl getanswer() 12 | return 21 + 21 13 | end 14 | 15 | local tensl signedfrac( x : TL.num ) 16 | return x - round(x) 17 | end 18 | 19 | local tensl len2( x : TL.vec3 ) 20 | return +[i] x[i]*x[i] 21 | end 22 | 23 | local tensl diag3( x : TL.num ) 24 | return {{x,0,0},{0,x,0},{0,0,x}} 25 | end 26 | 27 | -- here, we save out the functions 28 | TL.compiletofile("static_func.o","static_func.h",{ 29 | getanswer = getanswer, 30 | signedfrac = signedfrac, 31 | len2 = len2, 32 | diag3 = diag3, 33 | }) 34 | 35 | -------------------------------------------------------------------------------- /tenslang/example_c/static_call_main.c: -------------------------------------------------------------------------------- 1 | 2 | // static_call_main.c 3 | #include 4 | #include "static_func.h" 5 | 6 | int main(int argc, char ** argv) { 7 | 8 | int answer = getanswer(); 9 | 10 | double fraca = signedfrac(3.2); 11 | double fracb = signedfrac(-2.3); 12 | 13 | tensor_double_3 vecx; 14 | vecx.d[0] = 1.0; 15 | vecx.d[1] = 2.0; 16 | vecx.d[2] = 3.0; 17 | double xlen = len2(vecx); 18 | 19 | tensor_double_3_3 diag42 = diag3(42); 20 | 21 | printf("answer should be 42; was %d\n", answer); 22 | printf("signedfrac 3.2 should yield 0.2; was %f\n", fraca); 23 | printf("signedfrac -2.3 should yield -0.3; was %f\n", fracb); 24 | printf("len2 {1.0,2.0,3.0} should yield 14.0; was %f\n", xlen); 25 | printf("diag3(42) should yield 42.0 and 0; were\n" 26 | " %f %f %f\n" 27 | " %f %f %f\n" 28 | " %f %f %f\n", 29 | diag42.d[0], diag42.d[1], diag42.d[2], 30 | diag42.d[3], diag42.d[4], diag42.d[5], 31 | diag42.d[6], diag42.d[7], diag42.d[8]); 32 | 33 | return 0; 34 | } -------------------------------------------------------------------------------- /tenslang/examples/geometric.t: -------------------------------------------------------------------------------- 1 | 2 | import 'tenslang.tenslang' 3 | local TL = require 'tenslang.tenslib' 4 | 5 | local cmath = terralib.includecstring [[#include "math.h"]] 6 | local sqrt = TL.extern('sqrt', TL.arrow(TL.num, TL.num), cmath.sqrt) 7 | 8 | local maximpl = rawget(cmath,'fmax') or terra( a:double, b:double) 9 | if a > b then return a else return b end 10 | end 11 | local max = TL.extern('max', TL.arrow({TL.num,TL.num},TL.num), 12 | maximpl) 13 | 14 | local num = TL.num 15 | local vec2 = TL.vec2 16 | 17 | ------------------------------------------------------------------------------- 18 | 19 | -- helper functions 20 | local tensl len2d( x : vec2 ) return sqrt(+[i] x[i]*x[i]) end 21 | 22 | local EPS = 1e-5 23 | 24 | ------------------------------------------------------------------------------- 25 | 26 | -- example geometric constraints 27 | 28 | local tensl bzSample( 29 | t:num, -- this is constant 30 | p0:vec2, p1:vec2, p2:vec2, p3:vec2, 31 | samp:vec2 32 | ) 33 | -- These can be cached because they're a function of a constant 34 | var p = {p0,p1,p2,p3} 35 | var t1 = 1.0-t 36 | var c = { t1*t1*t1, 3*t1*t1*t, 3*t1*t*t, t*t*t } 37 | 38 | var interp = +[i] :[j] c[i]*p[i,j] 39 | var diff = interp - samp 40 | 41 | return diff 42 | end 43 | 44 | -- mid must lie on the bisector line 45 | local tensl bisector( p0:vec2, p1:vec2, mid:vec2 ) 46 | var ebase = p1-p0 47 | var e0 = p0-mid 48 | var e1 = p1-mid 49 | 50 | var projdiff = (+[i] ebase[i] * e0[i]) + (+[i] ebase[i] * e1[i]) 51 | var lenbase = max(len2d(ebase), EPS) -- limit divisor 52 | 53 | return projdiff / lenbase 54 | end 55 | 56 | local tensl colinear( p0:vec2, p1:vec2, p2:vec2 ) 57 | var e01 = p1-p0 58 | var e02 = p2-p0 59 | return e01[0] * e02[1] - e01[1] * e02[0] 60 | end 61 | 62 | local tensl triheight( base0:vec2, base1:vec2, apex:vec2, h:num ) 63 | var b = base1-base0 64 | var e0 = apex-base0 65 | var e1 = apex-base1 66 | 67 | var eavg = e0 + e1 68 | var bunit = (1.0/max(len2d(b), EPS)) * b 69 | 70 | var det = bunit[0]*eavg[1] - bunit[1]*eavg[0] 71 | var diff = det - h 72 | end 73 | 74 | local tensl equalpt( p0:vec2, p1:vec2 ) 75 | return p0-p1 76 | end 77 | 78 | local tensl equalnum( a:num, b:num ) 79 | return a-b 80 | end 81 | 82 | local tensl oncircle( pt:vec2, center:vec2, radius:num ) 83 | var c_pt = pt-center 84 | var ptlen = max(len2d(c_pt), EPS) 85 | return ptlen - radius 86 | end 87 | 88 | 89 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /tenslang/examples/tensor_expressions.t: -------------------------------------------------------------------------------- 1 | 2 | import 'tenslang.tenslang' 3 | local TL = require 'tenslang.tenslib' 4 | 5 | local cmath = terralib.includecstring [[#include "math.h"]] 6 | 7 | local sqrt = TL.extern('sqrt', TL.arrow(TL.num, TL.num), cmath.sqrt) 8 | 9 | ------------------------------------------------------------------------------- 10 | 11 | local tensl dot2d( x:TL.vec2, y:TL.vec2 ) 12 | return +[i] x[i] * y[i] 13 | end 14 | 15 | local tensl vec2len( x:TL.vec2 ) 16 | return sqrt( +[i] x[i]*x[i] ) 17 | end 18 | 19 | local tensl matvec3mult( m:TL.mat3, x:TL.vec3 ) 20 | return :[i] +[j] m[i,j] * x[j] 21 | end 22 | 23 | local tensl transpose2( m:TL.mat2 ) 24 | return :[i,j] m[j,i] 25 | end 26 | 27 | local tensl qform4( A:TL.mat4, x:TL.vec4 ) 28 | return +[i,j] x[i] * A[i,j] * x[j] 29 | end 30 | 31 | local tensl innerprod3( x:TL.vec3, A:TL.mat3, y:TL.vec3) 32 | return +[i,j] x[i] * A[i,j] * y[j] 33 | end 34 | 35 | local tensl outerprod3( x:TL.vec3, y:TL.vec3 ) 36 | return :[i,j] x[i] * y[j] 37 | end 38 | 39 | local tensl matmat3mult( A:TL.mat3, B:TL.mat3 ) 40 | return :[i,j] +[k] A[i,k] * B[k,j] 41 | end 42 | 43 | local tensl trace2( M:TL.mat2 ) 44 | return +[i] M[i,i] 45 | end 46 | 47 | local tensl frobenius3( M:TL.mat3 ) 48 | return +[i,j] M[i,j] * M[i,j] 49 | end 50 | 51 | ------------------------------------------------------------------------------- 52 | 53 | 54 | 55 | ------------------------------------------------------------------------------- 56 | -------------------------------------------------------------------------------- /tenslang/release/tenslang/src/builtins.t: -------------------------------------------------------------------------------- 1 | local B = {} 2 | package.loaded["tenslang.src.builtins"] = B 3 | 4 | local A = require 'tenslang.src.ast' 5 | local T = require 'tenslang.src.types' 6 | 7 | local LangLib = require 'tenslang.tenslib' 8 | 9 | local C = require 'tenslang.src.c' 10 | 11 | ------------------------------------------------------------------------------- 12 | --[[ Builtin Prototype ]]-- 13 | ------------------------------------------------------------------------------- 14 | 15 | local Builtin = {} 16 | Builtin.__index = Builtin 17 | 18 | local function isbuiltin(obj) return getmetatable(obj) == Builtin end 19 | B.isbuiltin = isbuiltin 20 | 21 | -- only define builtins in this file 22 | local function NewBuiltin(name, typ, genfunc) 23 | assert(type(name) == 'string') 24 | assert(T.istype(typ) and typ:isarrow()) 25 | 26 | local bi = setmetatable({ 27 | name = name, 28 | type = typ, 29 | genfunc = genfunc, 30 | }, Builtin) 31 | 32 | return bi 33 | end 34 | 35 | ------------------------------------------------------------------------------- 36 | --[[ Generic Built-in mechanism for Terra code ]]-- 37 | ------------------------------------------------------------------------------- 38 | 39 | function B.extern(name, typesig, func) 40 | return NewBuiltin(name, typesig, function(callast, ...) 41 | local args = {...} 42 | return `func( [args] ) 43 | end) 44 | end 45 | 46 | ------------------------------------------------------------------------------- 47 | --[[ Specific Builtins ]]-- 48 | ------------------------------------------------------------------------------- 49 | 50 | local function terror_body (file, line) 51 | local prelude = file..':'..tostring(line)..': Assert Failed\n' 52 | return quote do 53 | C.fprintf(C.stderr, prelude) 54 | C.exit(1) 55 | end end 56 | end 57 | 58 | B.assert = NewBuiltin('assert', T.arrow(T.bool,{}), 59 | function(callast, test) 60 | return quote 61 | if not test then 62 | [terror_body(callast.filename, callast.linenumber)] 63 | end 64 | end 65 | end) 66 | 67 | -- type could be difficult here? 68 | --B.print = NewBuiltin('print', T.arrow()) 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | -------------------------------------------------------------------------------- /tenslang/release/tenslang/src/c.t: -------------------------------------------------------------------------------- 1 | 2 | 3 | local c_blob = terralib.includecstring [[ 4 | #include "stdlib.h" 5 | #include "stdio.h" 6 | 7 | FILE * __get__stdout() { return stdout; } 8 | FILE * __get__stdin() { return stdin; } 9 | FILE * __get__stderr() { return stderr; } 10 | 11 | ]] 12 | 13 | rawset(c_blob, 'stdout', c_blob.__get__stdout()) 14 | rawset(c_blob, 'stdin', c_blob.__get__stdin()) 15 | rawset(c_blob, 'stderr', c_blob.__get__stderr()) 16 | 17 | package.loaded["tenslang.src.c"] = c_blob 18 | return c_blob 19 | -------------------------------------------------------------------------------- /tenslang/release/tenslang/src/coverage.t: -------------------------------------------------------------------------------- 1 | 2 | if not rawget(_G,'COVERAGE_MODULE_HAS_BEEN_INSTALLED') then 3 | local ffi = require 'ffi' 4 | 5 | _G['COVERAGE_MODULE_HAS_BEEN_INSTALLED'] = true 6 | local coverageloader, err = loadfile("coverageinfo.lua") 7 | --print('FOUND COVERAGE ', coverageloader, err) 8 | 9 | local filetable = coverageloader and coverageloader() or {} 10 | local function dumplineinfo() 11 | local F = io.open("coverageinfo.lua","w") 12 | F:write("return {\n") 13 | for filename,linetable in pairs(filetable) do 14 | F:write("['"..filename.."']={\n") 15 | for linenum,count in pairs(linetable) do 16 | F:write("["..linenum.."]="..count..",\n") 17 | end 18 | F:write("},\n") 19 | end 20 | F:write("}\n") 21 | F:close() 22 | end 23 | local function debughook(event) 24 | local info = debug.getinfo(2,"Sl") 25 | -- exclude for instance, metaprogrammed lua code 26 | if string.sub(info.source, 1,1) == '@' then 27 | local linetable = filetable[info.source] 28 | if not linetable then 29 | linetable = {} 30 | filetable[info.source] = linetable 31 | end 32 | linetable[info.currentline] = (linetable[info.currentline] or 0) + 1 33 | end 34 | end 35 | debug.sethook(debughook,"l") 36 | -- make a fake ffi object that causes dumplineinfo to be called when 37 | -- the lua state is removed 38 | ffi.cdef [[ 39 | typedef struct {} __linecoverage; 40 | ]] 41 | ffi.metatype("__linecoverage", { __gc = dumplineinfo } ) 42 | _G[{}] = ffi.new("__linecoverage") 43 | end 44 | -------------------------------------------------------------------------------- /tenslang/release/tenslang/src/pratt.t: -------------------------------------------------------------------------------- 1 | 2 | local P = {} 3 | 4 | --same tokentypes as lexer, duplicated here for convience 5 | P.name = terralib.languageextension.name 6 | P.string = terralib.languageextension.string 7 | P.number = terralib.languageextension.number 8 | P.eof = terralib.languageextension.eof 9 | P.default = terralib.languageextension.default 10 | 11 | --a parser for a language is defined by a table of functions, one for each non-termina 12 | --in the language (e.g expression,statement, etc.) 13 | 14 | --these functions are either (1) raw recursive decent parsers 15 | -- or (2) Pratt parser objects 16 | 17 | --pratt parser objects behave like functions 18 | --but their behavior is defined by adding rules for what to do when 19 | --a prefix or suffix is found 20 | --futhermore, when using a pratt parser object, you can specify 21 | --a precedence such that the parser will only parse expressions 22 | --with precedence _higher_ than that. 23 | --see tests/lib/pratttest.t for examples of how to use this interface 24 | --to parse common patters 25 | 26 | P.pratt = {} 27 | function P.Pratt() 28 | return setmetatable({ 29 | infixtable = {}; 30 | prefixtable = {}; 31 | },{ __index = P.pratt, __call = P.pratt.__call }) 32 | end 33 | 34 | 35 | --define a rule for infix operators like '+' 36 | --precidence is a numeric precedence 37 | --tokentype is a lexer token type (see embeddinglanguages.html) 38 | --rule is a function: function(parser,lhs) ... end 39 | --it is given the parser object and the AST value for the lhs of the expression. 40 | --it should parse and return the AST value for current expression 41 | --P.default can be used to define a rule that fires when no other rule applies 42 | function P.pratt:infix(tokentype,prec,rule) 43 | if self.infixtable[tokentype] then 44 | error("infix rule for "..tostring(tokentype).." already defined") 45 | end 46 | self.infixtable[tokentype] = { 47 | prec = prec; 48 | rule = rule; 49 | } 50 | return self 51 | end 52 | 53 | --define a prefix rule 54 | --rule is a function: function(parser) ... end, that takes the parser object 55 | --and returns an AST for the expression 56 | 57 | function P.pratt:prefix(tokentype,rule) 58 | if self.prefixtable[tokentype] then 59 | error("prefix rule for "..tostring(tokentype).." already defined") 60 | end 61 | self.prefixtable[tokentype] = rule 62 | return self 63 | end 64 | 65 | P.defaultprefix = function(parser) 66 | parser:error("unexpected symbol") 67 | end 68 | --table-driven implementation, invoked when you call the Pratt parser object 69 | P.pratt.__call = function(pratt,parser,precortoken,fixity) 70 | local isleft = fixity == nil or fixity == "left" 71 | local limit 72 | if not precortoken then 73 | limit,isleft = 0,false 74 | elseif type(precortoken) == "number" then 75 | limit = precortoken 76 | else 77 | if not pratt.infixtable[precortoken] then 78 | error("precidence not defined for "..tostring(precortoken)) 79 | end 80 | limit = pratt.infixtable[precortoken].prec 81 | end 82 | local tt = parser:cur().type 83 | local prefixrule = pratt.prefixtable[tt] or pratt.prefixtable[P.default] or P.defaultprefix 84 | local results = { prefixrule(parser) } 85 | while true do 86 | tt = parser:cur().type 87 | local op = pratt.infixtable[tt] or pratt.infixtable[P.default] 88 | if not op or (isleft and op.prec <= limit) or (not isleft and op.prec < limit) then 89 | break 90 | end 91 | results = { op.rule(parser,unpack(results)) } 92 | end 93 | return unpack(results) 94 | end 95 | 96 | 97 | --create a parser 98 | --langtable is the table of non-terminal functions and/or pratt parser objects 99 | --lexer is the lexer object given by the language extension interface 100 | function P.Parser(langtable,lexer) 101 | local instance = {} 102 | for k,v in pairs(lexer) do 103 | if type(v) == "function" then 104 | instance[k] = function(self,...) return v(lexer,...) end 105 | elseif string.sub(k,1,1) ~= "_" then 106 | instance[k] = v 107 | end 108 | end 109 | for k,v in pairs(langtable) do 110 | if instance[k] then error("language nonterminal overlaps with lexer function "..k) end 111 | instance[k] = v 112 | end 113 | return instance 114 | end 115 | 116 | --create a parser and run a non-terminal in one go 117 | --nonterminal is the name (e.g. "expression") of the non-terminal to use as the starting point in the langtable 118 | function P.Parse(langtable,lexer,nonterminal) 119 | local self = P.Parser(langtable,lexer) 120 | return self[nonterminal](self) 121 | end 122 | 123 | 124 | return P -------------------------------------------------------------------------------- /tenslang/release/tenslang/src/stats.t: -------------------------------------------------------------------------------- 1 | 2 | local Stats = {} 3 | package.loaded["tenslang.src.stats"] = Stats 4 | 5 | 6 | -- NOTE: I'm currently measuring 1 us overhead on wrapping a simple 7 | -- timer around function launches 8 | 9 | ------------------------------------------------------------------------------ 10 | --[[ Counters ]]-- 11 | ------------------------------------------------------------------------------ 12 | 13 | local Counter = {} 14 | Counter.__index = Counter 15 | 16 | function Stats.NewCounter(name) 17 | local counter = setmetatable({ 18 | _val = 0, 19 | _name = tostring(name), 20 | }, Counter) 21 | return counter 22 | end 23 | 24 | function Counter:increment() 25 | self._val = self._val + 1 26 | end 27 | 28 | function Counter:get() 29 | return self._val 30 | end 31 | function Counter:print(prefix) 32 | prefix = (prefix or self._name) .. ': ' 33 | print(prefix, self._val) 34 | end 35 | 36 | 37 | ------------------------------------------------------------------------------ 38 | --[[ Timers ]]-- 39 | ------------------------------------------------------------------------------ 40 | 41 | local Timer = {} 42 | Timer.__index = Timer 43 | 44 | function Stats.NewTimer(name) 45 | local timer = setmetatable({ 46 | _name = tostring(name), 47 | _start = nil, 48 | 49 | _count = 0, 50 | _min_ms = math.huge, 51 | _max_ms = 0, 52 | _sum_ms = 0, 53 | }, Timer) 54 | return timer 55 | end 56 | 57 | function Timer:setName(name) 58 | self._name = tostring(name) 59 | end 60 | -- prefix should be supplied if printed 61 | function Timer.__add(lhs, rhs) 62 | if getmetatable(lhs) ~= Timer or getmetatable(rhs) ~= Timer then 63 | error('cannot add a Timer to a non-Timer') 64 | end 65 | local sumtimer = setmetatable({ 66 | _name = '', 67 | _start = nil, 68 | 69 | _count = lhs._count + rhs._count, 70 | _min_ms = math.min(lhs._min_ms, rhs._min_ms), 71 | _max_ms = math.max(lhs._max_ms, rhs._max_ms), 72 | _sum_ms = lhs._sum_ms + rhs._sum_ms, 73 | }, Timer) 74 | return sumtimer 75 | end 76 | 77 | function Timer:start(timestamp_in_ms) 78 | if not timestamp_in_ms then 79 | timestamp_in_ms = terralib.currenttimeinseconds() * 1.0e3 80 | end 81 | self._start = timestamp_in_ms 82 | return timestamp_in_ms 83 | end 84 | function Timer:stop(timestamp_in_ms) 85 | if not timestamp_in_ms then 86 | timestamp_in_ms = terralib.currenttimeinseconds() * 1.0e3 87 | end 88 | if not self._start then error('must match timer stops with starts') end 89 | local dt = timestamp_in_ms - self._start 90 | self._start = nil 91 | 92 | self._count = self._count + 1 93 | self._min_ms = math.min(self._min_ms, dt) 94 | self._max_ms = math.max(self._max_ms, dt) 95 | self._sum_ms = self._sum_ms + dt 96 | 97 | return timestamp_in_ms 98 | end 99 | 100 | -- Bulk routines for convenience 101 | function Stats.StopAndStart(stop_counters, start_counters) 102 | local timestamp_in_ms = terralib.currenttimeinseconds() * 1.0e3 103 | for _,timer in ipairs(stop_counters) do 104 | timer:stop(timestamp_in_ms) 105 | end 106 | for _,timer in ipairs(start_counters) do 107 | timer:start(timestamp_in_ms) 108 | end 109 | return timestamp_in_ms 110 | end 111 | function Stats.Start(counters) 112 | return Stats.StopAndStart({},counters) 113 | end 114 | function Stats.Stop(counters) 115 | return Stats.StopAndStart(counters,{}) 116 | end 117 | 118 | function Timer:getcount() return self._count end 119 | function Timer:getmin() return self._min_ms end 120 | function Timer:getmax() return self._max_ms end 121 | function Timer:getsum() return self._sum_ms end 122 | function Timer:getavg() return self._sum_ms / self._count end 123 | 124 | function Timer:print(prefix) 125 | prefix = (prefix or self._name) .. ':' 126 | print(prefix) 127 | print(' avg: '..tostring(self:getavg())) 128 | print(' min: '..tostring(self._min_ms)) 129 | print(' max: '..tostring(self._max_ms)) 130 | print(' sum: '..tostring(self._sum_ms)) 131 | print(' count: '..tostring(self._count)) 132 | end 133 | 134 | 135 | 136 | ------------------------------------------------------------------------------ 137 | --[[ Global Statistics ]]-- 138 | ------------------------------------------------------------------------------ 139 | 140 | local global_stat_table = {} 141 | 142 | function Stats.NewGlobalCounter(name) 143 | name = tostring(name) 144 | if global_stat_table[name] then 145 | error("stat name '"..name.."' is already being used") end 146 | local counter = Stats.NewCounter(name) 147 | global_stat_table[name] = counter 148 | return counter 149 | end 150 | 151 | function Stats.NewGlobalTimer(name) 152 | name = tostring(name) 153 | if global_stat_table[name] then 154 | error("stat name '"..name.."' is already being used") end 155 | local timer = Stats.NewTimer(name) 156 | global_stat_table[name] = timer 157 | return timer 158 | end 159 | 160 | -- get statistic 161 | function Stats.GetGlobalStat(name) 162 | local lookup = global_stat_table[name] 163 | if not lookup then error("could not find global stat '"..name.."'") end 164 | return lookup 165 | end 166 | 167 | -------------------------------------------------------------------------------- /tenslang/release/tenslang/tenslang.t: -------------------------------------------------------------------------------- 1 | -- File to be imported, defining the language 2 | -- put import 'tenslang.tenslang' at the top of files 3 | 4 | -- shim in the coverage analysis 5 | --require 'tenslang.src.coverage' 6 | 7 | local P = require 'tenslang.src.parser' 8 | local Specializer = require 'tenslang.src.specializer' 9 | local F = require 'tenslang.src.functions' 10 | local Lib = require 'tenslang.tenslib' 11 | -- other passes and things? 12 | 13 | local function handleStatement(self, lexer) 14 | local ast, assigntuple = P.ParseStatement(lexer) 15 | local constructor = function(env_fn) 16 | if Lib._UNIT_TEST_PARSER then 17 | return ast 18 | elseif Lib._UNIT_TEST_SPECIALIZER then 19 | return Specializer.specialize(ast, env_fn()) 20 | else 21 | local decl_ast = Specializer.specialize(ast, env_fn()) 22 | return F.NewFunction { decl_ast = decl_ast } 23 | end 24 | end 25 | return constructor, assigntuple 26 | end 27 | 28 | local function handleExpression(self, lexer) 29 | local ast = P.ParseExpression(lexer) 30 | local constructor = function(env_fn) 31 | if Lib._UNIT_TEST_PARSER then 32 | return ast 33 | elseif Lib._UNIT_TEST_SPECIALIZER then 34 | return Specializer.specialize(ast, env_fn()) 35 | else 36 | local decl_ast = Specializer.specialize(ast, env_fn()) 37 | return F.NewFunction { decl_ast = decl_ast } 38 | end 39 | end 40 | return constructor 41 | end 42 | 43 | 44 | local tenslanguage = { 45 | name = 'tenslang', 46 | entrypoints = {'tensl'}, 47 | keywords = { 48 | '_', -- always good to reserve the underscore for language use 49 | 'var', 50 | }, 51 | 52 | expression = handleExpression, 53 | statement = handleStatement, 54 | localstatement = handleStatement, 55 | } 56 | 57 | return tenslanguage 58 | -------------------------------------------------------------------------------- /tenslang/release/tenslang/tenslib.t: -------------------------------------------------------------------------------- 1 | local Lib = {} 2 | package.loaded["tenslang.tenslib"] = Lib 3 | 4 | ------------------------------------------------------------------------------- 5 | -- Policy: 6 | -- This file is responsible for the Lua <--> Tenslang interface 7 | -- as exposed via the tenslib object. 8 | -- As such, it's generally preferrable to define most functions 9 | -- elsewhere in the compiler and then just export them here. Though that's 10 | -- hardly a strict policy. 11 | -- However, it's definitely a bad idea to define functions in this file 12 | -- that are only for use internally by the compiler. 13 | ------------------------------------------------------------------------------- 14 | 15 | local T = require 'tenslang.src.types' 16 | local B = require 'tenslang.src.builtins' 17 | local F = require 'tenslang.src.functions' 18 | 19 | ------------------------------------------------------------------------------- 20 | 21 | local function deep_array_copy(tbl) 22 | if type(tbl) ~= 'table' then return tbl 23 | else 24 | local cp = {} 25 | for i=1,#tbl do cp[i] = deep_array_copy(tbl[i]) end 26 | return cp 27 | end 28 | end 29 | 30 | local ConstantProto = {} 31 | ConstantProto.__index = ConstantProto 32 | 33 | function Lib.isconstant(obj) return getmetatable(obj) == ConstantProto end 34 | 35 | function Lib.Constant(typ, val) 36 | if not T.istype(typ) or not typ:isvalue() then 37 | error('first argument to Constant must be a value type', 2) 38 | end 39 | if not T.checkluaval(val, typ) then 40 | error('second argument to Constant must be a value of the given type', 2) 41 | end 42 | 43 | local c = setmetatable({ 44 | _type = typ, 45 | _value = deep_array_copy(val), 46 | }, ConstantProto) 47 | return c 48 | end 49 | 50 | function ConstantProto:get() 51 | return deep_array_copy(self._value) 52 | end 53 | 54 | function ConstantProto:gettype() 55 | return self._type 56 | end 57 | 58 | ------------------------------------------------------------------------------- 59 | 60 | -- selectively expose some parts of the type system to the user 61 | for _,name in ipairs({ 62 | "num", 63 | "bool", 64 | 65 | "tensor", 66 | "matrix", 67 | "vector", 68 | "arrow", 69 | 70 | "istype", 71 | }) do 72 | Lib[name] = T[name] 73 | end 74 | -- vec and mat shorthands 75 | for _,tchar in ipairs({ '', 'b' }) do 76 | for i=2,4 do 77 | local n = tostring(i) 78 | Lib['vec'..n..tchar] = T['vec'..n..tchar] 79 | Lib['mat'..n..tchar] = T['mat'..n..tchar] 80 | for j=2,4 do 81 | local m = tostring(j) 82 | Lib['mat'..n..'x'..m..tchar] = T['mat'..n..'x'..m..tchar] 83 | end 84 | end 85 | end 86 | 87 | ------------------------------------------------------------------------------- 88 | 89 | -- selectively expose some of the built-ins 90 | Lib.assert = B.assert 91 | 92 | -- generic way for clients to extend the language with external 93 | -- functions as a kind of custom built-in 94 | Lib.extern = B.extern 95 | 96 | ------------------------------------------------------------------------------- 97 | 98 | Lib.isfunction = F.isfunction 99 | Lib.compiletofile = F.compiletofile 100 | 101 | 102 | 103 | 104 | -------------------------------------------------------------------------------- /tenslang/runtests: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env terra 2 | local ffi = require 'ffi' 3 | 4 | if ffi.os == 'Windows' then 5 | print('TODO windows') 6 | end 7 | local lscmd = "find tests" 8 | 9 | local color = { 10 | DEFAULT = '\27[39m', 11 | OKBLUE = '\27[34m', -- Blue 12 | OKGREEN = '\27[32m', -- Green 13 | WARNING = '\27[33m', -- Yellow 14 | FAIL = '\27[31m', -- Red 15 | } 16 | 17 | local passed = {} 18 | local badpassed = {} 19 | local failed = {} 20 | local disabled = {} 21 | 22 | local exclude = { 23 | ['tests/test.lua'] = true, 24 | } 25 | 26 | local disable_str = '--DISABLE-TEST' 27 | local function prefixmatch(str, prefix) 28 | return string.sub(str,1,#prefix) == prefix 29 | end 30 | local function is_disabled(filename) 31 | local handle = io.open(filename, "r") 32 | local line1 = handle:read() 33 | handle:close() 34 | 35 | local is_disabled = false 36 | if prefixmatch(line1,disable_str) then is_disabled = true end 37 | return is_disabled 38 | end 39 | 40 | 41 | 42 | print([[ 43 | ============================================================================== 44 | = Running tests... = 45 | ============================================================================]] 46 | ..'==') 47 | 48 | for line in io.popen(lscmd):lines() do 49 | if ffi.os == "Windows" then error('TODO windows') end 50 | local filename = line:match("^(tests/.*%.t)$") 51 | if filename then 52 | if is_disabled(filename) then 53 | table.insert(disabled, filename) 54 | else 55 | --print('running '..filename) 56 | local execstring = "./bin/tensl " 57 | execstring = execstring..filename 58 | 59 | print(execstring) 60 | local success = os.execute(execstring) 61 | 62 | if success ~= 0 then 63 | table.insert(failed, filename) 64 | print(filename .. " "..color.FAIL.."FAILED"..color.DEFAULT) 65 | else 66 | table.insert(passed, filename) 67 | print(filename .. " "..color.OKGREEN.."PASSED"..color.DEFAULT) 68 | end 69 | end 70 | end 71 | end 72 | print() 73 | 74 | -- test whether the coverageanalysis exists 75 | local coverage_on = os.execute('test -f coverageinfo.lua') == 0 76 | if coverage_on then 77 | print('-- Assembling Coverage Analysis Report --') 78 | os.execute('./covanalysis') 79 | os.execute('rm coverageinfo.lua') 80 | end 81 | 82 | 83 | print([[ 84 | ----------------------------- Disabled tests -----------------------------]] 85 | ..'--') 86 | for i,e in ipairs(disabled) do print(e) end 87 | print() 88 | 89 | print([[ 90 | ------------------------------ Passed tests ------------------------------]] 91 | ..'--') 92 | for i,e in ipairs(passed) do print(e) end 93 | print() 94 | 95 | print([[ 96 | ------------------------------ Failed tests ------------------------------]] 97 | ..'--') 98 | for i,e in ipairs(failed) do print(e) end 99 | print() 100 | 101 | print('tests disabled: '..tostring(#disabled)) 102 | print('tests passed: '..tostring(#passed)) 103 | if #failed + #badpassed > 0 then 104 | print(color.FAIL..'tests failed: '..tostring(#failed + #badpassed).. 105 | color.DEFAULT) 106 | else 107 | print(color.OKGREEN..'All tests passed!'..color.DEFAULT) 108 | end 109 | -------------------------------------------------------------------------------- /tenslang/tests/arithmetic.t: -------------------------------------------------------------------------------- 1 | local test = require 'tests.test' 2 | 3 | import 'tenslang.tenslang' 4 | 5 | local Lib = require 'tenslang.tenslib' 6 | 7 | ------------------------------------------------------------------------------ 8 | 9 | local tensl arith() 10 | var x = 42.0 11 | var y : Lib.num = x 12 | Lib.assert(x-y == 0) 13 | Lib.assert(x+y == 84) 14 | 15 | x = 3.0 16 | y = 5.0 17 | Lib.assert(x*y == 15) 18 | Lib.assert(x/y == 3.0/5.0) 19 | Lib.assert(y/x == 5.0/3.0) 20 | 21 | Lib.assert((-x) * (-y) == x * y) 22 | Lib.assert(x <= y) 23 | Lib.assert(x < y) 24 | Lib.assert(y > x) 25 | Lib.assert(y >= x) 26 | Lib.assert(x ~= y) 27 | end 28 | 29 | arith() 30 | 31 | local tensl booltests() 32 | var x = true 33 | var y : Lib.bool = x 34 | Lib.assert(x and y == true) 35 | Lib.assert(x or y == true) 36 | Lib.assert(x == y) 37 | 38 | x = true 39 | y = false 40 | Lib.assert( (not x and not y) == not (x or y) ) 41 | Lib.assert(x ~= y) 42 | end 43 | 44 | booltests() 45 | 46 | ------------------------------------------------------------------------------ 47 | 48 | ------------------------------------------------------------------------------ 49 | -------------------------------------------------------------------------------- /tenslang/tests/ast_unit.t: -------------------------------------------------------------------------------- 1 | local test = require 'tests.test' 2 | -- call to maybe install coverage 3 | require 'tenslang.tenslang' 4 | 5 | local A = require 'tenslang.src.ast' 6 | 7 | 8 | ------------------------------------ 9 | -- Cannot create abstract AST nodes 10 | test.fail(function() 11 | local exp = A.Expression:New() 12 | end, 'cannot create new abstract AST') 13 | 14 | --------------------------------------- 15 | -- Test creation of various AST nodes 16 | 17 | local somesym = A.NewSymbol('somesym') 18 | local somename = A.Name:New { value=somesym } 19 | local somenumber = A.Number:New { value=42 } 20 | local somestring = A.String:New { value='foobar' } 21 | local somebool = A.Bool:New { value=true } 22 | 23 | local someuop = A.UnaryOp:New { op='-', expr=somenumber } 24 | local somebop = A.BinaryOp:New { op='+', lhs=somenumber, rhs=somenumber } 25 | 26 | local somelist = A.List:New { exprs={somename,somename} } 27 | local somelookup = A.Lookup:New { 28 | base = somename, args = { somename, somename }, 29 | } 30 | local somecall = A.Call:New { base=somename, args={} } 31 | 32 | 33 | local someexpr = A.ExprStmt:New { expr = somebop } 34 | local someargdecl = A.ArgDecl:New { name=somesym } 35 | 36 | local somedo = A.DoStmt:New { body={someexpr} } 37 | local someasgn = A.Assignment:New { lvalues={somename}, rvalues={somebop} } 38 | local somedecl = A.DeclStmt:New { name=somesym } 39 | local somelet = A.Let:New { block={someexpr}, expr=someuop } 40 | local somefunc = A.Function:New { 41 | name='foo', 42 | args={someargdecl}, 43 | body={somedo}, 44 | rets={somebool}, 45 | } 46 | 47 | test.fail(function() 48 | local dummy = A.Function:New { args={someargdecl}, body={somedo} } 49 | end, 'Could not create AST because of bad shape') 50 | 51 | somefunc:printpretty() 52 | test.eq(somefunc:depth(), 5) 53 | test.eq(somefunc:size(), 8) 54 | 55 | A.NewCopyPass { 56 | passname = "typecheck", 57 | copymembers = {"annotation1"}, 58 | defaultvals = { 59 | node_type = 3, 60 | }, 61 | --verbose=true, 62 | } 63 | A.NewInertPass { 64 | passname='analysis', 65 | --verbose = true, 66 | } 67 | 68 | 69 | -------------------------------------------------------------------------------- /tenslang/tests/c_build.t: -------------------------------------------------------------------------------- 1 | local test = require 'tests.test' 2 | 3 | import 'tenslang.tenslang' 4 | 5 | local Lib = require 'tenslang.tenslib' 6 | 7 | ------------------------------------------------------------------------------ 8 | 9 | -- This test is more complicated because it needs to invoke a Makefile build 10 | 11 | 12 | print("** ENSURE DIRECTORY IS CLEAN") 13 | assert(os.execute('cd example_c; make clean') == 0) 14 | print("** DO BUILD") 15 | assert(os.execute('cd example_c; make') == 0) 16 | print("** BUILD COMPLETED") 17 | 18 | -- now we need to check that the build produced working executables 19 | -- with the correct output 20 | 21 | local embedded = assert(io.popen('./example_c/embedded 2>&1', 'r')) 22 | local embedded_output = embedded:read('*all') 23 | test.eq(embedded_output,[[ 24 | [string ""]:1: causing an intentional error 25 | 42 26 | ]]) 27 | -- print anyway if test didn't fail 28 | print() 29 | print(embedded_output) 30 | embedded:close() 31 | 32 | local static_call = assert(io.popen('./example_c/static_call 2>&1', 'r')) 33 | local static_output = static_call:read('*all') 34 | test.eq(static_output,[[ 35 | answer should be 42; was 42 36 | signedfrac 3.2 should yield 0.2; was 0.200000 37 | signedfrac -2.3 should yield -0.3; was -0.300000 38 | len2 {1.0,2.0,3.0} should yield 14.0; was 14.000000 39 | diag3(42) should yield 42.0 and 0; were 40 | 42.000000 0.000000 0.000000 41 | 0.000000 42.000000 0.000000 42 | 0.000000 0.000000 42.000000 43 | ]]) 44 | -- print anyway if test didn't fail 45 | print() 46 | print(static_output) 47 | static_call:close() 48 | 49 | 50 | 51 | print("** CLEANING UP") 52 | assert(os.execute('cd example_c; make clean') == 0) 53 | print("** CLEANED UP") -------------------------------------------------------------------------------- /tenslang/tests/constants.t: -------------------------------------------------------------------------------- 1 | local test = require 'tests.test' 2 | 3 | import 'tenslang.tenslang' 4 | 5 | local Lib = require 'tenslang.tenslib' 6 | 7 | ------------------------------------------------------------------------------ 8 | 9 | local vecconst = Lib.Constant(Lib.vec3,{1,2,3}) 10 | local tensl retvec() 11 | return vecconst 12 | end 13 | 14 | test.eq(Lib.arrow({}, Lib.vec3), retvec:gettype()) 15 | test.aeq( retvec(), {1,2,3}) 16 | 17 | local matconst = Lib.Constant(Lib.mat3,{{1,0,0},{0,1,0},{0,0,1}}) 18 | local tensl retmatid() 19 | return matconst 20 | end 21 | 22 | test.eq(Lib.arrow({}, Lib.mat3), retmatid:gettype()) 23 | test.rec_aeq( retmatid(), {{1,0,0},{0,1,0},{0,0,1}}) 24 | 25 | ------------------------------------------------------------------------------ 26 | 27 | local cn = Lib.Constant(Lib.num,1) 28 | local cb = Lib.Constant(Lib.bool,false) 29 | local tensl retcs() 30 | return cn, cb 31 | end 32 | 33 | test.eq(Lib.arrow({},{Lib.num, Lib.bool}), 34 | retcs:gettype()) 35 | test.aeq( {retcs()}, {1,false}) 36 | -------------------------------------------------------------------------------- /tenslang/tests/control.t: -------------------------------------------------------------------------------- 1 | local test = require 'tests.test' 2 | 3 | import 'tenslang.tenslang' 4 | 5 | local Lib = require 'tenslang.tenslib' 6 | 7 | ------------------------------------------------------------------------------ 8 | 9 | local tensl doblock() 10 | var x = 3 11 | do 12 | var x = 4 13 | x = 5 14 | end 15 | return x 16 | end 17 | 18 | test.eq(Lib.arrow({},Lib.num), doblock:gettype()) 19 | test.eq( doblock(), 3 ) 20 | 21 | ------------------------------------------------------------------------------ 22 | 23 | local tensl decltest() 24 | var x : Lib.num 25 | x = 1 26 | return x 27 | end 28 | 29 | test.eq(Lib.arrow({},Lib.num), decltest:gettype()) 30 | test.eq( decltest(), 1 ) 31 | 32 | ------------------------------------------------------------------------------ 33 | 34 | local tensl repeatdecl() 35 | var x = 1 36 | var x = 2 37 | return x 38 | end 39 | 40 | test.eq(repeatdecl(), 2) 41 | 42 | ------------------------------------------------------------------------------ 43 | 44 | local tensl multiassgn() 45 | var x : Lib.num 46 | var y : Lib.num 47 | x,y = 1,2 48 | return y 49 | end 50 | 51 | test.eq(multiassgn(), 2) 52 | 53 | ------------------------------------------------------------------------------ 54 | -------------------------------------------------------------------------------- /tenslang/tests/functions.t: -------------------------------------------------------------------------------- 1 | local test = require 'tests.test' 2 | 3 | import 'tenslang.tenslang' 4 | 5 | local Lib = require 'tenslang.tenslib' 6 | 7 | ------------------------------------------------------------------------------ 8 | 9 | -- simple call with simple return value 10 | 11 | local tensl ret0() 12 | return 0 13 | end 14 | 15 | local tensl ret1using0() 16 | return ret0() + 1 17 | end 18 | 19 | test.eq(Lib.arrow({}, Lib.num), ret0:gettype()) 20 | test.eq(Lib.arrow({}, Lib.num), ret1using0:gettype()) 21 | test.eq( ret1using0(), 1) 22 | test.eq( ret0(), 0) 23 | test.eq( ret1using0(), 1) 24 | 25 | ------------------------------------------------------------------------------ 26 | 27 | local tensl transpose2d( m : Lib.mat2 ) 28 | return { { m[0,0], m[1,0] }, 29 | { m[0,1], m[1,1] } } 30 | end 31 | 32 | local tensl testtranspose() 33 | Lib.assert( transpose2d({{1,2},{3,4}}) == {{1,3},{2,4}} ) 34 | end 35 | 36 | test.eq(Lib.arrow(Lib.mat2, Lib.mat2), transpose2d:gettype()) 37 | test.eq(Lib.arrow({},{}), testtranspose:gettype()) 38 | testtranspose() 39 | 40 | local tensl retpair() 41 | return 1.2, 3.4 42 | end 43 | 44 | local tensl pair_to_vec() 45 | var x : Lib.vec2 46 | x[0], x[1] = retpair() 47 | return x 48 | end 49 | 50 | test.eq(Lib.arrow({}, {Lib.num, Lib.num}), retpair:gettype()) 51 | test.eq(Lib.arrow({},Lib.vec2), pair_to_vec:gettype()) 52 | test.aeq(pair_to_vec(), {1.2, 3.4}) 53 | 54 | ------------------------------------------------------------------------------ 55 | 56 | local tensl reti() 57 | return 10,10 58 | end 59 | 60 | local tensl castret() 61 | var x : Lib.num 62 | var y : Lib.num 63 | x,y = reti() 64 | return x/3,y/3 65 | end 66 | 67 | test.eq(Lib.arrow({}, {Lib.num, Lib.num}), reti:gettype()) 68 | test.eq(Lib.arrow({},{Lib.num,Lib.num}), castret:gettype()) 69 | test.aeq({castret()}, {10.0/3.0, 10.0/3.0}) 70 | 71 | 72 | 73 | -------------------------------------------------------------------------------- /tenslang/tests/isct_predicates.t: -------------------------------------------------------------------------------- 1 | require 'examples.isct_predicates' -------------------------------------------------------------------------------- /tenslang/tests/tensor.t: -------------------------------------------------------------------------------- 1 | local test = require 'tests.test' 2 | 3 | import 'tenslang.tenslang' 4 | 5 | local Lib = require 'tenslang.tenslib' 6 | 7 | ------------------------------------------------------------------------------ 8 | 9 | local tensl retvec() 10 | return { 1.0,2.0,3.0,4.0 } 11 | end 12 | 13 | test.eq(Lib.arrow({}, Lib.vec4), retvec:gettype()) 14 | test.aeq( retvec(), {1,2,3,4}) 15 | 16 | 17 | local tensl retid3() 18 | return { {1,0,0}, {0,1,0}, {0,0,1} } 19 | end 20 | 21 | test.eq(Lib.arrow({}, Lib.matrix(Lib.num, 3,3)), retid3:gettype()) 22 | 23 | test.rec_aeq( retid3(), {{1,0,0},{0,1,0},{0,0,1}}) 24 | 25 | ------------------------------------------------------------------------------ 26 | 27 | ------------------------------------------------------------------------------ 28 | 29 | local tensl getmat_entry( m : Lib.mat2, i:Lib.num, j:Lib.num ) 30 | return m[i,j] 31 | end 32 | 33 | test.eq(Lib.arrow({Lib.mat2, Lib.num, Lib.num},Lib.num), 34 | getmat_entry:gettype()) 35 | test.eq(getmat_entry({{1,2},{3,4}}, 0, 0), 1) 36 | test.eq(getmat_entry({{1,2},{3,4}}, 0, 1), 2) 37 | test.eq(getmat_entry({{1,2},{3,4}}, 1, 0), 3) 38 | test.eq(getmat_entry({{1,2},{3,4}}, 1, 1), 4) 39 | 40 | local tensl slicecol( m : Lib.mat2, i:Lib.num ) 41 | return { m[0,i], m[1,i] } 42 | end 43 | 44 | test.eq(Lib.arrow({Lib.mat2, Lib.num},Lib.vec2), slicecol:gettype()) 45 | test.aeq(slicecol({{1,2},{3,4}}, 0), {1,3}) 46 | test.aeq(slicecol({{1,2},{3,4}}, 1), {2,4}) 47 | 48 | 49 | ------------------------------------------------------------------------------ 50 | 51 | ------------------------------------------------------------------------------ 52 | -------------------------------------------------------------------------------- /tenslang/tests/tensor_index.t: -------------------------------------------------------------------------------- 1 | local test = require 'tests.test' 2 | 3 | import 'tenslang.tenslang' 4 | 5 | local Lib = require 'tenslang.tenslib' 6 | 7 | ------------------------------------------------------------------------------ 8 | 9 | local idmat3 = Lib.Constant(Lib.mat3, {{1,0,0},{0,1,0},{0,0,1}}) 10 | local idmat2 = Lib.Constant(Lib.mat2, {{1,0},{0,1}}) 11 | 12 | local tensl tarith() 13 | var x = {1.0,2.0,3.0} 14 | var y = {0.2,0.4,0.6} 15 | 16 | -- scaling 17 | var s1 = :[i] 0.2*x[i] 18 | Lib.assert(s1 == 0.2*x) 19 | 20 | -- vec vec dot 21 | var dot = +[i] x[i] * y[i] 22 | Lib.assert(dot == 0.2 + 0.8 + 1.8) 23 | 24 | -- mat vec mult 25 | var multx = :[i] +[j] idmat3[i,j] * x[j] 26 | var multy = :[i] +[j] idmat3[i,j] * y[j] 27 | Lib.assert(multx == x) 28 | Lib.assert(multy == y) 29 | 30 | var M = {{1,2,3},{4,5,6}} 31 | -- transpose 32 | var Mt = :[i,j] M[j,i] 33 | Lib.assert(Mt == {{1,4},{2,5},{3,6}}) 34 | 35 | -- inner product 36 | var p = {1,2} 37 | var inner = +[i,j] p[i] * M[i,j] * x[j] 38 | var innerT = +[i,j] x[i] * Mt[i,j] * p[j] 39 | Lib.assert(inner == innerT) 40 | Lib.assert(inner == 78) 41 | 42 | -- triple product? (can't do without shuffle) 43 | 44 | -- outer product 45 | var b1 = { 1,1} 46 | var b2 = {-1,1} 47 | var prod1 = (:[i,j] b1[i]*b1[j]) + (:[i,j] b2[i]*b2[j]) 48 | var prod2 = :[i,j] b1[i]*b1[j] + b2[i]*b2[j] 49 | Lib.assert(prod1 == prod2) 50 | Lib.assert(prod2 == 2.0*idmat2) 51 | 52 | -- mat mat mult 53 | var mmult = :[i,j] +[k] idmat2[i,k] * M[k,j] 54 | Lib.assert(mmult == M) 55 | 56 | -- trace 57 | var outerX = :[i,j] x[i]*x[j] 58 | var traceX = +[i] outerX[i,i] 59 | var normX = +[i] x[i]*x[i] 60 | Lib.assert(traceX == normX) 61 | 62 | var A = :[i,j] +[k] Mt[i,k] * M[k,j] 63 | 64 | -- matrix 2-norm 65 | var frobenius = +[i,j] A[i,j]*A[i,j] 66 | 67 | -- collapse columns 68 | var v = +[i] :[j] M[j,i] 69 | Lib.assert(v == {6,15}) 70 | 71 | -- replicate matrix in a funny way 72 | var R = :[i] :[j] A[i,j] 73 | Lib.assert(R == A) 74 | end 75 | 76 | tarith() 77 | 78 | local tensl indexintoexpr() 79 | var x = {1.0,2.0,3.0} 80 | var y = {0.2,0.4,0.6} 81 | 82 | return (x + 5*y)[1] 83 | end 84 | 85 | test.eq(indexintoexpr(), 4) 86 | 87 | 88 | -------------------------------------------------------------------------------- /tenslang/tests/tensorarith.t: -------------------------------------------------------------------------------- 1 | local test = require 'tests.test' 2 | 3 | import 'tenslang.tenslang' 4 | 5 | local Lib = require 'tenslang.tenslib' 6 | 7 | ------------------------------------------------------------------------------ 8 | 9 | local tensl darith() 10 | var id = {{1.0,0},{0,1}} 11 | Lib.assert(1.5*id == {{1.5,0},{0,1.5}}) 12 | 13 | var ones = {{1,1},{1,1}} 14 | Lib.assert( id + ones == {{2,1},{1,2}} ) 15 | 16 | Lib.assert(id/2 == {{0.5,0},{0,0.5}}) 17 | Lib.assert(-id ~= id) 18 | Lib.assert(-id == {{-1,0},{0,-1}}) 19 | 20 | var abool = {true,true,false,false} 21 | var bbool = {true,false,true,false} 22 | Lib.assert( (not abool and not bbool) == not(abool or bbool) ) 23 | Lib.assert( not not abool == abool) 24 | Lib.assert( (abool and bbool) == {true, false, false, false}) 25 | Lib.assert( (abool or bbool) == {true, true, true, false}) 26 | end 27 | 28 | darith() 29 | -------------------------------------------------------------------------------- /tenslang/tests/trivial.t: -------------------------------------------------------------------------------- 1 | local test = require 'tests.test' 2 | 3 | import 'tenslang.tenslang' 4 | 5 | local Lib = require 'tenslang.tenslib' 6 | 7 | ------------------------------------------------------------------------------ 8 | 9 | local tensl noop() end 10 | 11 | test.eq(Lib.arrow({},{}), noop:gettype()) 12 | test.eq(nil, noop()) 13 | 14 | ------------------------------------------------------------------------------ 15 | 16 | local tensl ret0() 17 | return 0 18 | end 19 | 20 | test.eq(Lib.arrow({},Lib.num), ret0:gettype()) 21 | test.eq(ret0(), 0) 22 | 23 | ------------------------------------------------------------------------------ 24 | 25 | local tensl ret01() 26 | return 0, 1 27 | end 28 | 29 | test.eq(Lib.arrow({},{Lib.num, Lib.num}), ret01:gettype()) 30 | test.aeq( {ret01()} , {0,1} ) 31 | 32 | ------------------------------------------------------------------------------ 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /typelang/.gitignore: -------------------------------------------------------------------------------- 1 | 2 | .DS_Store 3 | *.txt 4 | 5 | coverage_src 6 | 7 | -------------------------------------------------------------------------------- /typelang/README.md: -------------------------------------------------------------------------------- 1 | 2 | # Typelang 3 | 4 | Typelang is a demonstration language using the Lua/Terra compiler 5 | infrastructure. 6 | 7 | ## Quick Setup 8 | 9 | Add the [`bin/`](bin) directory to your `PATH` environment variable, so that the [`bin/typel`](bin/typel) script is accessible. To run a Typelang script named `hello42.t`, just execute, 10 | ```typel hello42.t``` 11 | Here's a listing for `hello42.t` that you can try out. It should print `42` to the console. 12 | ``` 13 | import 'typelang.typelang' 14 | 15 | local typel getanswer() return 21 + 21 end 16 | 17 | print(getanswer()) 18 | ``` 19 | 20 | ## More Details 21 | 22 | See the [full manual](docs/manual.md) for more information. 23 | 24 | ## Examples 25 | 26 | See the [examples](examples) directory for example uses of Typelang. This is a good way to get a few ideas about how to proceed once you've got some code running. 27 | 28 | ## Tests 29 | 30 | You can run the testing suite by executing 31 | ``` 32 | ./runtests 33 | ``` 34 | -------------------------------------------------------------------------------- /typelang/bin/typel: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env terra 2 | 3 | -- print the number of and specific arguments (for debugging) 4 | --print(#arg, unpack(arg)) 5 | 6 | -- Extract options 7 | local options = {} 8 | local write_i = 1 9 | for i=1,#arg do 10 | if arg[i] == '--help' then 11 | options.help = true 12 | else 13 | arg[write_i] = arg[i] 14 | write_i = write_i + 1 15 | end 16 | end 17 | for k=write_i,#arg do arg[k] = nil end -- compaction of arg list 18 | 19 | local function printhelp() 20 | print([[ 21 | typel [options] source_file.t 22 | OPTIONS: 23 | --help print this help message]]) 24 | end 25 | 26 | if options.help then 27 | printhelp() 28 | os.exit(0) 29 | end 30 | 31 | -- Extract script filename to run 32 | local scriptname = nil 33 | if #arg == 0 then 34 | scriptname = nil 35 | elseif #arg == 1 then 36 | scriptname = arg[1] 37 | else 38 | printhelp() 39 | print('Error: found too many arguments') 40 | print('',unpack(arg)) 41 | os.exit(1) 42 | end 43 | 44 | if not scriptname then 45 | printhelp() 46 | print('Error: expected source file to run; got none') 47 | os.exit(1) 48 | end 49 | 50 | -- get the directory of this launcher script file using a Lua trick 51 | local launchbin_path = debug.getinfo(1,"S").source 52 | -- has the form '@path_to_bin_dir/typel', so... 53 | local bindir_path = launchbin_path:sub(2,-#'typel' - 1) 54 | local release_path = bindir_path..'../release' 55 | -- For debug 56 | --print(launchbin_path, bindir_path, release_path) 57 | 58 | -- Now that we have the path, we can use it to extend the Terra 59 | -- searchpath to find the typelang files 60 | package.terrapath = package.terrapath..';'..release_path..'/?.t' 61 | 62 | -- and we can launch the script safely now 63 | local blob, load_err = terralib.loadfile(scriptname) 64 | if load_err then 65 | print(load_err) 66 | os.exit(1) 67 | else 68 | blob() -- actually execute the script 69 | end 70 | 71 | 72 | 73 | -------------------------------------------------------------------------------- /typelang/covanalysis: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env terra 2 | 3 | local ffi = require 'ffi' 4 | 5 | local SCAN_DIR = 'release/typelang/src' 6 | local ANALYSIS_DIR = 'coverage_src' 7 | 8 | if ffi.os == 'Windows' then 9 | print('TODO windows') 10 | end 11 | local lscmd = "find "..SCAN_DIR 12 | local mkdircmd = "mkdir -p "..ANALYSIS_DIR 13 | 14 | -- load info 15 | local covinfo = loadfile("coverageinfo.lua")() 16 | 17 | -- ensure that we have somewhere to put the analyzed files 18 | os.execute(mkdircmd) 19 | 20 | local function genReport(filename, lineinfo) 21 | local srcfilename = filename 22 | local dstfilename = filename:gsub(SCAN_DIR, ANALYSIS_DIR) 23 | 24 | local srcfile = io.open(srcfilename, 'r') 25 | local dstfile = io.open(dstfilename, 'w') 26 | 27 | local linenum = 1 28 | for line in srcfile:lines() do 29 | local nvisits = lineinfo[linenum] 30 | linenum = linenum + 1 31 | 32 | -- If we visited this line, then output count 33 | if nvisits then 34 | dstfile:write(string.format("%5d: ",nvisits)) 35 | -- Filter out lines we couldn't possibly visit 36 | elseif 37 | string.match(line,"^ *%-%-") or -- comments 38 | string.match(line,"^ *$") or -- blank lines 39 | -- token only lines 40 | string.match(line,"^ *end *$") or 41 | string.match(line,"^ *} *$") or 42 | string.match(line,"^ *then *$") or 43 | string.match(line,"^ *else *$") or 44 | string.match(line,"^ *local function") or 45 | -- tokens with comments 46 | string.match(line,"^ *end *%-%-$") or 47 | string.match(line,"^ *} *%-%-") or 48 | string.match(line,"^ *then *%-%-") or 49 | string.match(line,"^ *else *%-%-") 50 | -- random other lines 51 | --string.match(line,"^ *end%) *$") 52 | then 53 | dstfile:write(" : ") 54 | else 55 | dstfile:write("#####: ") 56 | end 57 | 58 | dstfile:write(line) 59 | dstfile:write("\n") 60 | end 61 | 62 | srcfile:close() 63 | dstfile:close() 64 | end 65 | 66 | -- Scan all the source files 67 | for line in io.popen(lscmd):lines() do 68 | if ffi.os == "Windows" then error('TODO windows') end 69 | local filename = line:match("^("..SCAN_DIR.."/.*%.t)$") 70 | 71 | -- filter out some files 72 | if filename and filename:match('coverage%.t') then filename = nil end 73 | 74 | if filename then 75 | local lookupname = '@./'..filename 76 | local lineinfo = covinfo[lookupname] 77 | if lineinfo then 78 | genReport(filename, lineinfo) 79 | else 80 | print('NO COVERAGE INFO FOR '..filename) 81 | end 82 | end 83 | end 84 | 85 | 86 | --[[ 87 | local i = 1 88 | for l in terralib:lines() do 89 | local ntimes = lineinfo[i] 90 | 91 | if ntimes then 92 | io.write(string.format("%5d: ",ntimes)) 93 | else 94 | if string.match(l,"^ *%-%-") or 95 | string.match(l,"^ *$") or 96 | string.match(l,"^ *end *$") or 97 | string.match(l,"^ *else *$") or 98 | string.match(l,"^ *local function") then 99 | io.write(" : ") 100 | else 101 | io.write("#####: ") 102 | end 103 | end 104 | io.write(l) 105 | io.write("\n") 106 | i = i + 1 107 | end 108 | ]] -------------------------------------------------------------------------------- /typelang/docs/buildhtml: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env terra 2 | 3 | local whichhandle = io.popen('which pandoc') 4 | local whichoutput = whichhandle:read("*a") 5 | whichhandle:close() 6 | local pandoc_exists = #whichoutput > 0 7 | 8 | if not pandoc_exists then 9 | print([[ 10 | Could not build html documentation because pandoc is not installed. 11 | ]]) 12 | return -- early exit this script 13 | end 14 | 15 | 16 | os.execute("pandoc manual.md -o manual.html".. 17 | " -f markdown -t html".. 18 | " --standalone --table-of-contents --toc-depth=3".. 19 | " --title-prefix='Typelang Manual' ".. 20 | " -c style.css".. 21 | " --template=template.html" 22 | ) 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /typelang/docs/template.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | $for(author-meta)$ 8 | 9 | $endfor$ 10 | $if(date-meta)$ 11 | 12 | $endif$ 13 | $if(title-prefix)$$title-prefix$ - $endif$$pagetitle$ 14 | 15 | $if(quotes)$ 16 | 17 | $endif$ 18 | $if(highlighting-css)$ 19 | 22 | $endif$ 23 | $for(css)$ 24 | 25 | $endfor$ 26 | $if(math)$ 27 | $math$ 28 | $endif$ 29 | $for(header-includes)$ 30 | $header-includes$ 31 | $endfor$ 32 | 33 | 34 |
35 | $for(include-before)$ 36 | $include-before$ 37 | $endfor$ 38 | $if(title)$ 39 |
40 |

$title$

41 | $if(subtitle)$ 42 |

$subtitle$

43 | $endif$ 44 | $for(author)$ 45 |

$author$

46 | $endfor$ 47 | $if(date)$ 48 |

$date$

49 | $endif$ 50 |
51 | $endif$ 52 | $if(toc)$ 53 |
54 | $toc$ 55 |
56 | $endif$ 57 | $body$ 58 | $for(include-after)$ 59 | $include-after$ 60 | $endfor$ 61 |
62 | 63 | -------------------------------------------------------------------------------- /typelang/example_c/Makefile: -------------------------------------------------------------------------------- 1 | 2 | 3 | # Assume we're on some Linux variant by default 4 | PLATFORM:=Linux 5 | # but try to detect other situations of interest 6 | ifeq ($(OS),Windows_NT) 7 | $(error Windows unsupported) 8 | else 9 | UNAME:=$(shell uname -s) 10 | ifeq ($(UNAME),Darwin) 11 | PLATFORM:=OSX 12 | endif 13 | endif 14 | 15 | $(warning Detected platform as $(PLATFORM)) 16 | 17 | # Try to find Terra 18 | FIND_TERRA_BIN:=$(shell which terra) 19 | TERRA_ROOT:=$(patsubst %/bin/terra,%,$(FIND_TERRA_BIN)) 20 | ifeq ($(FIND_TERRA_BIN),) 21 | $(error Could not find 'terra' executable on your PATH) 22 | endif 23 | 24 | $(warning Detected Terra directory location as $(TERRA_ROOT)) 25 | 26 | 27 | # Tools to use 28 | TYPEL:=../bin/typel 29 | 30 | 31 | # Setup the flags and options 32 | CFLAGS:= 33 | # add include directory 34 | CFLAGS:=$(CFLAGS) -I$(TERRA_ROOT)/include/ 35 | # add library directory and flags 36 | CFLAGS:=$(CFLAGS) -L$(TERRA_ROOT) -lterra 37 | # add rpath 38 | CFLAGS:=$(CFLAGS) -Wl,-rpath,$(TERRA_ROOT) 39 | 40 | ifeq ($(PLATFORM),OSX) 41 | CFLAGS:=$(CFLAGS) -pagezero_size 10000 -image_base 100000000 42 | endif 43 | 44 | $(warning CFLAGS are $(CFLAGS)) 45 | 46 | 47 | all: embedded static_call 48 | 49 | embedded: embedded_main.c 50 | $(CC) embedded_main.c -o embedded $(CFLAGS) 51 | 52 | static_func.o static_func.h: static_build.t 53 | $(TYPEL) static_build.t 54 | 55 | static_call: static_func.o static_func.h static_call_main.c 56 | $(CC) static_call_main.c static_func.o -o static_call 57 | 58 | 59 | 60 | clean: 61 | -@rm embedded 62 | -@rm static_call 63 | -@rm static_func.o 64 | -@rm static_func.h 65 | 66 | -------------------------------------------------------------------------------- /typelang/example_c/README.md: -------------------------------------------------------------------------------- 1 | Thanks to being built on the Terra/Lua infrastructure, Typelang comes equipped with some easy ways to be used in new or existing C/C++ programs. The examples in this directory demonstrate the two primary strategies for running DSL code from a host C program. 2 | 3 | 4 | # Embedding an interpreter for Typelang in a C program 5 | 6 | This strategy is entirely based on Lua/Terra facilities for embedding. So it doesn't really involve Typelang specifically. At program initialization, we build a Lua environment using 7 | 8 | ``` 9 | lua_State * L = luaL_newstate(); 10 | luaL_openlibs(L); 11 | terra_init(L); 12 | ``` 13 | 14 | (more details and context are in `embedded_main.c`) 15 | 16 | The only slight detail is that as always, the `package.terrapath` variable must be extended with the location of the Typelang [`release/`](../release) directory. For example, this can be accomplished by simply executing an appropriate string command. 17 | ``` 18 | const char *pathextend = 19 | "package.terrapath = package.terrapath..';release/?.t'"; 20 | if(terra_dostring(L, pathextend)) { 21 | fprintf(stderr, "%s\n", lua_tostring(L, -1)); 22 | lua_close(L); 23 | exit(1); 24 | } 25 | ``` 26 | 27 | To read more about how to manage the Lua/Terra interpreter from C, see the appropriate documentation, for instance the overview of the [Lua C API](http://www.lua.org/pil/24.html) and overview of the [Terra C API](http://terralang.org/api.html#c-api). 28 | 29 | 30 | 31 | # Generating Object files for a static build process 32 | 33 | This strategy is very similar to a built in Terra facility, but is part of the Typelang API proper. Rather than embed a whole Lua/Terra interpreter into a project, we generate object files that can be statically compiled into the project. This does mean that we have to give up on using any Lua meta-programming, but may be much easier to incoporate, since there are fewer runtime dependencies involved. 34 | 35 | In this strategy, we execute a Lua/Terra Typelang script during the build process. For instance, if we want to generate code for our hello42 function, we would use a build script like: 36 | 37 | ``` 38 | import 'typelang.typelang' 39 | local TL = require 'typelang.typelib' 40 | 41 | local typel hello42() return 21 + 21 end 42 | 43 | -- Save out the functions 44 | TL.compiletofile("hello42.o","hello42.h",{ hello42 = hello42 }) 45 | ``` 46 | 47 | This seems very much like our existing scripts, except we end it with a call to `TL.compiletofile(...)`. This function call is passed the path for an object file to generate, path for a header file to generate (can be left `nil` to omit header file generation) and a table of Typelang functions you want to expose. 48 | 49 | Running this script will produce a header file like the following 50 | 51 | ``` 52 | #ifndef _HELLO_42_H_ 53 | #define _HELLO_42_H_ 54 | 55 | #include "stdbool.h" 56 | #include "stdint.h" 57 | 58 | 59 | int32_t hello42(); 60 | 61 | #endif 62 | ``` 63 | 64 | Then, we can use this function from a C program like 65 | 66 | ``` 67 | #include 68 | #include "hello42.h" 69 | 70 | int main(int argc, char ** argv) { 71 | int answer = hello42(); 72 | printf("%d\n", answer); 73 | 74 | return 0; 75 | } 76 | ``` 77 | 78 | If we compile this source together with the generated `hello42.o` object code, then we'll get a program that prints `42`. 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | -------------------------------------------------------------------------------- /typelang/example_c/embedded_main.c: -------------------------------------------------------------------------------- 1 | 2 | // embedded_main.c 3 | #include 4 | #include "terra/terra.h" 5 | 6 | int main(int argc, char ** argv) { 7 | lua_State * L = luaL_newstate(); //create a plain lua state 8 | luaL_openlibs(L); //initialize its libraries 9 | //initialize the terra state in lua 10 | terra_init(L); 11 | 12 | // we can execute terra scripts from within the C program now 13 | // First, let's extend the terrapath 14 | const char *pathextend = 15 | "package.terrapath = package.terrapath.." 16 | // extend the path so that we can run both in the example directory 17 | "';../release/?.t'.." 18 | // and in the directory one up which tests are run from 19 | "';release/?.t'"; 20 | if(terra_dostring(L, pathextend)) { 21 | printf("pathextend failed\n"); 22 | fprintf(stderr, "%s\n", lua_tostring(L, -1)); 23 | lua_pop(L, 1); // pop error message from the stack 24 | lua_close(L); 25 | return 1; 26 | } 27 | 28 | // then, we can go ahead and execute the hello42 program 29 | const char *scriptstring = 30 | "import 'typelang.typelang'\n" 31 | "\n" 32 | "typel hello42() return 21 + 21 end\n" 33 | "\n" 34 | "print(hello42())\n" 35 | "assert(hello42() == 42)\n" 36 | "\n"; 37 | if(terra_dostring(L, scriptstring)) { 38 | printf("script failed\n"); 39 | fprintf(stderr, "%s\n", lua_tostring(L, -1)); 40 | lua_pop(L, 1); // pop error message from the stack 41 | } 42 | 43 | // finally, let's check to make sure that error reporting 44 | // is working ok 45 | const char *errscript = "error('causing an intentional error')"; 46 | if(terra_dostring(L, errscript)) { 47 | fprintf(stderr, "%s\n", lua_tostring(L, -1)); 48 | lua_pop(L, 1); // pop error message from the stack 49 | } 50 | 51 | lua_close(L); 52 | return 0; 53 | } -------------------------------------------------------------------------------- /typelang/example_c/static_build.t: -------------------------------------------------------------------------------- 1 | 2 | import 'typelang.typelang' 3 | 4 | local TL = require 'typelang.typelib' 5 | 6 | local typel getanswer() 7 | return 21 + 21 8 | end 9 | 10 | local typel signedfrac( x : TL.double ) 11 | return x - TL.double(TL.int32(x)) 12 | end 13 | 14 | -- here, we save out the functions 15 | TL.compiletofile("static_func.o","static_func.h",{ 16 | getanswer = getanswer, 17 | signedfrac = signedfrac, 18 | }) 19 | 20 | -------------------------------------------------------------------------------- /typelang/example_c/static_call_main.c: -------------------------------------------------------------------------------- 1 | 2 | // static_call_main.c 3 | #include 4 | #include "static_func.h" 5 | 6 | int main(int argc, char ** argv) { 7 | 8 | int answer = getanswer(); 9 | 10 | double fraca = signedfrac(3.2); 11 | double fracb = signedfrac(-2.3); 12 | 13 | printf("answer should be 42; was %d\n", answer); 14 | printf("signedfrac 3.2 should yield 0.2; was %f\n", fraca); 15 | printf("signedfrac -2.3 should yield -0.3; was %f\n", fracb); 16 | 17 | return 0; 18 | } -------------------------------------------------------------------------------- /typelang/release/typelang/src/builtins.t: -------------------------------------------------------------------------------- 1 | local B = {} 2 | package.loaded["typelang.src.builtins"] = B 3 | 4 | local A = require 'typelang.src.ast' 5 | local T = require 'typelang.src.types' 6 | 7 | local LangLib = require 'typelang.typelib' 8 | 9 | local C = require 'typelang.src.c' 10 | 11 | ------------------------------------------------------------------------------- 12 | --[[ Builtin Prototype ]]-- 13 | ------------------------------------------------------------------------------- 14 | 15 | local Builtin = {} 16 | Builtin.__index = Builtin 17 | 18 | local function isbuiltin(obj) return getmetatable(obj) == Builtin end 19 | B.isbuiltin = isbuiltin 20 | 21 | -- only define builtins in this file 22 | local function NewBuiltin(name, typ, genfunc) 23 | assert(type(name) == 'string') 24 | assert(T.istype(typ) and typ:isarrow()) 25 | 26 | local bi = setmetatable({ 27 | name = name, 28 | type = typ, 29 | genfunc = genfunc, 30 | }, Builtin) 31 | 32 | return bi 33 | end 34 | 35 | ------------------------------------------------------------------------------- 36 | --[[ Generic Built-in mechanism for Terra code ]]-- 37 | ------------------------------------------------------------------------------- 38 | 39 | function B.extern(name, typesig, func) 40 | return NewBuiltin(name, typesig, function(callast, ...) 41 | local args = {...} 42 | return `func( [args] ) 43 | end) 44 | end 45 | 46 | ------------------------------------------------------------------------------- 47 | --[[ Specific Builtins ]]-- 48 | ------------------------------------------------------------------------------- 49 | 50 | local function terror_body (file, line) 51 | local prelude = file..':'..tostring(line)..': Assert Failed\n' 52 | return quote do 53 | C.fprintf(C.stderr, prelude) 54 | C.exit(1) 55 | end end 56 | end 57 | 58 | B.assert = NewBuiltin('assert', T.arrow(T.bool,{}), 59 | function(callast, test) 60 | return quote 61 | if not test then 62 | [terror_body(callast.filename, callast.linenumber)] 63 | end 64 | end 65 | end) 66 | 67 | -- type could be difficult here? 68 | --B.print = NewBuiltin('print', T.arrow()) 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | -------------------------------------------------------------------------------- /typelang/release/typelang/src/c.t: -------------------------------------------------------------------------------- 1 | 2 | 3 | local c_blob = terralib.includecstring [[ 4 | #include "stdlib.h" 5 | #include "stdio.h" 6 | 7 | FILE * __get__stdout() { return stdout; } 8 | FILE * __get__stdin() { return stdin; } 9 | FILE * __get__stderr() { return stderr; } 10 | 11 | ]] 12 | 13 | rawset(c_blob, 'stdout', c_blob.__get__stdout()) 14 | rawset(c_blob, 'stdin', c_blob.__get__stdin()) 15 | rawset(c_blob, 'stderr', c_blob.__get__stderr()) 16 | 17 | package.loaded["typelang.src.c"] = c_blob 18 | return c_blob 19 | -------------------------------------------------------------------------------- /typelang/release/typelang/src/coverage.t: -------------------------------------------------------------------------------- 1 | 2 | if not rawget(_G,'COVERAGE_MODULE_HAS_BEEN_INSTALLED') then 3 | local ffi = require 'ffi' 4 | 5 | _G['COVERAGE_MODULE_HAS_BEEN_INSTALLED'] = true 6 | local coverageloader, err = loadfile("coverageinfo.lua") 7 | --print('FOUND COVERAGE ', coverageloader, err) 8 | 9 | local filetable = coverageloader and coverageloader() or {} 10 | local function dumplineinfo() 11 | local F = io.open("coverageinfo.lua","w") 12 | F:write("return {\n") 13 | for filename,linetable in pairs(filetable) do 14 | F:write("['"..filename.."']={\n") 15 | for linenum,count in pairs(linetable) do 16 | F:write("["..linenum.."]="..count..",\n") 17 | end 18 | F:write("},\n") 19 | end 20 | F:write("}\n") 21 | F:close() 22 | end 23 | local function debughook(event) 24 | local info = debug.getinfo(2,"Sl") 25 | -- exclude for instance, metaprogrammed lua code 26 | if string.sub(info.source, 1,1) == '@' then 27 | local linetable = filetable[info.source] 28 | if not linetable then 29 | linetable = {} 30 | filetable[info.source] = linetable 31 | end 32 | linetable[info.currentline] = (linetable[info.currentline] or 0) + 1 33 | end 34 | end 35 | debug.sethook(debughook,"l") 36 | -- make a fake ffi object that causes dumplineinfo to be called when 37 | -- the lua state is removed 38 | ffi.cdef [[ 39 | typedef struct {} __linecoverage; 40 | ]] 41 | ffi.metatype("__linecoverage", { __gc = dumplineinfo } ) 42 | _G[{}] = ffi.new("__linecoverage") 43 | end 44 | -------------------------------------------------------------------------------- /typelang/release/typelang/src/pratt.t: -------------------------------------------------------------------------------- 1 | 2 | local P = {} 3 | 4 | --same tokentypes as lexer, duplicated here for convience 5 | P.name = terralib.languageextension.name 6 | P.string = terralib.languageextension.string 7 | P.number = terralib.languageextension.number 8 | P.eof = terralib.languageextension.eof 9 | P.default = terralib.languageextension.default 10 | 11 | --a parser for a language is defined by a table of functions, one for each non-termina 12 | --in the language (e.g expression,statement, etc.) 13 | 14 | --these functions are either (1) raw recursive decent parsers 15 | -- or (2) Pratt parser objects 16 | 17 | --pratt parser objects behave like functions 18 | --but their behavior is defined by adding rules for what to do when 19 | --a prefix or suffix is found 20 | --futhermore, when using a pratt parser object, you can specify 21 | --a precedence such that the parser will only parse expressions 22 | --with precedence _higher_ than that. 23 | --see tests/lib/pratttest.t for examples of how to use this interface 24 | --to parse common patters 25 | 26 | P.pratt = {} 27 | function P.Pratt() 28 | return setmetatable({ 29 | infixtable = {}; 30 | prefixtable = {}; 31 | },{ __index = P.pratt, __call = P.pratt.__call }) 32 | end 33 | 34 | 35 | --define a rule for infix operators like '+' 36 | --precidence is a numeric precedence 37 | --tokentype is a lexer token type (see embeddinglanguages.html) 38 | --rule is a function: function(parser,lhs) ... end 39 | --it is given the parser object and the AST value for the lhs of the expression. 40 | --it should parse and return the AST value for current expression 41 | --P.default can be used to define a rule that fires when no other rule applies 42 | function P.pratt:infix(tokentype,prec,rule) 43 | if self.infixtable[tokentype] then 44 | error("infix rule for "..tostring(tokentype).." already defined") 45 | end 46 | self.infixtable[tokentype] = { 47 | prec = prec; 48 | rule = rule; 49 | } 50 | return self 51 | end 52 | 53 | --define a prefix rule 54 | --rule is a function: function(parser) ... end, that takes the parser object 55 | --and returns an AST for the expression 56 | 57 | function P.pratt:prefix(tokentype,rule) 58 | if self.prefixtable[tokentype] then 59 | error("prefix rule for "..tostring(tokentype).." already defined") 60 | end 61 | self.prefixtable[tokentype] = rule 62 | return self 63 | end 64 | 65 | P.defaultprefix = function(parser) 66 | parser:error("unexpected symbol") 67 | end 68 | --table-driven implementation, invoked when you call the Pratt parser object 69 | P.pratt.__call = function(pratt,parser,precortoken,fixity) 70 | local isleft = fixity == nil or fixity == "left" 71 | local limit 72 | if not precortoken then 73 | limit,isleft = 0,false 74 | elseif type(precortoken) == "number" then 75 | limit = precortoken 76 | else 77 | if not pratt.infixtable[precortoken] then 78 | error("precidence not defined for "..tostring(precortoken)) 79 | end 80 | limit = pratt.infixtable[precortoken].prec 81 | end 82 | local tt = parser:cur().type 83 | local prefixrule = pratt.prefixtable[tt] or pratt.prefixtable[P.default] or P.defaultprefix 84 | local results = { prefixrule(parser) } 85 | while true do 86 | tt = parser:cur().type 87 | local op = pratt.infixtable[tt] or pratt.infixtable[P.default] 88 | if not op or (isleft and op.prec <= limit) or (not isleft and op.prec < limit) then 89 | break 90 | end 91 | results = { op.rule(parser,unpack(results)) } 92 | end 93 | return unpack(results) 94 | end 95 | 96 | 97 | --create a parser 98 | --langtable is the table of non-terminal functions and/or pratt parser objects 99 | --lexer is the lexer object given by the language extension interface 100 | function P.Parser(langtable,lexer) 101 | local instance = {} 102 | for k,v in pairs(lexer) do 103 | if type(v) == "function" then 104 | instance[k] = function(self,...) return v(lexer,...) end 105 | elseif string.sub(k,1,1) ~= "_" then 106 | instance[k] = v 107 | end 108 | end 109 | for k,v in pairs(langtable) do 110 | if instance[k] then error("language nonterminal overlaps with lexer function "..k) end 111 | instance[k] = v 112 | end 113 | return instance 114 | end 115 | 116 | --create a parser and run a non-terminal in one go 117 | --nonterminal is the name (e.g. "expression") of the non-terminal to use as the starting point in the langtable 118 | function P.Parse(langtable,lexer,nonterminal) 119 | local self = P.Parser(langtable,lexer) 120 | return self[nonterminal](self) 121 | end 122 | 123 | 124 | return P -------------------------------------------------------------------------------- /typelang/release/typelang/typelang.t: -------------------------------------------------------------------------------- 1 | -- File to be imported, defining the language 2 | -- put import 'typelang.typelang' at the top of files 3 | 4 | -- shim in the coverage analysis 5 | --require 'typelang.src.coverage' 6 | 7 | local P = require 'typelang.src.parser' 8 | local Specializer = require 'typelang.src.specializer' 9 | local F = require 'typelang.src.functions' 10 | local Lib = require 'typelang.typelib' 11 | -- other passes and things? 12 | 13 | local function handleStatement(self, lexer) 14 | local ast, assigntuple = P.ParseStatement(lexer) 15 | local constructor = function(env_fn) 16 | if Lib._UNIT_TEST_PARSER then 17 | return ast 18 | elseif Lib._UNIT_TEST_SPECIALIZER then 19 | return Specializer.specialize(ast, env_fn()) 20 | else 21 | local decl_ast = Specializer.specialize(ast, env_fn()) 22 | return F.NewFunction { decl_ast = decl_ast } 23 | end 24 | end 25 | return constructor, assigntuple 26 | end 27 | 28 | local function handleExpression(self, lexer) 29 | local ast = P.ParseExpression(lexer) 30 | local constructor = function(env_fn) 31 | if Lib._UNIT_TEST_PARSER then 32 | return ast 33 | elseif Lib._UNIT_TEST_SPECIALIZER then 34 | return Specializer.specialize(ast, env_fn()) 35 | else 36 | local decl_ast = Specializer.specialize(ast, env_fn()) 37 | return F.NewFunction { decl_ast = decl_ast } 38 | end 39 | end 40 | return constructor 41 | end 42 | 43 | 44 | local typelanguage = { 45 | name = 'typelang', 46 | entrypoints = {'typel'}, 47 | keywords = { 48 | '_', -- always good to reserve the underscore for language use 49 | 'var', 50 | }, 51 | 52 | expression = handleExpression, 53 | statement = handleStatement, 54 | localstatement = handleStatement, 55 | } 56 | 57 | return typelanguage 58 | -------------------------------------------------------------------------------- /typelang/release/typelang/typelib.t: -------------------------------------------------------------------------------- 1 | local Lib = {} 2 | package.loaded["typelang.typelib"] = Lib 3 | 4 | ------------------------------------------------------------------------------- 5 | -- Policy: 6 | -- This file is responsible for the Lua <--> Typelang interface 7 | -- as exposed via the typelib object. 8 | -- As such, it's generally preferrable to define most functions 9 | -- elsewhere in the compiler and then just export them here. Though that's 10 | -- hardly a strict policy. 11 | -- However, it's definitely a bad idea to define functions in this file 12 | -- that are only for use internally by the compiler. 13 | ------------------------------------------------------------------------------- 14 | 15 | local T = require 'typelang.src.types' 16 | local B = require 'typelang.src.builtins' 17 | local F = require 'typelang.src.functions' 18 | 19 | ------------------------------------------------------------------------------- 20 | 21 | local ConstantProto = {} 22 | ConstantProto.__index = ConstantProto 23 | 24 | function Lib.isconstant(obj) return getmetatable(obj) == ConstantProto end 25 | 26 | function Lib.Constant(typ, val) 27 | if not T.istype(typ) or not typ:isvalue() then 28 | error('first argument to Constant must be a value type', 2) 29 | end 30 | if not T.checkluaval(val, typ) then 31 | error('second argument to Constant must be a value of the given type', 2) 32 | end 33 | 34 | local c = setmetatable({ 35 | _type = typ, 36 | _value = val, 37 | }, ConstantProto) 38 | return c 39 | end 40 | 41 | function ConstantProto:get() 42 | return self._value 43 | end 44 | 45 | function ConstantProto:gettype() 46 | return self._type 47 | end 48 | 49 | ------------------------------------------------------------------------------- 50 | 51 | -- selectively expose some parts of the type system to the user 52 | for _,name in ipairs({ 53 | "int32", 54 | "uint64", 55 | "bool", 56 | "float", 57 | "double", 58 | 59 | "arrow", 60 | 61 | "istype", 62 | }) do 63 | Lib[name] = T[name] 64 | end 65 | 66 | ------------------------------------------------------------------------------- 67 | 68 | -- selectively expose some of the built-ins 69 | Lib.assert = B.assert 70 | 71 | -- generic way for clients to extend the language with external 72 | -- functions as a kind of custom built-in 73 | Lib.extern = B.extern 74 | 75 | ------------------------------------------------------------------------------- 76 | 77 | Lib.isfunction = F.isfunction 78 | Lib.compiletofile = F.compiletofile 79 | 80 | 81 | 82 | 83 | -------------------------------------------------------------------------------- /typelang/runtests: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env terra 2 | local ffi = require 'ffi' 3 | 4 | if ffi.os == 'Windows' then 5 | print('TODO windows') 6 | end 7 | local lscmd = "find tests" 8 | 9 | local color = { 10 | DEFAULT = '\27[39m', 11 | OKBLUE = '\27[34m', -- Blue 12 | OKGREEN = '\27[32m', -- Green 13 | WARNING = '\27[33m', -- Yellow 14 | FAIL = '\27[31m', -- Red 15 | } 16 | 17 | local passed = {} 18 | local badpassed = {} 19 | local failed = {} 20 | local disabled = {} 21 | 22 | local exclude = { 23 | ['tests/test.lua'] = true, 24 | } 25 | 26 | local disable_str = '--DISABLE-TEST' 27 | local function prefixmatch(str, prefix) 28 | return string.sub(str,1,#prefix) == prefix 29 | end 30 | local function is_disabled(filename) 31 | local handle = io.open(filename, "r") 32 | local line1 = handle:read() 33 | handle:close() 34 | 35 | local is_disabled = false 36 | if prefixmatch(line1,disable_str) then is_disabled = true end 37 | return is_disabled 38 | end 39 | 40 | 41 | 42 | print([[ 43 | ============================================================================== 44 | = Running tests... = 45 | ============================================================================]] 46 | ..'==') 47 | 48 | for line in io.popen(lscmd):lines() do 49 | if ffi.os == "Windows" then error('TODO windows') end 50 | local filename = line:match("^(tests/.*%.t)$") 51 | if filename then 52 | if is_disabled(filename) then 53 | table.insert(disabled, filename) 54 | else 55 | --print('running '..filename) 56 | local execstring = "./bin/typel " 57 | execstring = execstring..filename 58 | 59 | print(execstring) 60 | local success = os.execute(execstring) 61 | 62 | if success ~= 0 then 63 | table.insert(failed, filename) 64 | print(filename .. " "..color.FAIL.."FAILED"..color.DEFAULT) 65 | else 66 | table.insert(passed, filename) 67 | print(filename .. " "..color.OKGREEN.."PASSED"..color.DEFAULT) 68 | end 69 | end 70 | end 71 | end 72 | print() 73 | 74 | -- test whether the coverageanalysis exists 75 | local coverage_on = os.execute('test -f coverageinfo.lua') == 0 76 | if coverage_on then 77 | print('-- Assembling Coverage Analysis Report --') 78 | os.execute('./covanalysis') 79 | os.execute('rm coverageinfo.lua') 80 | end 81 | 82 | 83 | print([[ 84 | ----------------------------- Disabled tests -----------------------------]] 85 | ..'--') 86 | for i,e in ipairs(disabled) do print(e) end 87 | print() 88 | 89 | print([[ 90 | ------------------------------ Passed tests ------------------------------]] 91 | ..'--') 92 | for i,e in ipairs(passed) do print(e) end 93 | print() 94 | 95 | print([[ 96 | ------------------------------ Failed tests ------------------------------]] 97 | ..'--') 98 | for i,e in ipairs(failed) do print(e) end 99 | print() 100 | 101 | print('tests disabled: '..tostring(#disabled)) 102 | print('tests passed: '..tostring(#passed)) 103 | if #failed + #badpassed > 0 then 104 | print(color.FAIL..'tests failed: '..tostring(#failed + #badpassed).. 105 | color.DEFAULT) 106 | else 107 | print(color.OKGREEN..'All tests passed!'..color.DEFAULT) 108 | end 109 | -------------------------------------------------------------------------------- /typelang/tests/arithmetic.t: -------------------------------------------------------------------------------- 1 | local test = require 'tests.test' 2 | 3 | import 'typelang.typelang' 4 | 5 | local Lib = require 'typelang.typelib' 6 | 7 | ------------------------------------------------------------------------------ 8 | 9 | local typel int32arith() 10 | var x = 42 11 | var y : Lib.int32 = x 12 | Lib.assert(x-y == 0) 13 | Lib.assert(x+y == 84) 14 | 15 | x = 3 16 | y = 5 17 | Lib.assert(x*y == 15) 18 | Lib.assert(x/y == 0) 19 | Lib.assert(y/x == 1) 20 | 21 | Lib.assert((-x) * (-y) == x * y) 22 | Lib.assert(x <= y) 23 | Lib.assert(x < y) 24 | Lib.assert(y > x) 25 | Lib.assert(y >= x) 26 | Lib.assert(x ~= y) 27 | end 28 | 29 | int32arith() 30 | 31 | local typel uint64arith() 32 | var x : Lib.uint64 = 42 33 | var y : Lib.uint64 = x 34 | Lib.assert(x-y == 0) 35 | Lib.assert(x+y == 84) 36 | 37 | x = 3 38 | y = 5 39 | Lib.assert(x*y == 15) 40 | Lib.assert(x/y == 0) 41 | Lib.assert(y/x == 1) 42 | 43 | Lib.assert((-x) * (-y) == x * y) 44 | Lib.assert(x <= y) 45 | Lib.assert(x < y) 46 | Lib.assert(y > x) 47 | Lib.assert(y >= x) 48 | Lib.assert(x ~= y) 49 | 50 | Lib.assert(-x > 0) -- weird but true cause unsigned 51 | end 52 | 53 | uint64arith() 54 | 55 | local typel floatarith() 56 | var x = 42.0f 57 | var y : Lib.float = x 58 | Lib.assert(x-y == 0f) 59 | Lib.assert(x+y == 84f) 60 | 61 | x = 3.0f 62 | y = 5.0f 63 | Lib.assert(x*y == 15f) 64 | Lib.assert(x/y == 3.0f/5.0f) 65 | Lib.assert(y/x == 5.0f/3.0f) 66 | 67 | Lib.assert((-x) * (-y) == x * y) 68 | Lib.assert(x <= y) 69 | Lib.assert(x < y) 70 | Lib.assert(y > x) 71 | Lib.assert(y >= x) 72 | Lib.assert(x ~= y) 73 | end 74 | 75 | floatarith() 76 | 77 | local typel doublearith() 78 | var x = 42.0 79 | var y : Lib.double = x 80 | Lib.assert(x-y == 0) 81 | Lib.assert(x+y == 84) 82 | 83 | x = 3.0 84 | y = 5.0 85 | Lib.assert(x*y == 15) 86 | Lib.assert(x/y == 3.0/5.0) 87 | Lib.assert(y/x == 5.0/3.0) 88 | 89 | Lib.assert((-x) * (-y) == x * y) 90 | Lib.assert(x <= y) 91 | Lib.assert(x < y) 92 | Lib.assert(y > x) 93 | Lib.assert(y >= x) 94 | Lib.assert(x ~= y) 95 | end 96 | 97 | doublearith() 98 | 99 | local typel coercearith() 100 | var x = 42 101 | var y : Lib.double = x 102 | Lib.assert(x-y == 0) 103 | Lib.assert(x+y == 84) 104 | 105 | x = 3 106 | y = 5 107 | Lib.assert(x*y == 15) 108 | Lib.assert(x/y == 3.0/5.0) 109 | Lib.assert(y/x == 5.0/3.0) 110 | 111 | Lib.assert((-x) * (-y) == x * y) 112 | Lib.assert(x <= y) 113 | Lib.assert(x < y) 114 | Lib.assert(y > x) 115 | Lib.assert(y >= x) 116 | Lib.assert(x ~= y) 117 | end 118 | 119 | coercearith() 120 | 121 | local typel booltests() 122 | var x = true 123 | var y : Lib.bool = x 124 | Lib.assert(x and y == true) 125 | Lib.assert(x or y == true) 126 | Lib.assert(x == y) 127 | 128 | x = true 129 | y = false 130 | Lib.assert( (not x and not y) == not (x or y) ) 131 | Lib.assert(x ~= y) 132 | end 133 | 134 | booltests() 135 | 136 | --int32arith:printstats() 137 | --uint64arith:printstats() 138 | --floatarith:printstats() 139 | --doublearith:printstats() 140 | 141 | ------------------------------------------------------------------------------ 142 | 143 | ------------------------------------------------------------------------------ 144 | -------------------------------------------------------------------------------- /typelang/tests/ast_unit.t: -------------------------------------------------------------------------------- 1 | local test = require 'tests.test' 2 | -- call to maybe install coverage 3 | require 'typelang.typelang' 4 | 5 | local A = require 'typelang.src.ast' 6 | 7 | 8 | ------------------------------------ 9 | -- Cannot create abstract AST nodes 10 | test.fail(function() 11 | local exp = A.Expression:New() 12 | end, 'cannot create new abstract AST') 13 | 14 | --------------------------------------- 15 | -- Test creation of various AST nodes 16 | 17 | local somesym = A.NewSymbol('somesym') 18 | local somename = A.Name:New { value=somesym } 19 | local somenumber = A.Number:New { value=42 } 20 | local somestring = A.String:New { value='foobar' } 21 | local somebool = A.Bool:New { value=true } 22 | 23 | local someuop = A.UnaryOp:New { op='-', expr=somenumber } 24 | local somebop = A.BinaryOp:New { op='+', lhs=somenumber, rhs=somenumber } 25 | 26 | local somelookup = A.Lookup:New { 27 | base = somename, arg = somename, 28 | } 29 | local somecall = A.Call:New { base=somename, args={} } 30 | 31 | 32 | local someexpr = A.ExprStmt:New { expr = somebop } 33 | local someargdecl = A.ArgDecl:New { name=somesym } 34 | 35 | local somedo = A.DoStmt:New { body={someexpr} } 36 | local someasgn = A.Assignment:New { lvalues={somename}, rvalues={somebop} } 37 | local somedecl = A.DeclStmt:New { name=somesym } 38 | local somelet = A.Let:New { block={someexpr}, expr=someuop } 39 | local somefunc = A.Function:New { 40 | name='foo', 41 | args={someargdecl}, 42 | body={somedo}, 43 | rets={somebool}, 44 | } 45 | 46 | test.fail(function() 47 | local dummy = A.Function:New { args={someargdecl}, body={somedo} } 48 | end, 'Could not create AST because of bad shape') 49 | 50 | somefunc:printpretty() 51 | test.eq(somefunc:depth(), 5) 52 | test.eq(somefunc:size(), 8) 53 | 54 | A.NewCopyPass { 55 | passname = "typecheck", 56 | copymembers = {"annotation1"}, 57 | defaultvals = { 58 | node_type = 3, 59 | }, 60 | --verbose=true, 61 | } 62 | A.NewInertPass { 63 | passname='analysis', 64 | --verbose = true, 65 | } 66 | 67 | 68 | -------------------------------------------------------------------------------- /typelang/tests/c_build.t: -------------------------------------------------------------------------------- 1 | local test = require 'tests.test' 2 | 3 | import 'typelang.typelang' 4 | 5 | local Lib = require 'typelang.typelib' 6 | 7 | ------------------------------------------------------------------------------ 8 | 9 | -- This test is more complicated because it needs to invoke a Makefile build 10 | 11 | 12 | print("** ENSURE DIRECTORY IS CLEAN") 13 | assert(os.execute('cd example_c; make clean') == 0) 14 | print("** DO BUILD") 15 | assert(os.execute('cd example_c; make') == 0) 16 | print("** BUILD COMPLETED") 17 | 18 | -- now we need to check that the build produced working executables 19 | -- with the correct output 20 | 21 | local embedded = assert(io.popen('./example_c/embedded 2>&1', 'r')) 22 | local embedded_output = embedded:read('*all') 23 | test.eq(embedded_output,[[ 24 | [string ""]:1: causing an intentional error 25 | 42 26 | ]]) 27 | -- print anyway if test didn't fail 28 | print() 29 | print(embedded_output) 30 | embedded:close() 31 | 32 | local static_call = assert(io.popen('./example_c/static_call 2>&1', 'r')) 33 | local static_output = static_call:read('*all') 34 | test.eq(static_output,[[ 35 | answer should be 42; was 42 36 | signedfrac 3.2 should yield 0.2; was 0.200000 37 | signedfrac -2.3 should yield -0.3; was -0.300000 38 | ]]) 39 | -- print anyway if test didn't fail 40 | print() 41 | print(static_output) 42 | static_call:close() 43 | 44 | 45 | 46 | print("** CLEANING UP") 47 | assert(os.execute('cd example_c; make clean') == 0) 48 | print("** CLEANED UP") -------------------------------------------------------------------------------- /typelang/tests/constants.t: -------------------------------------------------------------------------------- 1 | local test = require 'tests.test' 2 | 3 | import 'typelang.typelang' 4 | 5 | local Lib = require 'typelang.typelib' 6 | 7 | ------------------------------------------------------------------------------ 8 | 9 | local ci = Lib.Constant(Lib.int32,1) 10 | local cf = Lib.Constant(Lib.float,1) 11 | local cd = Lib.Constant(Lib.double,1) 12 | local cb = Lib.Constant(Lib.bool,false) 13 | local typel retcs() 14 | return ci, cf, cd, cb 15 | end 16 | 17 | test.eq(Lib.arrow({},{Lib.int32, Lib.float, Lib.double, Lib.bool}), 18 | retcs:gettype()) 19 | test.aeq( {retcs()}, {1,1,1,false}) 20 | -------------------------------------------------------------------------------- /typelang/tests/control.t: -------------------------------------------------------------------------------- 1 | local test = require 'tests.test' 2 | 3 | import 'typelang.typelang' 4 | 5 | local Lib = require 'typelang.typelib' 6 | 7 | ------------------------------------------------------------------------------ 8 | 9 | local typel doblock() 10 | var x = 3 11 | do 12 | var x = 4 13 | x = 5 14 | end 15 | return x 16 | end 17 | 18 | test.eq(Lib.arrow({},Lib.int32), doblock:gettype()) 19 | test.eq( doblock(), 3 ) 20 | 21 | ------------------------------------------------------------------------------ 22 | 23 | local typel decltest() 24 | var x : Lib.double 25 | x = 1 26 | return x 27 | end 28 | 29 | test.eq(Lib.arrow({},Lib.double), decltest:gettype()) 30 | test.eq( decltest(), 1 ) 31 | 32 | ------------------------------------------------------------------------------ 33 | 34 | local typel repeatdecl() 35 | var x = 1 36 | var x = 2 37 | return x 38 | end 39 | 40 | test.eq(repeatdecl(), 2) 41 | 42 | ------------------------------------------------------------------------------ 43 | 44 | local typel multiassgn() 45 | var x : Lib.double 46 | var y : Lib.double 47 | x,y = 1,2 48 | return y 49 | end 50 | 51 | test.eq(multiassgn(), 2) 52 | 53 | ------------------------------------------------------------------------------ 54 | -------------------------------------------------------------------------------- /typelang/tests/functions.t: -------------------------------------------------------------------------------- 1 | local test = require 'tests.test' 2 | 3 | import 'typelang.typelang' 4 | 5 | local Lib = require 'typelang.typelib' 6 | 7 | ------------------------------------------------------------------------------ 8 | 9 | -- simple call with simple return value 10 | 11 | local typel ret0() 12 | return 0 13 | end 14 | 15 | local typel ret1using0() 16 | return ret0() + 1 17 | end 18 | 19 | test.eq(Lib.arrow({}, Lib.int32), ret0:gettype()) 20 | test.eq(Lib.arrow({}, Lib.int32), ret1using0:gettype()) 21 | test.eq( ret1using0(), 1) 22 | test.eq( ret0(), 0) 23 | test.eq( ret1using0(), 1) 24 | 25 | ------------------------------------------------------------------------------ 26 | 27 | local typel reti() 28 | return 10,10 29 | end 30 | 31 | local typel castret() 32 | var x : Lib.double 33 | var y : Lib.int32 34 | x,y = reti() 35 | return x/3,y/3 36 | end 37 | 38 | test.eq(Lib.arrow({}, {Lib.int32, Lib.int32}), reti:gettype()) 39 | test.eq(Lib.arrow({},{Lib.double,Lib.int32}), castret:gettype()) 40 | test.aeq({castret()}, {10.0/3.0, 3}) 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /typelang/tests/isct_predicates.t: -------------------------------------------------------------------------------- 1 | require 'examples.isct_predicates' -------------------------------------------------------------------------------- /typelang/tests/trivial.t: -------------------------------------------------------------------------------- 1 | local test = require 'tests.test' 2 | 3 | import 'typelang.typelang' 4 | 5 | local Lib = require 'typelang.typelib' 6 | 7 | ------------------------------------------------------------------------------ 8 | 9 | local typel noop() end 10 | 11 | test.eq(Lib.arrow({},{}), noop:gettype()) 12 | test.eq(nil, noop()) 13 | 14 | ------------------------------------------------------------------------------ 15 | 16 | local typel ret0() 17 | return 0 18 | end 19 | 20 | test.eq(Lib.arrow({},Lib.int32), ret0:gettype()) 21 | test.eq(ret0(), 0) 22 | 23 | ------------------------------------------------------------------------------ 24 | 25 | local typel ret01() 26 | return 0, 1 27 | end 28 | 29 | test.eq(Lib.arrow({},{Lib.int32, Lib.int32}), ret01:gettype()) 30 | test.aeq( {ret01()} , {0,1} ) 31 | 32 | ------------------------------------------------------------------------------ 33 | 34 | 35 | 36 | 37 | 38 | --------------------------------------------------------------------------------