std/eiy
import std/eiy to use it. Source: src/std/eiy.iyi at v0.18.0.
The tags:
* `<%= expr %>` prints the expression's `to_s`.
* `<% code %>` runs the code; `<% if x %>` ... `<% end %>` and every other
iyi statement work, because the code is spliced into the program as is.
* `<%- ... %>` drops the indentation before the tag when the tag is the
first thing on its line; `<% ... -%>` drops the rest of the line after
it, newline included. Both keep a control line from leaving a blank one.
* `<%# note %>` is a comment: nothing is printed and nothing runs.
* `<%% ... %>` is the text `<% ... %>` itself, for a template about
templates.Printing goes through one verb, print, so a template writes to anything that has it: an IyiIO (stdout, a File) or Eiy::Buffer, which collects the text for render.
Compile time:
* `Eiy.embed("page.eiy", io)` splices the template's code here; `io`
names what it prints to.
* `Eiy.render("page.eiy")` is the rendered text as a `String`.
* `Eiy.def_to_s("page.eiy")` in a class defines `to_s(io)` and `to_s`
from the template.
The template path is read beside the file that writes the macro, or as
itself when absolute. The macros run `eiy/process`, an iyi program in this
directory that reads the template with the lexer below and prints the
source; it is compiled once and cached with the compiler's other macro
programs, and rebuilt when this module changes.Run time:
* `Eiy.process_string(text, filename, io_name)` and
`Eiy.process_file(filename, io_name)` are that translation as a value;
`Eiy::Lexer` is the tokeniser under it, for a tool that wants tokens.What it refuses: a tag that is never closed, with its line and column (unterminated <%= tag at line 3, column 5); next_token?, process_string? and process_file? answer nil for the same template. A template file that cannot be read is refused by File.read's own sentence. Positions count lines from 1 and columns from 1 in characters, not bytes, so a column on a line with non-ASCII text is the column a reader sees.
Eiystruct
pub struct Eiy
# Collects what a template prints, for `render` or for a caller that wants
# the text rather than a stream.
class Buffer
def initialize
def print(text : String) : Nil
def print(value) : Nil
# `print`, the one verb a template's target has, so a Buffer can be one.
def to_s(io) : Nil
def to_s : String
end
class Lexer
class Token
enum Type : Int32
String = 0
Output = 1
Control = 2
EOF = 3
end
def column_number : Int32
def column_number=(value : Int32) : Int32
def control? : Bool
def eof? : Bool
def initialize
def line_number : Int32
def line_number=(value : Int32) : Int32
def output? : Bool
def string? : Bool
def suppress_leading : Bool
def suppress_leading=(value : Bool) : Bool
def suppress_leading? : Bool
def suppress_trailing : Bool
def suppress_trailing=(value : Bool) : Bool
def suppress_trailing? : Bool
def type : Type
def type=(value : Type) : Type
def value : String
def value=(value : String) : String
end
# Why the last `next_token?` answered nil, or nil while it has not.
def error : String | ::Nil
def initialize(string : String)
# The next token; the same `Token` object is reused, so copy what is
# needed before asking again. Panics on a tag that is never closed.
def next_token : Token
# The next token, or nil on a tag that is never closed; `error` then says
# where.
def next_token? : Token | ::Nil
end
def initialize
# Where `embed` reads *template* from when it is written in *caller*: the
# template itself when absolute, otherwise beside the caller.
def self.locate(template : String, caller : String) : String
# `process_string` of the file at *filename*.
def self.process_file(filename : String, io_name : String = DefaultIoName) : String
# `process_string?` of the file at *filename*.
def self.process_file?(filename : String, io_name : String = DefaultIoName) : String | ::Nil
# The iyi source that prints the template *string* to *io_name*. Panics on
# a tag that is never closed, naming *filename* when there is one.
def self.process_string(string : String, filename : String = "", io_name : String = DefaultIoName) : String
# The iyi source that prints the template *string* to *io_name*, or nil
# for a tag that is never closed.
def self.process_string?(string : String, filename : String = "", io_name : String = DefaultIoName) : String | ::Nil
# Defines `to_s(io)` and `to_s` from the template at *filename*. The
# parameter is `__io__`, a name no template means, so a template's own
# `io` stays its own.
macro def_to_s(filename)
# Splices the template at *filename* here; its code prints to *io_name*.
macro embed(filename, io_name)
# The template at *filename*, rendered to a `String`. The buffer's name is
# the macro's own, so a template that renders another one inside it does
# not replace the buffer it is printing to.
macro render(filename)
end
Eiy: embedded iyi. A template is text with iyi code inside it, and this module turns the template into iyi source — at compile time, so a rendered template is ordinary compiled code by the time the program runs.