Skip to content
Browse files

Implement macros, update test example, readme. Add more comments

  • Loading branch information...
1 parent a877550 commit f0f7af7f2e634bd278021988efad7e38c688638e @meric committed Apr 8, 2012
Showing with 383 additions and 325 deletions.
  1. +5 −247 README.markdown
  2. +199 −45 l2l.lua
  3. +179 −33 test.lsp
View
252 README.markdown
@@ -6,261 +6,19 @@ Lisp to Lua Compiler
Description
-----------
-A Dangerous Lisp to Lua Compiler.
+A tail-call-optimized, object-oriented, unicode-enabled lisp that compiles to and runs as fast as lua. Equipped with macroes and compile-time compiler manipulation.
Requires Lua 5.2!
Example
-------
-Here is an example lisp file, output messages when compiling the example, the output lua code from compiling the example as well as the output of the final program when run.
+See test.lsp
-Example Lisp File
------------------
+TODO
+----
- ; Example 1: Function declaration
- (print "\n--- Example 1 ---\n")
- (defun ! (n)
- (cond ((== n 0) 1)
- ((== n 1) 1)
- (true (* n (! (- n 1))))))
-
- (print (! 100))
-
- ; Example 2: Acccessing functions from Lua environment
- (print "\n--- Example 2 ---\n")
- (set hello-world "hello gibberish world")
- (print (string.gsub hello-world "gibberish " ""))
-
- ; Example 3: Quasiquote and unquote
- (print "\n--- Example 3 ---\n")
- (map print `(1 2 3 ,(map (lambda (x) (* x 5)) '(1 2 3))))
- ; Note: prints all numbers only for lua 5.2. only 5.2 supports __ipairs override
-
- ; Example 4: Let
- (print "\n--- Example 4 ---\n")
- (let (a (+ 1 2)
- b (+ 3 4))
- (print a)
- (print b))
-
- ; Example 5: Accessor method
- (print "\n--- Example 5 ---\n")
- (.write {"write" (lambda (self x) (print x))} "hello-world")
-
- ; Example 6: Anonymous function
- (print "\n--- Example 6 ---\n")
- (print ((lambda (x y) (+ x y)) 10 20))
-
- ; Example 7: Directive (The '#' prefix)
- (print "\n--- Example 7 ---\n")
- ; The following line will run as soon as it is parsed, no code will be generated
- ; It will add a new "--" operator that will be effective immediately
- #(set -- (Operator (lambda (str) (.. "-- " (tostring str)))))
-
- ; Adds a lua comment to lua executable, using operator we defined.
- (-- "This is a comment") ; Will appear in `out.lua`
-
- ; Example 8: Define a do block
- #(print "\n--- Example 8 ---\n")
- ; E.g. (do (print 1) (print 2)) will execute (print 1) and (print 2) in sequence
- #(set do (Operator (lambda (...)
- (.. "(function()\n"
- (indent (compile [...]))
- "\nend)()"))))
- (print "\n--- Example 8 ---\n")
- (print "\n--- Did you what was printed while compiling? ---\n")
- (do
- (print 1)
- (print 2))
-
- ; We can now make this program be interpreted by wrapping code in "#(do ...)"!
-
- #(do
- (print "I am running this line in the compilation step!")
- (print "This too!")
- (print (.. "1 + 1 = " (+ 1 1) "!"))
- (print "Okay that's enough."))
-
- ; We've had enough, so let's delete our do Operator
- #(set do nil)
-
- ; Uncommenting the following will result in an error when compiling
- ; #(do (print 1))
-
- ; Example 9: Vector
- (print "\n--- Example 9 ---\n")
- (let (a (* 7 8))
- (map print [1 2 a 4]))
-
- ; Example 10: Dictionary
- (print "\n--- Example 10 ---\n")
- (let (dict {"a" "b" 1 2 "3" 4})
- (print dict["a"] "b")
- (print dict.a "b")
- (print dict[1] 2)
- (print dict.3 4))
-
-Printed out while compiling example
------------------------------------
-
- --- Example 8 ---
-
- I am running this line in the compilation step!
- This too!
- 1 + 1 = 2!
- Okay that's enough.
-
-Compiled Output
----------------
- -- header omitted --
- print("\n--- Example 1 ---\n");
-
- function _c33_(n)
- return (function()
- if (0 == n) then
- return 1
- end
- if (1 == n) then
- return 1
- end
- if true then
- return (n * _c33_((n - 1)))
- end
- end)()
- end;
-
- print(_c33_(100));
-
- print("\n--- Example 2 ---\n");
-
- hello_c45_world = "hello gibberish world";
-
- print(string["gsub"](hello_c45_world,"gibberish ",""));
-
- print("\n--- Example 3 ---\n");
-
- map(print,List(1,2,3,map((function(x)
- return (x * 5)
- end),List(1,2,3))));
-
- print("\n--- Example 4 ---\n");
-
- (function()
- local a = (1 + 2)
- local b = (3 + 4)
- print(a);
- return print(b)
- end)();
-
- print("\n--- Example 5 ---\n");
-
- ({["write"] = (function(self,x)
- return print(x)
- end)}):write("hello-world");
-
- print("\n--- Example 6 ---\n");
-
- print((function(x,y)
- return (x + y)
- end)(10,20));
-
- print("\n--- Example 7 ---\n");
-
- ;
-
- -- This is a comment;
-
- ;
-
- ;
-
- print("\n--- Example 8 ---\n");
-
- print("\n--- Did you what was printed while compiling? ---\n");
-
- (function()
- print(1);
- return print(2)
- end)();
-
- ;
-
- ;
-
- print("\n--- Example 9 ---\n");
-
- (function()
- local a = (7 * 8)
- return map(print,({1,2,a,4}))
- end)();
-
- print("\n--- Example 10 ---\n");
-
- return (function()
- local dict = ({["a"] = "b",[1] = 2,["3"] = 4})
- print(dict["a"],"b");
- print(dict["a"],"b");
- print(dict[1],2);
- return print(dict["3"],4)
- end)()
-
-
-
-Output from running compiled lua
---------------------------------
- --- Example 1 ---
-
- 9.3326215443944e+157
-
- --- Example 2 ---
-
- hello world 1
-
- --- Example 3 ---
-
- 1
- 2
- 3
- (5 10 15)
-
- --- Example 4 ---
-
- 3
- 7
-
- --- Example 5 ---
-
- hello-world
-
- --- Example 6 ---
-
- 30
-
- --- Example 7 ---
-
-
- --- Example 8 ---
-
-
- --- Did you what was printed while compiling? ---
-
- 1
- 2
-
- --- Example 9 ---
-
- 1
- 2
- 56
- 4
-
- --- Example 10 ---
-
- b b
- b b
- 2 2
- 4 4
+Proper error messages, with line number tracking
Quickstart
----------
View
244 l2l.lua
@@ -1,5 +1,13 @@
+-- Lisp to Lua Compiler by Eric Man
+-- License undecided.
+
local input, output = arg[1] or "test.lsp", arg[2] or "out.lua"
+local ENV = _ENV
+local L2L = setmetatable({}, {__index = ENV})
+_ENV = L2L
+
+
List = {}
-- Create new List, e.g. List(item1, item2, item3)
@@ -18,8 +26,13 @@ setmetatable(List, {__call=function(self, ...)
return first
end})
+-- Alternative constructor for List
Pair = function (a, b)
- return setmetatable({a, b}, List)
+ local pair = setmetatable({a, b}, List)
+ if getmetatable(b) == List then
+ pair.last = b.last
+ end
+ return pair
end
List.__index = List
@@ -28,6 +41,10 @@ function List:__tostring()
return "("..List.concat(map(tostring, self), " ")..")"
end
+function List:tolisp()
+ return "("..List.concat(map(tolisp, self), " ")..")"
+end
+
function List:tolua()
return "List("..List.concat(map(tolua, self), ",")..")"
end
@@ -81,6 +98,8 @@ function List:cdr()
return self[2]
end
+-- Vector, Dictionary, Number, Symbol are intermediate types that will only
+-- exist in parse tree during compile time but not be in code during run-time.
Vector = {}
-- Create new Vector, e.g. Vector(item1, item2, item3)
@@ -102,6 +121,10 @@ function Vector:tolua()
return "({"..List.concat(map(tostring, self), ",").."})"
end
+function Vector:tolisp()
+ return tostring(self)
+end
+
Dictionary = {}
-- Create new Dictionary, e.g. Dictionary(key1, item1, key2, item2)
@@ -133,6 +156,10 @@ function Dictionary:tolua()
return "({"..List.concat(map(tostring, t), ",").."})"
end
+function Dictionary:tolisp()
+ return tostring(self)
+end
+
-- Map is usable for all types implementing __ipairs
function map(f, l)
local result = Pair()
@@ -170,11 +197,16 @@ function Symbol:tolua()
return "Symbol("..self.name:tolua()..")"
end
+function Symbol:tolisp()
+ return tostring(self)
+end
+
function hash(char)
- return "_c"..char:byte().."_"
+ return "__c"..char:byte().."__"
end
-LUAKEYWORDS ={
+-- Lua keywords that needs to be allowed in Lisp
+L2LNONKEYWORDS ={
["and"] = true,
["break"] = true,
["do"] = true,
@@ -199,17 +231,15 @@ LUAKEYWORDS ={
}
function Symbol:tohash()
- local name = tostring(self.name):gsub("[^a-zA-Z0-9.%[%]\"]",
- function(a)
- if a ~= "." then
- return hash(a)
- else
- return "."
- end
- end)
- if LUAKEYWORDS[name] then
- name = name:gsub("(.)", hash)
- end
+ local name = tostring(self.name):gsub("[^_a-zA-Z0-9.%[%]\"]", hash)
+
+ name = name:gsub("[^.]+", function(word)
+ if L2LNONKEYWORDS[word] then
+ return word:gsub("(.)", hash)
+ end
+ return word
+ end)
+
if name:sub(1,1) == "." then
return name
else
@@ -234,6 +264,10 @@ function Number:tolua()
return tostring(self.number)
end
+function Number:tolisp()
+ return tostring(self.number)
+end
+
Operator = {}
setmetatable(Operator, {__call=function(self, lambda)
@@ -247,18 +281,18 @@ function Operator:__call(...)
end
function Operator:__tostring()
- if _G[self] then
- return _G[self]
+ if _ENV[self] then
+ return _ENV[self]
end
-- Search and cache name
- for k, v in pairs(_G) do
+ for k, v in pairs(_ENV) do
if v == self then
- _G[self] = k
- return _G[self]
+ _ENV[self] = k
+ return _ENV[self]
end
end
- _G[self] = "<operator:"..tostring(self.lambda)..">"
- return _G[self]
+ _ENV[self] = "<operator:"..tostring(self.lambda)..">"
+ return _ENV[self]
end
-- String additions
@@ -273,25 +307,45 @@ end
function string.tolua(str)
return '"'..str:gsub('"','\\"')..'"'
end
+
+function string.tolisp(str)
+ return '"'..str:gsub('"','\\"')..'"'
+end
+
-- These types can be translated directly into Lua
-LUATYPES =
+L2LTYPES =
{[string] = true,
[List] = true,
[Vector] = true,
[Dictionary] = true,
[Symbol] = true,
[Number] = true}
+-- Convert parse tree object into lua form.
function tolua(object)
- if LUATYPES[getmetatable(object)] or type(object) == "string" then
+ if L2LTYPES[getmetatable(object)] or type(object) == "string" then
return object:tolua()
end
- -- if type(object) == "table" then
- -- return setmetatable(object, Dictionary):tolua()
- -- end
if object == nil then
return "nil"
end
+ if type(object) == "number" then
+ return tostring(object)
+ end
+ error("Internal Error: "..tostring(object)..", "..type(object))
+end
+
+-- Convert parse tree object back into lisp form. (used by macroexpand)
+function tolisp(object)
+ if L2LTYPES[getmetatable(object)] or type(object) == "string" then
+ return object:tolisp()
+ end
+ if object == nil then
+ return "nil"
+ end
+ if type(object) == "number" then
+ return tostring(object)
+ end
error("Internal Error: "..tostring(object)..", "..type(object))
end
@@ -330,7 +384,7 @@ function parse(str)
if str:starts("#") then
local rest = str:sub(2):match("%s*(.*)")
local node = List(parse(rest))
- return List(Symbol("execute"), node[1]), List.unpack(node[2])
+ return List(Symbol("directive"), node[1]), List.unpack(node[2])
end
-- Replace with Quasiquote
@@ -384,8 +438,8 @@ function generate(tree)
local parameters = List.concat(map(generate, tree[2] or {}), ",")
if getmetatable(first) == Symbol then
first = first:tohash()
- if getmetatable(_G[first]) == Operator then
- return _G[first](List.unpack(tree[2]))
+ if getmetatable(_ENV[first]) == Operator then
+ return _ENV[first](List.unpack(tree[2]))
end
if first:sub(1,1)=="." and first ~="..." then
assert(tree:cdr(), "Accessor method has no owner: "..tostring(first))
@@ -402,6 +456,7 @@ function generate(tree)
return tolua(tree)
end
+-- Indent some code by two spaces
function indent(str)
return str:gsub("\n", "\n "):gsub("^", " ")
end
@@ -418,56 +473,58 @@ end
-- Define primitives
-_G[Symbol("=="):tohash()] = Operator(function(first, ...)
+-- Equality operator
+_ENV[Symbol("=="):tohash()] = Operator(function(first, ...)
local content =
List.concat(map(function(node)
return generate(node).. " == ".. generate(first)
end, {...}), " and ")
return "(".. (content == "" and "true" or content) .. ")"
end)
-_G[Symbol("*"):tohash()] = Operator(function(...)
+-- Multiplication operator
+_ENV[Symbol("*"):tohash()] = Operator(function(...)
return "(".. List.concat(map(generate, {...}), " * ") .. ")"
end)
-_G[Symbol("+"):tohash()] = Operator(function(...)
+-- Addition operator
+_ENV[Symbol("+"):tohash()] = Operator(function(...)
return "(".. List.concat(map(generate, {...}), " + ") .. ")"
end)
-_G[Symbol("-"):tohash()] = Operator(function(...)
+-- Subtraction and negation operator
+_ENV[Symbol("-"):tohash()] = Operator(function(...)
local parameters = {...}
if #parameters == 1 then
return "(-"..generate(parameters[1])..")"
end
return "(".. List.concat(map(generate, parameters), " - ") .. ")"
end)
-_G[Symbol("/"):tohash()] = Operator(function(...)
+-- Division operator
+_ENV[Symbol("/"):tohash()] = Operator(function(...)
local parameters = {...}
if #parameters == 1 then
return "(1 /"..generate(parameters[1])..")"
end
return "(".. List.concat(map(generate, parameters), " / ") .. ")"
end)
-_G[Symbol(".."):tohash()] = Operator(function(...)
+-- String concatenation operator
+_ENV[Symbol(".."):tohash()] = Operator(function(...)
local parameters = {...}
if #parameters == 1 then
return generate(parameters[1])
end
return "(".. List.concat(map(generate, parameters), " .. ") .. ")"
end)
-
+-- Is this node an atom?
atom = Operator(function(node)
return "(getmetatable("..generate(node)..")~=List)"
end)
-set = Operator(function(name, value)
- assert(getmetatable(name)==Symbol, "Expected Symbol: " .. tostring(name))
- return generate(name) .." = ".. generate(value)
-end)
-
+-- define local variables and execute some code with those locals in context
let = Operator(function(labels, ...)
assert(getmetatable(labels)==List, "Expected List: "..tostring(labels))
local declarations = {}
@@ -487,6 +544,11 @@ let = Operator(function(labels, ...)
return "(function()\n"..indent(body).."\nend)()"
end)
+-- works globally unless used on variable defined by (let (a v1 b v2) ...)
+set = Operator(function(name, value)
+ assert(getmetatable(name)==Symbol, "Expected Symbol: " .. tostring(name))
+ return generate(name) .." = ".. generate(value)
+end)
car = Operator(function(node)
return generate(node).."[1]"
end)
@@ -497,9 +559,10 @@ end)
cons = Operator(function(first, second)
assert(getmetatable(second) == List, "Expected List: " .. tostring(second))
- return ("setmetatable({%s, %s}, List)"):format(generate(first), generate(second))
+ return ("Pair(%s, %s)"):format(generate(first), generate(second))
end)
+-- Define anonymous function. e.g. (lambda (a b) (+ a b))
lambda = Operator(function(arguments, ...)
local arglist = List.concat(map(function(a)
assert(getmetatable(a) == Symbol, "Expected Symbol: "..tostring(arguments))
@@ -509,17 +572,103 @@ lambda = Operator(function(arguments, ...)
return "(function("..arglist..")\n"..indent(body).."\nend)"
end)
+-- Like lambda but named.
defun = Operator(function(name, arguments, ...)
assert(getmetatable(name) == Symbol, "Expected Symbol: "..tostring(name))
local arglist = List.concat(map(function(a)
assert(getmetatable(a) == Symbol, "Expected Symbol: "..tostring(a))
return a:tohash()
- end, arguments or {}))
+ end, arguments or {}), ",")
local body = compile({...})
name = name:tohash()
return "function "..name.."("..arglist..")\n"..indent(body).."\nend"
end)
+
+-- Shortcut function to generate sibling parse trees.
+-- E.g. for block of code in functions.
+function compile(iterable)
+ local body = map(generate, iterable)
+ if body then
+ body.last[1] = "return "..tostring(body.last[1])
+ end
+ body = List.concat(body, ";\n")
+ return body
+end
+
+defmacro = Operator(function(name, arguments, ...)
+ assert(getmetatable(name) == Symbol, "Expected Symbol: "..tostring(name))
+ local name = name:tohash()
+ local arglist = List.concat(map(function(a)
+ assert(getmetatable(a) == Symbol, "Expected Symbol: "..tostring(a))
+ return a:tohash()
+ end, arguments or {}), ",")
+
+ -- Actual macro body
+ local macrobody = map(generate, {...})
+ if macrobody then
+ macrobody.last[1] = "return generate("..tostring(macrobody.last[1])..")"
+ end
+ macrobody = List.concat(macrobody, ";\n")
+
+ -- Create macro expand version.
+ -- (Yes, we're duplicating work just for ease of use)
+ local expandbody = map(generate, {...})
+ if expandbody then
+ expandbody.last[1] = "return "..tostring(expandbody.last[1])
+ end
+ expandbody = List.concat(expandbody, ";\n")
+
+ _ENV[name] = Operator(load("return function("..arglist..")\n"..
+ indent(macrobody).."\nend", "defmacro", "t", _ENV)())
+ macroexpand[name] = Operator(load("return function("..arglist..")\n"
+ ..indent(expandbody).."\nend", "defmacro", "t", _ENV)())
+ return ""
+end)
+
+function eval(form)
+ return load("return "..generate(form), "eval", "t", _ENV)()
+end
+
+_ENV[Symbol("macroexpand-1"):tohash()] = function(form)
+ if getmetatable(form) ~= List then
+ return form, false
+ end
+ local first = parse(tolisp(form))[1]
+ if getmetatable(first) ~= Symbol then
+ return form, false
+ end
+ local name = first:tohash()
+ if not macroexpand[name] then
+ return form, false
+ end
+ local parameters = form:cdr()
+ return macroexpand[name](List.unpack(parameters)), true
+end
+
+-- Creating psuedo-functor object because macroexpand table will be used to
+-- store macroexpand functions for each macros.
+macroexpand = setmetatable({
+ lambda = function (form)
+ local macroexpand1 = _ENV[Symbol("macroexpand-1"):tohash()]
+ if getmetatable(form) ~= List then
+ return form
+ end
+ local expanded, first, rest
+ first = macroexpand(parse(tolisp(form))[1])
+ rest = map(macroexpand, form:cdr() or {})
+ form, expanded = macroexpand1(Pair(first, rest))
+
+ -- Recursively expand while expansion was performed successfully.
+ while expanded do
+ first = macroexpand(parse(tolisp(form))[1])
+ rest = map(macroexpand, form:cdr() or {})
+ form, expanded = macroexpand1(Pair(first, rest))
+ end
+ return form
+ end
+}, {__call = function(self, ...) return self.lambda(...) end})
+
quote = Operator(function(tree)
if getmetatable(tree) == List then
if tree[1] == nil and tree[2] == nil then
@@ -552,8 +701,11 @@ cond = Operator(function(...)
return "(function()"..body.."\nend)()"
end)
-execute = Operator(function(statement)
- load(generate(statement))()
+-- Compile time code execution
+directive = Operator(function(statement)
+ -- run code straight away, while being parsed.
+ load(generate(statement), "directive", "t", _ENV)()
+ -- emit empty lua code
return ""
end)
@@ -564,6 +716,8 @@ if not input or not output then
os.exit()
end
+-- Read file, compile code, write file.
+-- (And copy prelude from this compiler too)
source_file = io.open(input, "r")
source = source_file:read("*all")
source_file:close()
View
212 test.lsp
@@ -7,54 +7,124 @@
(print (! 100))
-(defun Σ () (print "ΣΣΣ"))
+; Output:
+; --- Example 1 ---
+;
+; 9.3326215443944e+157
-; Example 2: Acccessing functions from Lua environment
+; Example 2: Unicode Symbols
(print "\n--- Example 2 ---\n")
+(defun Σ () (print "ΣΣΣ"))
+(Σ)
+
+; Output:
+; --- Example 2 ---
+;
+; ΣΣΣ
+
+; Example 3: Acccessing functions from Lua environment
+(print "\n--- Example 3 ---\n")
(set hello-world "hello gibberish world")
(print (string.gsub hello-world "gibberish " ""))
-; Example 3: Quasiquote and unquote
-(print "\n--- Example 3 ---\n")
+; Output:
+; --- Example 3 ---
+;
+; hello world 1
+
+; Example 4: Quasiquote and unquote
+(print "\n--- Example 4 ---\n")
(map print `(1 2 3 ,(map (lambda (x) (* x 5)) '(1 2 3))))
; Note: prints all numbers only for lua 5.2. only 5.2 supports __ipairs override
-; Example 4: Let
-(print "\n--- Example 4 ---\n")
+; Output:
+; --- Example 4 ---
+;
+; 1
+; 2
+; 3
+; (5 10 15)
+
+; Example 5: Let
+(print "\n--- Example 5 ---\n")
(let (a (+ 1 2)
b (+ 3 4))
(print a)
(print b))
-; Example 5: Accessor method
-(print "\n--- Example 5 ---\n")
-(.write {"write" (lambda (self x) (print x))} "hello-world")
+; Output:
+; --- Example 5 ---
+;
+; 3
+; 7
-; Example 6: Anonymous function
+; Example 6: Accessor method
(print "\n--- Example 6 ---\n")
-(print ((lambda (x y) (+ x y)) 10 20))
+(.write {"write" (lambda (self x) (print x))} "hello-world")
-; Example 7: Directive (The '#' prefix)
+; Output:
+; --- Example 6 ---
+;
+; hello-world
+
+; Example 7: Anonymous function
(print "\n--- Example 7 ---\n")
+(print ((lambda (x y) (+ x y)) 10 20))
+
+; Output:
+; --- Example 7 ---
+;
+; 30
+
+; Example 8: Vector
+(print "\n--- Example 8 ---\n")
+(let (a (* 7 8))
+ (map print [1 2 a 4]))
+
+; Output:
+; --- Example 8 ---
+;
+; 1
+; 2
+; 56
+; 4
+
+; Example 9: Dictionary
+(print "\n--- Example 9 ---\n")
+(let (dict {"a" "b" 1 2 "3" 4})
+ (print dict["a"] "b")
+ (print dict.a "b")
+ (print dict[1] 2)
+ (print dict.3 4))
+
+; Output:
+; --- Example 9 ---
+;
+; b b
+; b b
+; 2 2
+; 4 4
+
+; Example 10: Directive (The '#' prefix)
+(print "\n--- Example 10 ---\n")
; The following line will run as soon as it is parsed, no code will be generated
; It will add a new "--" operator that will be effective immediately
#(set -- (Operator (lambda (str) (.. "-- " (tostring str)))))
; Adds a lua comment to lua executable, using operator we defined.
(-- "This is a comment") ; Will appear in `out.lua`
-; Example 8: Define a do block
-#(print "\n--- Example 8 ---\n")
+; Output:
+; --- Example 10 ---
+;
+
+; Example 11: Define a do block
+#(print "\n--- Example 11 ---\n")
; E.g. (do (print 1) (print 2)) will execute (print 1) and (print 2) in sequence
#(set do (Operator (lambda (...)
(.. "(function()\n"
(indent (compile [...]))
"\nend)()"))))
-(print "\n--- Example 8 ---\n")
-(print "\n--- Did you see what was printed while compiling? ---\n")
-(do
- (print 1)
- (print 2))
; We can now make this program be interpreted by wrapping code in "#(do ...)"!
@@ -64,24 +134,100 @@
(print (.. "1 + 1 = " (+ 1 1) "!"))
(print "Okay that's enough."))
+; Compiler Output:
+; --- Example 11 ---
+
+; I am running this line in the compilation step!
+; This too!
+; 1 + 1 = 2!
+; Okay that's enough.
+
+(print "\n--- Example 11 ---\n")
+(print "\n--- Did you see what was printed while compiling? ---\n")
+(do
+ (print 1)
+ (print 2))
+
+; Output:
+; --- Example 11 ---
+;
+;
+; --- Did you see what was printed while compiling? ---
+;
+; 1
+; 2
+
; We've had enough, so let's delete our do Operator
#(set do nil)
; Uncommenting the following will result in an error when compiling
; #(do (print 1))
-; Example 9: Vector
-(print "\n--- Example 9 ---\n")
-(let (a (* 7 8))
- (map print [1 2 a 4]))
-
-; Example 10: Dictionary
-(print "\n--- Example 10 ---\n")
-(let (dict {"a" "b" 1 2 "3" 4})
- (print dict["a"] "b")
- (print dict.a "b")
- (print dict[1] 2)
- (print dict.3 4))
-
-
+; Example 12: Macro
+(print "\n--- Example 12 ---\n")
+
+(defmacro if (condition action otherwise)
+ `(cond
+ (,condition ,action)
+ (true ,otherwise)))
+
+(let (a 2)
+ (if (== a "1") (print "a == 1")
+ (if (== a 2) (print "a == 2") (print "a != 2"))))
+
+; Lua Output:
+; print("\n--- Example 12 ---\n");
+;
+; (function()
+; local a = 2
+; return (function()
+; if ("1" == a) then
+; return print("a == 1")
+; end
+; if true then
+; return (function()
+; if (2 == a) then
+; return print("a == 2")
+; end
+; if true then
+; return print("a != 2")
+; end
+; end)()
+; end
+; end)()
+; end)();
+
+; Output:
+; --- Example 12 ---
+;
+; a == 2
+
+#(print "\n--- Example 12 ---\n")
+
+(defmacro GAMMA () '(+ 1 2))
+(defmacro BETA () '(.. (tostring (GAMMA)) "4"))
+(defmacro ALPHA () '(BETA))
+
+; Macros are compiler-time constructs. They are not visible during run-time.
+; So if we want to view their expansion, we must do it during compile-time too.
+#(print (macroexpand '(ALPHA)))
+
+; View the Lua representation of macro expansion
+; Not really supposed to do this... .lambda is a private variable for Operators.
+; Can only do this during compile time, due to implementation.
+; But just for fun....
+#(print (if.lambda 1 '(print 1) '(print 2)))
+
+; Compiler Output:
+; --- Example 12 ---
+;
+; (.. (tostring (+ 1 2)) 4)
+; (function()
+; if 1 then
+; return print(1)
+; end
+; if true then
+; return print(2)
+; end
+; end)()

0 comments on commit f0f7af7

Please sign in to comment.
Something went wrong with that request. Please try again.