\n"
]]
def [[ STANDOUT 1 text
"$text"
]]
]
[SETHEADSTYLE
.red { color: red; }
]
[# ------------------------------------------------------------------ #]
[htmlize [J BlogMe - an extensible language for generating HTML]
[P ([STANDOUT 2007oct25]: This page refers to Blogme2, and is
obsolete... The docs for Blogme3 are [R blogme3.html across this link]
- they're very messy at the moment, but I'm working on them, and they
should be ready in a few days (top priority this time, really).)]
[P ([STANDOUT 2007apr18]: Hey! The rest of this page refers to
BlogMe2, that is obsolete... I just finished rewriting it ([Q ->]
[HREF littlelangs.html#blogme3 BlogMe3]), but I haven't had the time
yet to htmlize its docs...)]
[P (2005sep28: I wrote this page in a hurry by htmlizing two of
blogme's documentation files, README and INTERNALS, which are not
very clean...)]
[P See also the [HREF littlelangs.html#blogme [# -> blogme] entry
about BlogMe in my page about little languages].]
[# P [HLIST2 [J Quick index:]
[HREF #introduction Introduction]
[HREF #language How the language works]
[HREF #evaluation How [QQ []]-expressions are evaluated]
[HREF #def Defining new words in Lua with [QQ def]]
[HREF #internals The internals of blogme2.lua]
[HREF #main_tables The main tables used by the program]
[HREF #tables_for_words Blogme words (the tables _W and _A)]
[HREF #parsers The blogme parsers (the table _P)]
[J To be written: [HREF #files files, installing, running the demos,
help needed].]
]]
[#
# «.introduction» (to "introduction")
# «.language» (to "language")
# «.evaluation» (to "evaluation")
# «.def» (to "def")
# «.internals» (to "internals")
# «.main_tables» (to "main_tables")
# «.tables_for_words» (to "tables_for_words")
# «.parsers» (to "parsers")
# «.files» (to "files")
# «.help-needed» (to "help-needed")
# «.etc» (to "etc")
#]
[# ------------------------------------------------------------------ #]
[WITHINDEX
[RULE ----------------------------------------]
[sec «introduction» (to ".introduction")
H2 Introduction]
[P The "language" that blogme2.lua accepts is extensible and can deal
with input having a lot of explicit mark-up, like this,]
[QQQ
[HLIST2 Items:
[HREF http://foo/bar a link]
[HREF http://another/link]
[IT Italic text]
[BF Boldface]
]
]
[P and conceivably also with input with a lot of [IT implicit] mark-up
and with control structures, like these examples (which haven't been
implemented yet):]
[QQQ
[BLOGME
Tuesday, February 15, 2005
I usually write my notes in plain text files using Emacs; in
these files "["s and "]"s can appear unquoted, urls appear
anywhere without any special markup (like http://angg.twu.net/)
and should be recognized and htmlized to links, some lines are
dates or "anchors" and should be treated in special ways, the
number of blank lines between paragraphs matter, in text
paragraphs maybe _this markup_ should mean bold or italic, and
there may be links to images that should be inlined, etc etc
etc.
]
[IF LOCAL==true
[INCLUDE todo-list.blogme]
]
]
[P BlogMe also support executing blocks of Lua code on-the-fly, like
this:]
[QQQ
[lua:
-- We can put any block of Lua code here
-- as long as its "["s and "]"s are balanced.
]
]
[#
-- The following
def [[ IMAGE 2 url,alt HREF(url, ""..IMG(url,alt)) ]]
#]
[sec «language» (to ".language")
H2 How the language works]
[P BlogMe's language has only one special syntactical construct, "[QQ
[...]]". There are only have four classes of characters "[(]", "[)]",
whitespace, and "word"; "[QQ [...]]" blocks in the text are treated
specially, and we use Lua's "[QQ %b[]]" regexp-ish construct to skip
over the body of a "[QQ [...]]" quickly, skipping over all balanced
"[QQ []]" pairs inside. The first "word" of such a block (we call it
the "head" of the block) determines how to deal with the "rest" of the
block.]
[P To "evaluate" an expression like]
[QQQ
[HREF http://foo/bar a link]
]
[P we only parse its "head" - "[QQ HREF]" - and then we run the Lua
function called [QQ HREF]. It is up to that function [QQ HREF] to
parse what comes after the head (the "rest"); [QQ HREF] may evaluate
the [QQ []]-expressions in the rest, or use the rest without
evaluations, or even ignore the rest completely. After the execution
of [QQ HREF] the parsing resumes from the point after the associated
"[Q [)]]".]
[sec «evaluation» (to ".evaluation")
H2 How [QQ []]-expressions are evaluated]
[P Actually the evaluation process is a bit more subtle than than. In
the last example, BlogMe doesn't just execute [QQ HREF()]; it uses an
auxiliary table, [QQ _A], and it executes:]
[QQQ HREF(_A["HREF"]())]
[P [QQ _A["HREF"]] returns a function, [QQ vargs2], that uses the rest
to produce arguments for [QQ HREF]. Running [QQ vargs2()] in that
situation returns]
[QQQ "http://foo/bar", "a link"]
[P and [QQ HREF] is called as [QQ HREF("http://foo/bar", "a link")].
So, to define [QQ HREF] as a head all we would need to do ("would"
because it's already defined) is:]
[QQQ
HREF = function (url, text)
return ""..text..""
end
_A["HREF"] = vargs2
]
[sec «def» (to ".def")
H2 Defining new words in Lua with [QQ def]]
[P Defining new heads is so common - and writing out the full Lua code
for a new head, as above, is so boring - that there are several tools
to help us with that. I will explain only one of them, "[QQ def]":]
[QQQ def [[ HREF 2 url,text "$text" ]]]
[P "[QQ def]" is a lua function taking one argument, a string; it
splits that string into its three first "words" (delimited by blanks)
and a "rest"; here is its definition:]
[QQQ
restspecs = {
["1"]=vargs1, ["2"]=vargs2, ["3"]=vargs3, ["4"]=vargs4,
["1L"]=vargs1_a, ["2L"]=vargs2_a, ["3L"]=vargs3_a, ["4L"]=vargs4_a
}
def = function (str)
local _, __, name, restspec, arglist, body =
string.find (str, "^%s*([^%s]+)%s+([^%s]+)%s+([^%s]+)%s(.*)")
_G[name] = lambda(arglist, undollar(body))
_A[name] = restspecs[restspec] or _G[restspec]
or error("Bad restspec: "..name)
end
]
[P The first "word" ("name") is the name of the head that we're
defining; the second "word" ("restspec") determines the _GETARGS
function for that head, and it may be either a special string (one of
the ones registered in the table "restspecs") or the name of a global
function.]
[# ------------------------------------------------------------]
[sec «internals» (to ".internals")
H2 The internals of blogme2.lua:]
[sec «main_tables» (to ".main_tables")
H2 The main tables used by the program]
[LIST2
[J [QQ _G]: Lua's [LR
http://www.lua.org/manual/5.0/manual.html#predefined table of globals]]
[J [QQ _W]: blogme words]
[J [QQ _P]: low-level parsers]
[J [QQ _A]: argument-parsing functions for blogme words]
[J [QQ _AA]: abbreviations for argument-parsing functions (see `def')]
[J [QQ _V]: blogme variables (see "$" and `withvars')]
]
[# --------------------]
[sec «tables_for_words» (to ".tables_for_words")
H2 Blogme words (the tables _W and _A)]
[P (Source code: the function `[QQ run_head]', at the end of [AL
blogme/blogme2-inner.lua blogme2-inner.lua].)]
[P Let's examine an example. When blogme processes:]
[QQQ [HREF http://foo bar]]
[P it expands it to:]
[QQQ bar]
[P When the blogme evaluator processes a bracketed expression it first
obtains the first "word" of the brexp (called the "head" of the
brexp), that in this case is "[QQ HREF]"; then it parses and evaluates
the "arguments" of the brexp, and invokes the function associated to
the word "[QQ HREF]" using those arguments. Different words may have
different ways of parsing and evaluating their arguments; this is like
the distinction in Lisp between functions and special forms, and like
the special words like LIT in Forth. Here are the hairy details: if
[QQ HREF] is defined by]
[QQQ
HREF = function (url, str)
return ""..str.."" end
_W["HREF"] = HREF
_A["HREF"] = vargs2
]
[P then the "value" of [QQ [HREF http://foo bar]] will be the same as
the value returned by [QQ HREF("http://foo", "bar")], because]
[QQQ _W["HREF"](_A["HREF"]())]
[P will be the same as:]
[QQQ HREF(vargs2())]
[P when [QQ vargs2] is run the parser is just after the end of the
word "[QQ HREF]" in the brexp, and running [QQ vargs2()] there parses
the rest of the brexp and returns two strings, [QQ "http://foo"] and
[QQ "bar"].]
[P See: (info "(elisp)Function Forms")
[BR] and: (info "(elisp)Special Forms")]
[# --------------------]
[sec «parsers» (to ".parsers")
H2 The blogme parsers (the table _P)]
[P (Corresponding source code: most of [AL blogme/blogme2-inner.lua
blogme2-inner.lua].)]
[P Blogme has a number of low-level parsers, each one identified by a
string (a "blogme pattern"); the (informal) "syntax" of those blogme
patterns was vaguely inspired by Lua5's [HREF
http://www.lua.org/manual/5.0/manual.html#pm syntax for patterns]. In
the table below "BP" stands for "blogme pattern".]
[QQQ
BP Long name/meaning Corresponding Lua pattern
-----+----------------------+--------------------------
"%s" | space char | "[ \t\n]"
"%w" | word char | "[^%[%]]"
"%c" | normal char | "[^ \t\n%[%]]"
"%B" | bracketed expression | "%b[]"
"%W" | bigword | "(%w*%b[]*)*" (but not the empty string!)
]
[P [HLIST2 [J The low-level parsing functions of blogme are of two
kinds (levels):]
[J Functions in the "parse only" level only succeed or fail. When
they succeed they return true and advance the global variable
`pos'; when they fail they return nil and leave pos unchanged
[FOOTREF .partial_failure partial_failure (*)].]
[J Functions in the "parse and process" level are like the
functions in the "parse only" level, but with something extra:
when they succeed they store in the global variable `val' the
"semantic value" of the thing that they parsed. When they fail
they are allowed to garble `val', but they won't change `pos'.]
]]
[P See: (info "(bison)Semantic Values")]
[P These low-level parsing functions are stored in the table `[QQ
_P]', with the index being the "blogme patterns". They use the global
variables `[QQ subj]', `[QQ pos]', `[QQ b]', `[QQ e]', and `[QQ
val]'.]
[P An example: running [QQ _P["%w+"]()] tries to parse a (non-empty)
series of word chars starting at [QQ pos]; running [QQ
_P["%w+:string"]()] does the same, but in case of success the semantic
value is stored into `[QQ val]' as a string -- the comment "[QQ
:string]" in the name of the pattern indicates that this is a "parse
and process" function, and tells something about how the semantic
value is built.]
[P [FOOTREF partial_failure .partial_failure (*)]: Blogme patterns
containing a semicolon (";") violate the convention that says that
patterns that fail do not advance pos. Parsing "A;B" means first
parsing "A", not caring if it succeds or fails, discarding its
semantic value (if any), then parsing "B", and returning the result of
parsing "B". If "A" succeds but "B" fails then "A;B" will fail, but
pos will have been advanced to the end of "A". "A" is usually "%s*".]
[# ------------------------------------------------------------]
[sec «files» (to ".files")
H2 Files]
[P (To do: write this stuff, organize.)]
[P [HLIST2 Files:
[J Its main directory: [A0L blogme/].]
[J Its [AL blogme/README README], and a description of its [AL
blogme/INTERNALS INTERNALS].]
[HLIST2 [J Its source code:]
[AL blogme/blogme2-inner.lua blogme2-inner.lua]
[AL blogme/blogme2-middle.lua blogme2-middle.lua]
[AL blogme/blogme2-outer.lua blogme2-outer.lua]
[AL blogme/blogme2.lua blogme2.lua]
]
[J The [HREF TH/blogme.blogme BlogMe source] for this page.]
[J The [HREF TH/math-b.blogme BlogMe source] for my
[HREF math-b.html math page].]
[J The [HREF speedbar.blogme BlogMe source] for the navigation bar thing.]
]]
[P There is no .tar.gz yet (coming soon!).]
[sec «help-needed» (to ".help-needed")
H2 Help needed]
[P Lua seems to be quite popular in the M$-Windows
world, but I haven't used W$ for anything significative since 1994
and I can't help with W$-related questions. If you want to try
BlogMe on W$ then please consider writing something about your
experience to help the people coming after you.]
[sec «etc» (to ".etc")
H2 Etc]
[P A [AL .emacs#blogme-mode BlogMe mode for emacs] and [AL
.emacs#favourite-modes a way to switch modes quickly] (with M-m).]
[P [BF A note on usage] (see [AL blogme/blogme2.lua the corresponding
source code]):]
[QQQ blogme2.lua -o foo.html -i foo.blogme]
[P This behaves in a way that is a bit unexpected: what gets written
to foo.html is not the result of "expanding" the contents of
foo.blogme - it's the contents of the variable [QQ blogme_output]. The
function (or "blogme word") [QQ htmlize] sets this variable. Its
source code is [AL blogme/blogme2-outer.lua here].]
[P History: BlogMe is the result of many years playing with little
languages; see [HREF [-> littlelangs] this page]. BlogMe borrowed many
ideas from Forth, Tcl and Lisp.]
[P [HREF [-> contact] How to get in touch with the author.]]
]
]
[#
# Local Variables:
# coding: raw-text-unix
# modes: (fundamental-mode blogme-mode)
# End:
#]