Skip to content

Commit

Permalink
Add more documentation
Browse files Browse the repository at this point in the history
  • Loading branch information
AlphaRunic committed Jul 10, 2021
1 parent 46ebca9 commit b1779f0
Show file tree
Hide file tree
Showing 4 changed files with 191 additions and 102 deletions.
133 changes: 129 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,17 @@ Luay includes utilities such as Java's StringBuilder class, Node's EventEmitter

## Main Method

Luay brings a feature to Lua that many other programming languages support, the "main" method. A Luay program must have an entry point, so it looks for a global function called "main". If it cannot find "main", it then looks for a global class called "Program", subsequently a "Main" method inside of the "Program" class. If it doesn't find either, the following error is thrown:
Luay brings a feature to Lua that many other programming languages support, the `main` method. A Luay program must have an entry point, so it looks for a global function called `main`. If it cannot find `main`, it then looks for a global class called `Program`, subsequently a `Main` method inside of the `Program` class. If it doesn't find either, the following error is thrown:
```
[Luay] Your program lacks a 'main' function or 'Program' class with 'Main' method, therefore it can not run.
```

## Strings

Strings in Luay support a limited set of standard arithmetic operators to manipulate strings with. These include: +, unary -, unary ~, *, and >>.
Here's an example of what each operator does:
Strings in Luay support a limited set of standard arithmetic operators to manipulate strings with. These include: `+`, unary `-`, unary `~`, `*`, and `>>`.
Here's what each operator does:
```lua
-- concatenation
-- standard concatenation
print("hello " + "world") --> hello world

-- reverse concatenation
Expand All @@ -42,6 +42,10 @@ for char in ~"hello world" do
end
```

## Data Structures

Luay's standard library (see <a href="#stdio">StdIO</a>) comes with many data structure classes so you don't have to make them yourself. These include: `Vector<T>`, `List`, `Map`, `Stack`, `Queue`, `Deque`, `Set`, `Pair`, `KeyValuePair`, `StrictPair`, and `StrictKeyValuePair`. Yeah, it's a handful, and more are coming. Each index-value styled data structure can be iterated over using the `~` operator, as seen in <a href="#strings">Strings</a>. Otherwise, most data structures have a :Keys or :Indices method to iterate over keys and values.

## StdIO

Luay's standard library can be included in your program by calling
Expand Down Expand Up @@ -86,6 +90,127 @@ function main()
end
```

## Object Oriented

Object oriented programming is made easy in Luay using a set of functions to create classes. Since Luay is only an embedded version of Lua, changing the grammar could cause more problems than just incompatibility with <a href="#main-method">Main Methods</a>. Thus, we have these functions in Luay: `class(name: string) -> Class`, `extend(class: Class, super: instanceof Class) -> void`, `constructor(class: Class, body?: function) -> ClassInstance`, and `namespace(name: string) -> ((body: table) -> {alias = (name: string) -> void})`. Here's each one of them in use, to show you the syntax:

1. Single Class
```lua
Animal = class "Animal" do
function Animal.new(name)
return constructor(Animal, function(self)
self.name = name
end)
end

function Animal:Speak(sound)
printf "The {self.name} says: {sound}"
end
end

local dog = Animal("Dog")
dog:Speak("Woof!") --> The Dog says: Woof!
```

Now let's make a class for the dog itself.

2. Inheritance
```lua
...

Dog = class "Dog" do
function Dog.new(breed)
extend(Dog, Animal("Dog"))
return constructor(Dog, function(self)
self.breed = breed
end)
end

function Dog:Bark()
self:Speak("Woof!")
end
end

local dog2 = Dog("Border Collie")
dog2:Bark() --> The Dog says: Woof!
print(dog.breed) --> Border Collie
```

Onto static classes. Static classes are different than classes with static and regular methods. Static classes contain only static methods and have no constructor. Static classes are different from classes with regular methods and static methods because you can encapsulate state in a static class (in an easier manner). A good example of a static class is the `Program` class that Luay looks for if a `main` function is not found.

3. Static Classes
```lua
using(luay.std)

Program = class "Program" do
Program.mode = "default"

function Program:DoOperation()
if self.mode == "default" then
print "doing things normally..."
elseif self.mode == "verbose" then
print "doing things LOUDLY..."
elseif self.mode == "silent" then
print "doing things *quietly*..."
else
throw(Error(f"Invalid mode: '{self.mode}'"))
end
end

function Program:Main(argc, argv)
local args = Vector("string", argv)
args:Shift()

if args:First() then
self.mode = args:First():lower()
end

self:DoOperation()
end
end
```

3. Classes with Static and Regular Methods
```lua
using(luay.std)

Array = class "Array" do
function Array.new()
return constructor(Array, function(self)
self.cache = {}
end)
end

function Array.from(tab)
local arr = Array()
for v in values(tab) do
arr:Add(v)
end
return arr
end

function Array:Add(value)
table.insert(self.cache, value)
return self
end

function Array:Remove(idx)
table.remove(self.cache, idx)
return self
end

function Array:__repr()
repr(self.cache)
end
end

local arr = Array.from {"foo", "bar", "baz"}
arr:Add("luay")
repr(arr) --> {"foo", "bar", "baz", "luay"}
arr:Remove(2)
repr(arr)--> {"foo", "baz", "luay"}
```

## Lambdas

Yes, you read that right. Lambdas are a shorthand way of writing an anonymous function that represents data. For example, say I have a list of numbers. I want to double each value in that list. Normally, you could do it like this in standard Luay:
Expand Down
78 changes: 60 additions & 18 deletions lib/core.lua
Original file line number Diff line number Diff line change
Expand Up @@ -11,45 +11,67 @@ function import(module)
return
end

---
function singleton(name)
local body = _ENV[name]
local lib = body[1]
local mod = {}
mod[name] = lib
return setmetatable(mod, {
__newindex = function(self, k, v)
throw(std.Error("Cannot write to singleton"))
throw(luay.std.Error("cannot write to singleton"))
end;

__tostring = function()
return f"<singleton '{name}'>"
return ("<singleton '%s'>"):format(name);
end
})
end

--- Creates a namespace
--- and injects it into
--- the global scope. An
--- alias for the namespace
--- is optional using
---@see NamespaceDeclaration
---@param name string
---@return [[(body: table) -> NamespaceDeclaration]]
function namespace(name)
---@param body table
return function(body)
local state = {name = name}

local meta = {
---@meta NamespaceMeta
local meta = {
__newindex = function(self, k, v)
throw(std.Error("Cannot write to namespace"))
throw(luay.std.Error("cannot write to namespace"))
end;

---@return string
__tostring = function()
return ("<namespace '%s'>"):format(state.name);
end
}

_ENV[name] = setmetatable(body, meta)

local namespaceBody = setmetatable(body, meta)
_ENV[name] = namespaceBody
---@class NamespaceDeclaration
return {
alias = function(_, alias)
--- Sets an alias for the
--- namespace when tostring
--- is called on it.
---@param self NamespaceDeclaration
---@param alias string
alias = function(self, alias)
state.name = alias
end
}
end
end

---@param self table
---@param instance table
function extend(self, instance)
self.__super = instance
setmetatable(self, {
Expand All @@ -76,9 +98,13 @@ local function classmeta(cls)
return { __index = cls }
end

---@param name string
function class(name)
return cast(setmetatable({}, {
__call = function(self, ...)
if not self.new then
throw(luay.std.Error("cannot instantiate static class"), 1)
end
return self.new(...)
end
}), name)
Expand All @@ -89,47 +115,60 @@ local function instance(classBody)
return setmetatable({ meta = meta }, meta)
end

---@param body table
---@param initializer? function
function constructor(body, initializer)
local self = instance(body)
initializer(self)
;(initializer or function() end)(self)
return self
end

---@deprecated since 7/9/21
---@param body table
function defaultConstructor(body)
return constructor(body, function(self) end)
end

function typeof(v)
if type(v) == "table" and v.__type then
return v.__type
---@param value any
function typeof(value)
if type(value) == "table" and value.__type then
return value.__type
else
return type(v)
return type(value)
end
end

function instanceof(v, t)
return typeof(v) == t
---@param value any
---@param t type
function instanceof(value, t)
return typeof(value) == t
end

function cast(v, t)
assert(v ~= nil and type(v) == "table", "value to cast is nil or not a table")
---@param value unknown
---@param t type
function cast(value, t)
assert(value ~= nil and type(value) == "table", "value to cast is nil or not a table")
assert(t ~= nil and type(t) == "string", "must provide a valid type to cast to, got: " + type(t))
v.__type = t
return v
value.__type = t
return value
end

---@param err Error
---@param level integer
function throw(err, level)
assert(type(err) == "table" and err.message ~= nil, "cannot throw error of type '" + typeof(err) + "', ")
assert(type(err) == "table" and err.message, "cannot throw error of type '" + typeof(err) + "', ")
error(colors("%{red}" + err.message), 2 + (level or 0))
end

---@param name string
function enum(name)
return function(body)
assert(type(body) == "table", "cannot create enum with body of type '" + type(body) + "'")
_ENV[name] = table.inverse(body)
end
end

---@param fn function
function getfenv(fn)
local i = 1
while true do
Expand All @@ -143,6 +182,8 @@ function getfenv(fn)
end
end

---@param fn function
---@param env table
function setfenv(fn, env)
local i = 1
while true do
Expand All @@ -160,6 +201,7 @@ function setfenv(fn, env)
return fn
end

---@param content string
function lambda(content)
assert(typeof(content) == "string", "lambda function converts a string to a function expression")
assert(content:Includes("|") and not content:IsBlank(), "malformed lambda")
Expand Down
4 changes: 2 additions & 2 deletions lib/std.lua
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ do
elseif data_type == 'nil' then
io.write('nil')
else
io.write(tostring(data))
io.write((not tostring(data) or tostring(data) == "") and "nil" or tostring(data))
end

if level == 1 or data_type == 'table' then
Expand Down Expand Up @@ -1272,7 +1272,7 @@ do
local Stream = class "Stream" do
function Stream.new()
extend(Stream, EventEmitter())
return defaultConstructor(Stream)
return constructor(Stream)
end

function Stream:Pipe(dest, opts)
Expand Down
Loading

0 comments on commit b1779f0

Please sign in to comment.