├── .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 |
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 |
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 |
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 |
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 |
--------------------------------------------------------------------------------