|
| 1 | +# The Teal API |
| 2 | + |
| 3 | +If you have Lua application, you can load the Teal compiler API and use it to convert |
| 4 | +Teal source code into Lua source code on the fly. This is useful, for example, for |
| 5 | +adding Teal support to a Lua-based plugin system. |
| 6 | + |
| 7 | +## Before we begin: getting the API |
| 8 | + |
| 9 | +Before we `require` any code, let's consider how to integrate the Teal compiler |
| 10 | +in your application. As it is typical in the Lua ecosystem, there are a few |
| 11 | +ways to do it. |
| 12 | + |
| 13 | +### An application which embeds Lua sources |
| 14 | + |
| 15 | +If you have an application (let's say, written in C, C++ or Rust), which embeds Lua |
| 16 | +sources, the simplest way to do it is to embed the amalgamated Teal |
| 17 | +sources, `tl.lua`, into your application's Lua sources the same way you embed |
| 18 | +any other Lua modules. `tl.lua` is included in the Teal source distribution, |
| 19 | +and it is a single file which encapsulates all `teal.*` modules that comprise |
| 20 | +the Lua compiler. |
| 21 | + |
| 22 | +### Using the `teal` modules directly |
| 23 | + |
| 24 | +You might want to go this route if you are building a pure-Lua application, |
| 25 | +and you want to integrate dependencies using a package manager ecosystem such |
| 26 | +as LuaRocks or Lux. You can register a dependency on the `tl` package using |
| 27 | +your package manager of choice and require `teal` like you require any other |
| 28 | +module. |
| 29 | + |
| 30 | +## The entry point |
| 31 | + |
| 32 | +Now we can start by requiring the `teal` module, and getting a compiler instance: |
| 33 | + |
| 34 | +```lua |
| 35 | +local teal = require("teal") |
| 36 | + |
| 37 | +local compiler = teal.compiler() |
| 38 | +``` |
| 39 | + |
| 40 | +We then need to give the compiler some input, which can be a filename, or some |
| 41 | +Teal code as a string. This will produce an input handle. |
| 42 | + |
| 43 | +To read from a file, you can use `open`: |
| 44 | + |
| 45 | +```lua |
| 46 | +-- Let's create a simple .tl file: |
| 47 | +local fd = io.open("my_file.tl", "w") |
| 48 | +fd:write("print('hello')") |
| 49 | +fd:close() |
| 50 | + |
| 51 | +-- We can load the file using the `open` method of the API: |
| 52 | +local handle1 = compiler:open("my_file.tl") |
| 53 | + |
| 54 | +-- We don't need the file anymore |
| 55 | +os.remove("my_file.tl") |
| 56 | +``` |
| 57 | + |
| 58 | +To read from a string, use `input`. The optional second argument is |
| 59 | +a filename to be used in error messages. |
| 60 | + |
| 61 | +``` |
| 62 | +-- We can read Teal code directly as a string using `input`: |
| 63 | +local handle2 = compiler:input([[ |
| 64 | + local x = 1 |
| 65 | + local y = "oh-oh" |
| 66 | + print(x + y) |
| 67 | +]]) |
| 68 | +``` |
| 69 | + |
| 70 | +We can use those handles to request either one or all steps of the compilation |
| 71 | +pipeline to be performed. Here is an example generating code all at once: |
| 72 | + |
| 73 | + |
| 74 | +```lua |
| 75 | +local lua_code = handle1:gen() |
| 76 | +assert(type(lua_code) == "string") |
| 77 | +``` |
| 78 | + |
| 79 | +And here we can see the use of the API generating it step by step, using |
| 80 | +the `handle2` example above, which contains a type error: |
| 81 | + |
| 82 | +```lua |
| 83 | +-- Lexing the tokens should be fine |
| 84 | +local tokens, lex_errs = handle2:lex() |
| 85 | +assert(type(tokens) == "table") |
| 86 | +assert(#lex_errs == 0) |
| 87 | + |
| 88 | +-- Parsing should be fine as well |
| 89 | +local ast, parse_errs = tokens:parse() |
| 90 | +assert(type(ast) == "table") |
| 91 | +assert(parse_errs == nil) |
| 92 | + |
| 93 | +-- Checking should catch the type error from our example! |
| 94 | +local module, check_errs = ast:check() |
| 95 | +assert(type(module) == "table") |
| 96 | +assert(#check_errs.type_errors == 1) |
| 97 | +``` |
| 98 | + |
| 99 | +Of course, if the code contains no errors, you can also call `gen` |
| 100 | +and generate the Lua output. Both methods will produce the same result: |
| 101 | + |
| 102 | +``` |
| 103 | +local handle3 = compiler:input([[ |
| 104 | + local x: integer = 1 |
| 105 | + local y: integer = 2 |
| 106 | + print(x + y) |
| 107 | +]]) |
| 108 | +
|
| 109 | +-- Showcasing all steps |
| 110 | +local lua_code = handle3:lex():parse():check():gen() |
| 111 | +assert(lua_code == "local x = 1\nlocal y = 2\nprint(x + y)") |
| 112 | +
|
| 113 | +-- Or going straight to gen |
| 114 | +local lua_code2 = handle3:gen() |
| 115 | +assert(lua_code == lua_code2) |
| 116 | +``` |
0 commit comments