Skip to content

Commit

Permalink
docs: rework 'files' example
Browse files Browse the repository at this point in the history
* Use more auxiliary functions, showing the record being
  passed in as an argument and out as a return
* Returning a number from `return main()` does not do
  what one would expect (it's not equivalent to `os.exit`)
* Do not reference snippets of the code itself in the
  markdown, makes it harder to maintain in sync
* Uppercase-convert the input as a trivial example of
  data transformation between input and output
* Use more descriptive variable/field names instead of
  comments
* Do not use `""` as a sentinel value, as it is less
  idiomatic for Lua
  • Loading branch information
hishamhm committed Jul 20, 2023
1 parent 41cc391 commit c103fa7
Show file tree
Hide file tree
Showing 3 changed files with 173 additions and 126 deletions.
51 changes: 18 additions & 33 deletions docs/examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@ In all of these examples it is assumed that the root directory is 'examples'.

## Files

This example demonstrates `cyan init` to create a Teal project, as well as the
Teal language construct of `record`, the type `FILE` for file handles, and
basic IO.
This example demonstrates using `cyan` to create and build a Teal project, as
well as the Teal language construct of `record`, the type `FILE` for file
handles, and basic IO.

### Create the Project

Expand Down Expand Up @@ -43,31 +43,11 @@ files> find .

### Create main.tl

In these examples `main.tl` will be the program that gets run. In other
examples you will have modules that are called from main.
In this example `main.tl` will be the program that gets run. More complex
projects will have modules that are called from main.

Create [src/main.tl](files/src/main.tl).

The file handles appear like this in main.tl

```
-- record to hold file handles
local type Handles = record
i: FILE -- input
o: FILE -- output
end
```

And are used like this:
```
local function main(f: Handles): integer
local lines = f.i:read("a")
-- do work on input, then write it out --
f.o:write(lines)
return 0
end
```

### Build the Project

Use Cyan to build the project.
Expand All @@ -92,20 +72,25 @@ files> tree .

### Run the Project

In this example the program reads from STDIN and writes to a file.
The program reads input arguments `-i <filename>` and `-o <filename>` from
the command line to select input and output filenames. If those are not
given, it uses standard input and standard output as fallback defaults.

In this example the program reads data from the `ls` command piped via
standard input, and writes to a file:

```
files> ls -R1 | lua build/main.lua -o tmp.out
files> cat tmp.out
build
src
tlconfig.lua
BUILD
SRC
TLCONFIG.LUA
./build:
main.lua
./BUILD:
MAIN.LUA
./src:
main.tl
./SRC:
MAIN.TL
```

You can delete the temporary file.
Expand Down
111 changes: 71 additions & 40 deletions docs/examples/files/build/main.lua
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
local _tl_compat; if (tonumber((_VERSION or ''):match('[%d.]*$')) or 0) < 5.3 then local p, m = pcall(require, 'compat53.module'); if p then _tl_compat = m end end; local assert = _tl_compat and _tl_compat.assert or assert; local io = _tl_compat and _tl_compat.io or io

local _tl_compat; if (tonumber((_VERSION or ''):match('[%d.]*$')) or 0) < 5.3 then local p, m = pcall(require, 'compat53.module'); if p then _tl_compat = m end end; local io = _tl_compat and _tl_compat.io or io; local os = _tl_compat and _tl_compat.os or os; local string = _tl_compat and _tl_compat.string or string



Expand All @@ -11,58 +10,90 @@ local _tl_compat; if (tonumber((_VERSION or ''):match('[%d.]*$')) or 0) < 5.3 th



local function parse_arguments(args)
local i_name, o_name
local err
local i = 1
while i <= #args do
local a, b = args[i], args[i + 1]
if a == "-i" then
if not b then
err = "-i name?"
else
i_name = b
i = i + 2
end
elseif a == "-o" then
if not b then
err = "-o name?"
else
o_name = b
i = i + 2
end
else
err = "unknown arg: " .. a
end
if err then

error(err)
end
end
return i_name, o_name
end


local function get_fd(name, mode, default)
if not name then
return default
end

local function main(f)
local lines = f.i:read("a")
local fd, err = io.open(name, mode)
if not fd then
return nil, err
end

f.o:write(lines)
return 0
return fd
end


local function get_handles(input_name, output_name)
local handles = {}
local err

local fin, fout = "", ""
local errstr = ""
local i = 1
while i <= #arg do
local a, b = arg[i], arg[i + 1]
if a == "-i" then
if not b then
errstr = "-i filename?"
else
fin = b
i = i + 2
end
elseif a == "-o" then
if not b then
errstr = "-o filename?"
else
fout = b
i = i + 2
end
else
errstr = "unknown arg: " .. a
handles.input, err = get_fd(input_name, "r", io.stdin)
if err then
error(err)
end
if errstr ~= "" then
error(errstr)

handles.output, err = get_fd(output_name, "w", io.stdout)
if err then
error(err)
end

return handles
end


local handle = {}
if fin == "" then
handle.i = io.stdin
else
handle.i = assert(io.open(fin, "r"))
end
if fout == "" then
handle.o = io.stdout
else
handle.o = assert(io.open(fout, "w"))
local function process_handles(h)
local lines = h.input:read("*a")
h.input:close()



lines = lines:upper()

h.output:write(lines)
h.output:close()
end

local function main(args)
local input_name, output_name = parse_arguments(args)

local h = get_handles(input_name, output_name)

process_handles(h)

os.exit(0)
end

return main(handle)
main(arg)
137 changes: 84 additions & 53 deletions docs/examples/files/src/main.tl
Original file line number Diff line number Diff line change
@@ -1,68 +1,99 @@
-- files/src/main.tl
-- Read content from STDIN or file; write content to STDOUT or file.
--
-- build: cyan build
--
-- usage: lua files.lua [-i <file>] [-o <file>]
--
-- Reads content from STDIN or file;
-- writes content in uppercase to STDOUT or file.
-------------------------------------------------------------------------------

-- record to hold file handles
local type Handles = record
i: FILE -- input
o: FILE -- output
local record Handles
input: FILE
output: FILE
end

-- a simple command-line parser
local function parse_arguments(args: {string}): string, string
local i_name, o_name: string, string
local err: string
local i = 1
while i <= #args do
local a, b = args[i], args[i+1]
if a == "-i" then
if not b then
err = "-i name?"
else
i_name = b
i = i + 2
end
elseif a == "-o" then
if not b then
err = "-o name?"
else
o_name = b
i = i + 2
end
else
err = "unknown arg: " .. a
end
if err then
-- exit, giving a hint on the way out
error(err)
end
end
return i_name, o_name
end

-- create or return file handles
local function get_fd(name: string, mode: string, default: FILE): FILE, string
if not name then
return default
end

local fd, err = io.open(name, mode)
if not fd then
return nil, err
end

-- main -----------------------------------------------------------------------
local function main(f: Handles): integer
local lines = f.i:read("a")
-- do work on input, then write it out --
f.o:write(lines)
return 0
return fd
end

-- get handles record
local function get_handles(input_name: string, output_name: string): Handles
local handles: Handles = {}
local err: string

-- command line ---------------------------------------------------------------
local fin, fout = "", "" -- input and output file names
local errstr = ""
local i = 1
while i <= #arg do
local a, b = arg[i], arg[i+1]
if a == "-i" then
if not b then
errstr = "-i filename?"
else
fin = b
i = i + 2
end
elseif a == "-o" then
if not b then
errstr = "-o filename?"
else
fout = b
i = i + 2
end
else
errstr = "unknown arg: " .. a
end
if errstr ~= "" then -- exit, giving a hint on the way out
error(errstr)
end
handles.input, err = get_fd(input_name, "r", io.stdin)
if err then
error(err)
end

handles.output, err = get_fd(output_name, "w", io.stdout)
if err then
error(err)
end

return handles
end

-- create file handles, as needed
local handle: Handles = {}
if fin == "" then
handle.i = io.stdin
else
handle.i = assert(io.open(fin, "r"))
-- do some work using the file handles
local function process_handles(h: Handles)
local lines = h.input:read("*a")
h.input:close()

-- simple example of doing work on the input:
-- convert it to ASCII uppercase
lines = lines:upper()

h.output:write(lines)
h.output:close()
end
if fout == "" then
handle.o = io.stdout
else
handle.o = assert(io.open(fout, "w"))

local function main(args: {string}): integer
local input_name, output_name = parse_arguments(args)

local h = get_handles(input_name, output_name)

process_handles(h)

os.exit(0)
end

-- launch the main function
return main(handle)
main(arg)

0 comments on commit c103fa7

Please sign in to comment.