1
0
Fork 0
mirror of https://github.com/Reuh/anselme.git synced 2025-10-28 00:59:31 +00:00

Anselme v2.0.0-alpha rewrite

Woke up and felt like changing a couple things. It's actually been worked on for a while, little at a time...

The goal was to make the language and implementation much simpler. Well I don't know if it really ended up being simpler but it sure is more robust.

Main changes:
* proper first class functions and closures supports! proper scoping rules! no more namespace shenanigans!
* everything is an expression, no more statements! make the implementation both simpler and more complex, but it's much more consistent now! the syntax has massively changed as a result though.
* much more organized and easy to modify codebase: one file for each AST node, no more random fields or behavior set by some random node exceptionally, everything should now follow the same API defined in ast.abstract.Node

Every foundational feature should be implemented right now. The vast majority of things that were possible in v2 are possible now; some things aren't, but that's usually because v2 is a bit more sane.
The main missing things before a proper release are tests and documentation. There's a few other things that might be implemented later, see the ideas.md file.
This commit is contained in:
Étienne Fildadut 2023-12-17 17:15:16 +01:00
parent 2ff494d108
commit fe351b5ca4
484 changed files with 7099 additions and 18084 deletions

60
stdlib/base.lua Normal file
View file

@ -0,0 +1,60 @@
local ast = require("ast")
local Pair, ArgumentTuple, Nil, String, Typed, Boolean = ast.Pair, ast.ArgumentTuple, ast.Nil, ast.String, ast.Typed, ast.Boolean
return {
{ "_;_", "(left, right)", function(state, left, right) return right end },
{ "_;", "(left)", function(state, left) return Nil:new() end },
{ "_:_", "(name, value)", function(state, a, b) return Pair:new(a,b) end },
{
"_::_", "(value, check)",
function(state, value, check)
local r = check:call(state, ArgumentTuple:new(value))
if r:truthy() then
return value
else
error(("type check failure: %s does not satisfy %s"):format(value:format(state), check:format(state)), 0)
end
end
},
{
"print", "(a)",
function(state, a)
print(a:format(state))
return Nil:new()
end
},
{
"hash", "(a)",
function(state, a)
return String:new(a:hash())
end
},
{
"type", "(value)",
function(state, v)
if v.type == "typed" then
return v.type_expression
else
return String:new(v.type)
end
end
},
{
"value", "(value)",
function(state, v)
if v.type == "typed" then
return v.expression
else
return v
end
end
},
{
"type", "(type, value)",
function(state, t, v)
return Typed:new(t, v)
end
},
{ "true", Boolean:new(true) },
{ "false", Boolean:new(false) },
}

42
stdlib/boolean.lua Normal file
View file

@ -0,0 +1,42 @@
local ast = require("ast")
local Boolean, ArgumentTuple = ast.Boolean, ast.ArgumentTuple
return {
{
"_==_", "(a, b)",
function(state, a, b)
if a.mutable ~= b.mutable then return Boolean:new(false)
elseif a.mutable then
return Boolean:new(a == b)
else
return Boolean:new(a:hash() == b:hash())
end
end
},
{
"!_", "(a)",
function(state, a)
return Boolean:new(not a:truthy())
end
},
{
"_&_", "(left, right)",
function(state, left, right)
if left:truthy() then
return right:call(state, ArgumentTuple:new())
else
return left
end
end
},
{
"_|_", "(left, right)",
function(state, left, right)
if left:truthy() then
return left
else
return right:call(state, ArgumentTuple:new())
end
end
},
}

2
stdlib/boot.ans Normal file
View file

@ -0,0 +1,2 @@
:@$is(t) $(x) x!type == t
:@$equal(x) $(y) x == y

View file

@ -1,16 +0,0 @@
-- Script run when creating a VM
return [[
(Built-in type definition)
::nil="nil"
::number="number"
::string="string"
::list="list"
::map="map"
::pair="pair"
::function reference="function reference"
::variable reference="variable reference"
::object="object"
::annotated="annotated"
::pi=3.1415926535898
]]

10
stdlib/checkpoint.lua Normal file
View file

@ -0,0 +1,10 @@
local resumable_manager = require("state.resumable_manager")
return {
{
"new checkpoint", "(level::number=0)",
function(state, level)
return resumable_manager:capture(state, level.number)
end
}
}

38
stdlib/closure.lua Normal file
View file

@ -0,0 +1,38 @@
local ast = require("ast")
local Nil, Boolean, Definition = ast.Nil, ast.Boolean, ast.Definition
return {
{
"defined", "(c::closure, s::string)",
function(state, c, s)
return Boolean:new(c.exported_scope:defined_in_current_strict(state, s:to_identifier()))
end
},
{
"_._", "(c::closure, s::string)",
function(state, c, s)
local identifier = s:to_identifier()
assert(c.exported_scope:defined_in_current_strict(state, identifier), ("no exported variable %q defined in closure"):format(s.string))
return c.exported_scope:get(state, identifier)
end
},
{
"_._", "(c::closure, s::string) = v",
function(state, c, s, v)
local identifier = s:to_identifier()
assert(c.exported_scope:defined_in_current_strict(state, identifier), ("no exported variable %q defined in closure"):format(s.string))
c.exported_scope:set(state, identifier, v)
return Nil:new()
end
},
{
"_._", "(c::closure, s::symbol) = v",
function(state, c, s, v)
assert(s.exported, "can't define a non-exported variable from the outside of the closure")
state.scope:push(c.exported_scope)
local r = Definition:new(s, v):eval(state)
state.scope:pop()
return r
end
}
}

64
stdlib/conditionals.lua Normal file
View file

@ -0,0 +1,64 @@
local ast = require("ast")
local ArgumentTuple, Nil, Boolean, Identifier = ast.ArgumentTuple, ast.Nil, ast.Boolean, ast.Identifier
local if_identifier = Identifier:new("_if_status")
local if_symbol = if_identifier:to_symbol()
local function ensure_if_variable(state)
if not state.scope:defined_in_current(if_symbol) then
state.scope:define(if_symbol, Boolean:new(false))
end
end
local function set_if_variable(state, bool)
state.scope:set(if_identifier, Boolean:new(bool))
end
local function last_if_success(state)
return state.scope:get(if_identifier):truthy()
end
return {
{
"_~_", "(condition, expression)", function(state, condition, expression)
ensure_if_variable(state)
if condition:truthy() then
set_if_variable(state, true)
return expression:call(state, ArgumentTuple:new())
else
set_if_variable(state, false)
return Nil:new()
end
end
},
{
"~_", "(expression)",
function(state, expression)
ensure_if_variable(state)
if last_if_success(state) then
return Nil:new()
else
set_if_variable(state, true)
return expression:call(state, ArgumentTuple:new())
end
end
},
{
"_~?_", "(condition, expression)",
function(state, condition, expression)
ensure_if_variable(state)
local cond = condition:call(state, ArgumentTuple:new())
local r
if cond:truthy() then
set_if_variable(state, true)
else
set_if_variable(state, false)
return Nil:new()
end
while cond:truthy() do
r = expression:call(state, ArgumentTuple:new())
cond = condition:call(state, ArgumentTuple:new())
end
return r
end
},
}

View file

@ -1,480 +0,0 @@
local truthy, anselme, compare, is_of_type, identifier_pattern, format_identifier, find, get_variable, mark_as_modified, set_variable, check_mutable, copy, mark_constant, hash
local lua_functions
lua_functions = {
-- discard left
["_;_(a, b)"] = {
mode = "raw",
value = function(a, b) return b end
},
["_;(a)"] = {
mode = "raw",
value = function(a) return { type = "nil", value = nil } end
},
-- comparaison
["_==_(a, b)"] = {
mode = "raw",
value = function(a, b)
return {
type = "number",
value = compare(a, b) and 1 or 0
}
end
},
["_!=_(a, b)"] = {
mode = "raw",
value = function(a, b)
return {
type = "number",
value = compare(a, b) and 0 or 1
}
end
},
["_>_(a::number, b::number)"] = function(a, b) return a > b end,
["_<_(a::number, b::number)"] = function(a, b) return a < b end,
["_>=_(a::number, b::number)"] = function(a, b) return a >= b end,
["_<=_(a::number, b::number)"] = function(a, b) return a <= b end,
-- arithmetic
["_+_(a::number, b::number)"] = function(a, b) return a + b end,
["_+_(a::string, b::string)"] = function(a, b) return a .. b end,
["_-_(a::number, b::number)"] = function(a, b) return a - b end,
["-_(a::number)"] = function(a) return -a end,
["_*_(a::number, b::number)"] = function(a, b) return a * b end,
["_/_(a::number, b::number)"] = function(a, b) return a / b end,
["_//_(a::number, b::number)"] = function(a, b) return math.floor(a / b) end,
["_%_(a::number, b::number)"] = function(a, b) return a % b end,
["_^_(a::number, b::number)"] = function(a, b) return a ^ b end,
-- boolean
["!_(a)"] = {
mode = "raw",
value = function(a)
return {
type = "number",
value = truthy(a) and 0 or 1
}
end
},
-- pair
["_=_(a, b)"] = {
mode = "raw",
value = function(a, b)
return {
type = "pair",
value = { a, b }
}
end
},
["_:_(a, b)"] = {
mode = "raw",
value = function(a, b)
return {
type = "pair",
value = { a, b }
}
end
},
-- annotate
["_::_(a, b)"] = {
mode = "raw",
value = function(a, b)
return {
type = "annotated",
value = { a, b }
}
end
},
-- namespace
["_._(r::function reference, name::string)"] = {
mode = "unannotated raw",
value = function(r, n)
local state = anselme.running.state
local rval = r.value
local name = n.value
for _, ffqm in ipairs(rval) do
local var, vfqm = find(state.aliases, state.interpreter.global_state.variables, "", ffqm.."."..name)
if var then
return get_variable(state, vfqm)
end
end
for _, ffqm in ipairs(rval) do
local fn, fnfqm = find(state.aliases, state.functions, "", ffqm.."."..name)
if fn then
return {
type = "function reference",
value = { fnfqm }
}
end
end
return nil, ("can't find variable %q in function reference (searched in namespaces: %s)"):format(name, table.concat(rval, ", "))
end
},
["_._(r::function reference, name::string) := v"] = {
mode = "unannotated raw",
value = function(r, n, v)
local state = anselme.running.state
local rval = r.value
local name = n.value
for _, ffqm in ipairs(rval) do
local var, vfqm = find(state.aliases, state.interpreter.global_state.variables, "", ffqm.."."..name)
if var then
local s, e = set_variable(state, vfqm, v)
if not s then return nil, e end
return v
end
end
return nil, ("can't find variable %q in function reference (searched in namespaces: %s)"):format(name, table.concat(rval, ", "))
end
},
["_._(r::object, name::string)"] = {
mode = "unannotated raw",
value = function(r, n)
local state = anselme.running.state
local obj = r.value
local name = n.value
-- attribute already present in object
local var = find(state.aliases, obj.attributes, "", obj.class.."."..name)
if var then return var end
-- search for attribute in base class
local cvar, cvfqm = find(state.aliases, state.interpreter.global_state.variables, "", obj.class.."."..name)
if cvar then return get_variable(state, cvfqm) end
-- search for method in base class
local fn, fnfqm = find(state.aliases, state.functions, "", obj.class.."."..name)
if fn then
return {
type = "function reference",
value = { fnfqm }
}
end
return nil, ("can't find attribute %q in object"):format(name)
end
},
["_._(r::object, name::string) := v"] = {
mode = "unannotated raw",
value = function(r, n, v)
local state = anselme.running.state
local obj = r.value
local name = n.value
-- check constant state
if r.constant then
return nil, "can't change the value of an attribute of a constant object"
end
-- attribute already present in object
local var, vfqm = find(state.aliases, obj.attributes, "", obj.class.."."..name)
if var then
if not check_mutable(state, vfqm) then return nil, "can't change the value of a constant attribute" end
obj.attributes[vfqm] = v
mark_as_modified(anselme.running.state, obj.attributes)
return v
end
-- search for attribute in base class
local cvar, cvfqm = find(state.aliases, state.interpreter.global_state.variables, "", obj.class.."."..name)
if cvar then
if not check_mutable(state, cvfqm) then return nil, "can't change the value of a constant attribute" end
obj.attributes[cvfqm] = v
mark_as_modified(anselme.running.state, obj.attributes)
return v
end
return nil, ("can't find attribute %q in object"):format(name)
end
},
-- index
["()(l::list, i::number)"] = {
mode = "unannotated raw",
value = function(l, i)
local index = i.value
if index < 0 then index = #l.value + 1 + index end
if index > #l.value or index == 0 then return nil, "list index out of bounds" end
return l.value[index] or { type = "nil", value = nil }
end
},
["()(l::map, k)"] = {
mode = "raw",
value = function(l, i)
local lv = l.type == "annotated" and l.value[1] or l
local h, err = hash(i)
if not h then return nil, err end
local v = lv.value[h]
if v then
return v[2]
else
return { type = "nil", value = nil }
end
end
},
-- index assignment
["()(l::list, i::number) := v"] = {
mode = "raw",
value = function(l, i, v)
local lv = l.type == "annotated" and l.value[1] or l
local iv = i.type == "annotated" and i.value[1] or i
if lv.constant then return nil, "can't change the contents of a constant list" end
local index = iv.value
if index < 0 then index = #lv.value + 1 + index end
if index > #lv.value + 1 or index == 0 then return nil, "list assignment index out of bounds" end
lv.value[index] = v
mark_as_modified(anselme.running.state, lv.value)
return v
end
},
["()(l::map, k) := v::nil"] = {
mode = "raw",
value = function(l, k, v)
local lv = l.type == "annotated" and l.value[1] or l
if lv.constant then return nil, "can't change the contents of a constant map" end
local h, err = hash(k)
if not h then return nil, err end
lv.value[h] = nil
mark_as_modified(anselme.running.state, lv.value)
return v
end
},
["()(l::map, k) := v"] = {
mode = "raw",
value = function(l, k, v)
local lv = l.type == "annotated" and l.value[1] or l
if lv.constant then return nil, "can't change the contents of a constant map" end
local h, err = hash(k)
if not h then return nil, err end
lv.value[h] = { k, v }
mark_as_modified(anselme.running.state, lv.value)
return v
end
},
["()(fn::function reference, l...)"] = {
-- bypassed, this case is manually handled in the expression interpreter
},
["_!(fn::function reference)"] = {
-- bypassed, this case is manually handled in the expression interpreter
},
["_!(fn::variable reference)"] = {
mode = "unannotated raw",
value = function(v)
return get_variable(anselme.running.state, v.value)
end
},
["&_(v::variable reference)"] = {
mode = "unannotated raw",
value = function(v) return v end
},
["&_(fn::function reference)"] = {
mode = "unannotated raw",
value = function(v) return v end
},
-- format
["{}(v)"] = {
mode = "raw",
value = function(v)
return v
end
},
-- alias
["alias(ref::function reference, alias::string)"] = {
mode = "unannotated raw",
value = function(ref, alias)
-- check identifiers
alias = alias.value
local aliasfqm = alias:match("^"..identifier_pattern.."$")
if not aliasfqm then error(("%q is not a valid identifier for an alias"):format(alias)) end
aliasfqm = format_identifier(aliasfqm)
-- define alias
for _, fnfqm in ipairs(ref.value) do
local aliases = anselme.running.state.aliases
if aliases[aliasfqm] ~= nil and aliases[aliasfqm] ~= fnfqm then
error(("trying to define alias %q for %q, but already exist and refer to %q"):format(aliasfqm, fnfqm, aliases[alias]))
end
aliases[aliasfqm] = fnfqm
end
return { type = "nil" }
end
},
["alias(ref::variable reference, alias::string)"] = {
mode = "unannotated raw",
value = function(ref, alias)
-- check identifiers
alias = alias.value
local aliasfqm = alias:match("^"..identifier_pattern.."$")
if not aliasfqm then error(("%q is not a valid identifier for an alias"):format(alias)) end
aliasfqm = format_identifier(aliasfqm)
-- define alias
local aliases = anselme.running.state.aliases
if aliases[aliasfqm] ~= nil and aliases[aliasfqm] ~= ref.value then
error(("trying to define alias %q for %q, but already exist and refer to %q"):format(aliasfqm, ref.value, aliases[alias]))
end
aliases[aliasfqm] = ref.value
return { type = "nil" }
end
},
-- pair methods
["name(p::pair)"] = {
mode = "unannotated raw",
value = function(a)
return a.value[1]
end
},
["value(p::pair)"] = {
mode = "unannotated raw",
value = function(a)
return a.value[2]
end
},
-- list methods
["len(l::list)"] = {
mode = "unannotated raw", -- raw to count pairs in the list
value = function(a)
return {
type = "number",
value = #a.value
}
end
},
["insert(l::list, v)"] = {
mode = "raw",
value = function(l, v)
local lv = l.type == "annotated" and l.value[1] or l
if lv.constant then return nil, "can't insert values into a constant list" end
table.insert(lv.value, v)
mark_as_modified(anselme.running.state, lv.value)
return l
end
},
["insert(l::list, i::number, v)"] = {
mode = "raw",
value = function(l, i, v)
local lv = l.type == "annotated" and l.value[1] or l
local iv = i.type == "annotated" and i.value[1] or i
if lv.constant then return nil, "can't insert values into a constant list" end
table.insert(lv.value, iv.value, v)
mark_as_modified(anselme.running.state, lv.value)
return l
end
},
["remove(l::list)"] = {
mode = "unannotated raw",
value = function(l)
if l.constant then return nil, "can't remove values from a constant list" end
mark_as_modified(anselme.running.state, l.value)
return table.remove(l.value)
end
},
["remove(l::list, i::number)"] = {
mode = "unannotated raw",
value = function(l, i)
if l.constant then return nil, "can't remove values from a constant list" end
mark_as_modified(anselme.running.state, l.value)
return table.remove(l.value, i.value)
end
},
["find(l::list, v)"] = {
mode = "raw",
value = function(l, v)
local lv = l.type == "annotated" and l.value[1] or l
for i, x in ipairs(lv.value) do
if compare(x, v) then
return i
end
end
return { type = "number", value = 0 }
end
},
-- string
["len(s::string)"] = function(s)
return require("utf8").len(s)
end,
-- other methods
["error(m::string)"] = function(m) error(m, 0) end,
["rand()"] = function() return math.random() end,
["rand(a::number)"] = function(a) return math.random(a) end,
["rand(a::number, b::number)"] = function(a, b) return math.random(a, b) end,
["floor(a::number)"] = function(a) return math.floor(a) end,
["ceil(a::number)"] = function(a) return math.ceil(a) end,
["round(a::number, increment=1::number)"] = function(a, increment)
local n = a / increment
if n >= 0 then
return math.floor(n + 0.5) * increment
else
return math.ceil(n - 0.5) * increment
end
end,
["unannotated(v)"] = {
mode = "raw",
value = function(v)
if v.type == "annotated" then
return v.value[1]
else
return v
end
end
},
["type(v)"] = {
mode = "unannotated raw",
value = function(v)
return {
type = "string",
value = v.type
}
end
},
["annotation(v::annotated)"] = {
mode = "raw",
value = function(v)
return v.value[2]
end
},
["is a(v, t)"] = {
mode = "raw",
value = function(v, t)
return {
type = "number",
value = is_of_type(v, t) or 0
}
end
},
["constant(v)"] = {
mode = "raw",
value = function(v)
local c = copy(v)
mark_constant(c)
return c
end
}
}
local anselme_functions = [[
:$ random(l...)
~ l(rand(1, l!len))!
:$ next(l...)
:j = 0
~? j += 1; j < len(l) & l(j).👁 != 0
~ l(j)!
:$ cycle(l...)
:f = l(1)
:j = 1
~? j += 1; j <= len(l) & !((f := l(j); 1) ~ l(j).👁 < f.👁)
~ f!
:$ concat(l::list, separator=""::string)
:r = ""
:j = 0
~? j += 1; j <= len(l)
~ r += "{l(j)}"
~ j < len(l)
~ r += separator
@r
]]
local functions = {
lua = lua_functions,
anselme = anselme_functions
}
package.loaded[...] = functions
local icommon = require((...):gsub("stdlib%.functions$", "interpreter.common"))
truthy, compare, is_of_type, get_variable, mark_as_modified, set_variable, check_mutable, mark_constant, hash = icommon.truthy, icommon.compare, icommon.is_of_type, icommon.get_variable, icommon.mark_as_modified, icommon.set_variable, icommon.check_mutable, icommon.mark_constant, icommon.hash
local pcommon = require((...):gsub("stdlib%.functions$", "parser.common"))
identifier_pattern, format_identifier, find = pcommon.identifier_pattern, pcommon.format_identifier, pcommon.find
anselme = require((...):gsub("stdlib%.functions$", "anselme"))
copy = require((...):gsub("stdlib%.functions$", "common")).copy
return functions

36
stdlib/init.lua Normal file
View file

@ -0,0 +1,36 @@
local parser = require("parser")
local function define_lua(state, list)
for _, fn in ipairs(list) do
state.scope:define_lua(fn[1], fn[2], fn[3], true)
end
end
local function load(state, l)
for _, m in ipairs(l) do
define_lua(state, require("stdlib."..m))
end
end
return function(main_state)
load(main_state, {
"boolean",
"tag",
"conditionals",
"base",
"type_check"
})
local f = assert(io.open("stdlib/boot.ans"))
local boot = parser(f:read("*a"), "boot.ans")
f:close()
boot:eval(main_state)
load(main_state, {
"number",
"string",
"text",
"structures",
"closure",
"checkpoint"
})
end

View file

@ -1,6 +0,0 @@
return [[
(Built-in variables)
::alias 👁 = "seen"
::alias 🔖 = "checkpoint"
::alias 🏁 = "reached"
]]

View file

@ -1,33 +0,0 @@
return [[
(Types)
~ &nil!alias("nul")
~ &number!alias("nombre")
~ &string!alias("texte")
~ &list!alias("liste")
~ &map!alias("dictionnaire")
~ &pair!alias("paire")
~ &function reference!alias("réference de fonction")
~ &variable reference!alias("réference de variable")
~ &object!alias("objet")
~ &annotated!alias("annoté")
(Built-in functions)
~ &name!alias("nom")
~ &value!alias("valeur")
~ &len!alias("longueur")
~ &insert!alias("ajouter")
~ &remove!alias("retirer")
~ &find!alias("trouver")
~ &error!alias("erreur")
~ &rand!alias("aléa")
~ &is a!alias("est un")
~ &unannotated!alias("non annoté")
~ &cycle!alias("cycler")
~ &random!alias("aléatoire")
~ &next!alias("séquence")
(Built-in variables)
::alias 👁 = "vu"
::alias 🔖 = "checkpoint"
::alias 🏁 = "atteint"
]]

49
stdlib/number.lua Normal file
View file

@ -0,0 +1,49 @@
local ast = require("ast")
local Boolean, Number = ast.Boolean, ast.Number
return {
{
"_<_", "(a::number, b::number)",
function(state, a, b)
if a.number < b.number then return b
else return Boolean:new(false)
end
end
},
{ "_<_", "(a::equal(false), b::number)", function(state, a, b) return Boolean:new(false) end },
{
"_<=_", "(a::number, b::number)",
function(state, a, b)
if a.number <= b.number then return b
else return Boolean:new(false)
end
end
},
{ "_<=_", "(a::equal(false), b::number)", function(state, a, b) return Boolean:new(false) end },
{
"_>_", "(a::number, b::number)",
function(state, a, b)
if a.number > b.number then return b
else return Boolean:new(false)
end
end
},
{ "_>_", "(a::equal(false), b::number)", function(state, a, b) return Boolean:new(false) end },
{
"_>=_", "(a::number, b::number)",
function(state, a, b)
if a.number >= b.number then return b
else return Boolean:new(false)
end
end
},
{ "_>=_", "(a::equal(false), b::number)", function(state, a, b) return Boolean:new(false) end },
{ "_+_", "(a::number, b::number)", function(state, a, b) return Number:new(a.number + b.number) end },
{ "_-_", "(a::number, b::number)", function(state, a, b) return Number:new(a.number - b.number) end },
{ "_*_", "(a::number, b::number)", function(state, a, b) return Number:new(a.number * b.number) end },
{ "_/_", "(a::number, b::number)", function(state, a, b) return Number:new(a.number / b.number) end },
{ "_//_", "(a::number, b::number)", function(state, a, b) return Number:new(a.number // b.number) end },
{ "_%_", "(a::number, b::number)", function(state, a, b) return Number:new(a.number % b.number) end },
{ "_^_", "(a::number, b::number)", function(state, a, b) return Number:new(a.number ^ b.number) end },
{ "-_", "(a::number)", function(state, a) return Number:new(-a.number) end },
}

6
stdlib/string.lua Normal file
View file

@ -0,0 +1,6 @@
local ast = require("ast")
local String = ast.String
return {
{ "_+_", "(a::string, b::string)", function(state, a, b) return String:new(a.string .. b.string) end }
}

87
stdlib/structures.lua Normal file
View file

@ -0,0 +1,87 @@
local ast = require("ast")
local Nil, List, Table, Number = ast.Nil, ast.List, ast.Table, ast.Number
return {
-- tuple
{
"*_", "(t::tuple)",
function(state, tuple)
return List:new(state, tuple)
end
},
{
"_!", "(l::tuple, i::number)",
function(state, l, i)
return l:get(i.number)
end
},
-- list
{
"_!", "(l::list, i::number)",
function(state, l, i)
return l:get(state, i.number)
end
},
{
"_!", "(l::list, i::number) = value",
function(state, l, i, v)
l:set(state, i.number, v)
return Nil:new()
end
},
{
"insert", "(l::list, value)",
function(state, l, v)
l:insert(state, v)
return Nil:new()
end
},
{
"len", "(l::list)",
function(state, l)
return Number:new(l:len(state))
end
},
{
"to tuple", "(l::list)",
function(state, l)
return l:to_tuple(state)
end
},
-- struct
{
"*_", "(s::struct)",
function(state, struct)
return Table:new(state, struct)
end
},
{
"_!", "(s::struct, key)",
function(state, s, k)
return s:get(k)
end
},
-- table
{
"_!", "(t::table, key)",
function(state, t, key)
return t:get(state, key)
end
},
{
"_!", "(t::table, key) = value",
function(state, t, key, value)
t:set(state, key, value)
return Nil:new()
end
},
{
"to struct", "(t::table)",
function(state, t)
return t:to_struct(state)
end
},
}

27
stdlib/tag.lua Normal file
View file

@ -0,0 +1,27 @@
local ast = require("ast")
local Tuple, Table, Struct, ArgumentTuple = ast.Tuple, ast.Table, ast.Struct, ast.ArgumentTuple
local tag_manager = require("state.tag_manager")
return {
{
"_#_", "(tags, expression)",
function(state, tags, expression)
local tags_struct
if Tuple:is(tags) and not tags.explicit then
tags_struct = Struct:from_tuple(tags):eval(state)
elseif Struct:is(tags) then
tags_struct = tags
elseif Table:is(tags) then
tags_struct = tags:to_struct(state)
else
tags_struct = Struct:from_tuple(Tuple:new(tags)):eval(state)
end
tag_manager:push(state, tags_struct)
local v = expression:call(state, ArgumentTuple:new())
tag_manager:pop(state)
return v
end
}
}

24
stdlib/text.lua Normal file
View file

@ -0,0 +1,24 @@
local ast = require("ast")
local Nil, Choice = ast.Nil, ast.Choice
local event_manager = require("state.event_manager")
return {
-- text
{
"_!", "(txt::text)",
function(state, text)
event_manager:write(state, text)
return Nil:new()
end
},
-- choice
{
"_|>_", "(txt::text, fn)",
function(state, text, func)
event_manager:write(state, Choice:new(text, func))
return Nil:new()
end
},
}

20
stdlib/type_check.lua Normal file
View file

@ -0,0 +1,20 @@
local ast = require("ast")
local Boolean = ast.Boolean
return {
{ "number", "(x)", function(state, x) return Boolean:new(x.type == "number") end },
{ "string", "(x)", function(state, x) return Boolean:new(x.type == "string") end },
{ "boolean", "(x)", function(state, x) return Boolean:new(x.type == "boolean") end },
{ "symbol", "(x)", function(state, x) return Boolean:new(x.type == "symbol") end },
{ "text", "(x)", function(state, x) return Boolean:new(x.type == "text") end },
{ "tuple", "(x)", function(state, x) return Boolean:new(x.type == "tuple") end },
{ "list", "(x)", function(state, x) return Boolean:new(x.type == "list") end },
{ "struct", "(x)", function(state, x) return Boolean:new(x.type == "struct") end },
{ "table", "(x)", function(state, x) return Boolean:new(x.type == "table") end },
{ "closure", "(x)", function(state, x) return Boolean:new(x.type == "closure") end },
{ "overload", "(x)", function(state, x) return Boolean:new(x.type == "overload") end },
{ "function", "(x)", function(state, x) return Boolean:new(x.type == "overload" or x.type == "closure" or x.type == "funciton" or x.type == "lua function") end },
}

View file

@ -1,412 +0,0 @@
local format, to_lua, from_lua, events, anselme, escape, hash, update_hashes, get_variable, find_function_variant_from_fqm, post_process_text, traverse
local types = {}
types.lua = {
["nil"] = {
to_anselme = function(val)
return {
type = "nil",
value = nil
}
end
},
boolean = {
to_anselme = function(val)
return {
type = "number",
value = val and 1 or 0
}
end
},
number = {
to_anselme = function(val)
return {
type = "number",
value = val
}
end
},
string = {
to_anselme = function(val)
return {
type = "string",
value = val
}
end
},
table = {
to_anselme = function(val)
local is_map = false
local l = {}
local m = {}
for _, v in ipairs(val) do
local r, e = from_lua(v)
if not r then return r, e end
table.insert(l, r)
end
for k, v in pairs(val) do
if not l[k] then
is_map = true
local kv, ke = from_lua(k)
if not k then return k, ke end
local vv, ve = from_lua(v)
if not v then return v, ve end
local h, err = hash(kv)
if not h then return nil, err end
m[h] = { kv, vv }
end
end
if is_map then
for i, v in ipairs(l) do
local key = { type = "number", value = i }
local h, err = hash(key)
if not h then return nil, err end
m[h] = { key, v }
end
return {
type = "map",
value = m
}
else
return {
type = "list",
value = l
}
end
end
}
}
types.anselme = {
["nil"] = {
format = function()
return ""
end,
to_lua = function()
return nil
end,
hash = function()
return "nil()"
end,
traverse = function() return true end,
},
number = {
format = function(val)
return tostring(val)
end,
to_lua = function(val)
return val
end,
hash = function(val)
return ("n(%s)"):format(val)
end,
traverse = function() return true end,
},
string = {
format = function(val)
return tostring(val)
end,
to_lua = function(val)
return val
end,
hash = function(val)
return ("s(%s)"):format(val)
end,
traverse = function() return true end,
},
pair = {
format = function(val)
local k, ke = format(val[1])
if not k then return k, ke end
local v, ve = format(val[2])
if not v then return v, ve end
return ("%s=%s"):format(k, v)
end,
to_lua = function(val, state)
local k, ke = to_lua(val[1], state)
if ke then return nil, ke end
local v, ve = to_lua(val[2], state)
if ve then return nil, ve end
return { [k] = v }
end,
hash = function(val)
local k, ke = hash(val[1])
if not k then return k, ke end
local v, ve = hash(val[2])
if not v then return v, ve end
return ("p(%s=%s)"):format(k, v)
end,
traverse = function(val, callback, pertype_callback)
local k, ke = traverse(val[1], callback, pertype_callback)
if not k then return k, ke end
local v, ve = traverse(val[2], callback, pertype_callback)
if not v then return v, ve end
return true
end,
},
annotated = {
format = function(val)
local k, ke = format(val[1])
if not k then return k, ke end
local v, ve = format(val[2])
if not v then return v, ve end
return ("%s::%s"):format(k, v)
end,
to_lua = function(val, state)
local k, ke = to_lua(val[1], state)
if ke then return nil, ke end
return k
end,
hash = function(val)
local k, ke = hash(val[1])
if not k then return k, ke end
local v, ve = hash(val[2])
if not v then return v, ve end
return ("a(%s::%s)"):format(k, v)
end,
traverse = function(val, callback, pertype_callback)
local k, ke = traverse(val[1], callback, pertype_callback)
if not k then return k, ke end
local v, ve = traverse(val[2], callback, pertype_callback)
if not v then return v, ve end
return true
end,
},
list = {
mutable = true,
format = function(val)
local l = {}
for _, v in ipairs(val) do
local s, e = format(v)
if not s then return s, e end
table.insert(l, s)
end
return ("[%s]"):format(table.concat(l, ", "))
end,
to_lua = function(val, state)
local l = {}
for _, v in ipairs(val) do
local s, e = to_lua(v, state)
if e then return nil, e end
table.insert(l, s)
end
return l
end,
hash = function(val)
local l = {}
for _, v in ipairs(val) do
local s, e = hash(v)
if not s then return s, e end
table.insert(l, s)
end
return ("l(%s)"):format(table.concat(l, ","))
end,
traverse = function(val, callback, pertype_callback)
for _, item in ipairs(val) do
local s, e = traverse(item, callback, pertype_callback)
if not s then return s, e end
end
return true
end,
mark_constant = function(v)
v.constant = true
end,
},
map = {
mutable = true,
format = function(val)
local l = {}
for _, v in pairs(val) do
local ks, ke = format(v[1])
if not ks then return ks, ke end
local vs, ve = format(v[2])
if not vs then return vs, ve end
table.insert(l, ("%s=%s"):format(ks, vs))
end
table.sort(l)
return ("{%s}"):format(table.concat(l, ", "))
end,
to_lua = function(val, state)
local l = {}
for _, v in pairs(val) do
local kl, ke = to_lua(v[1], state)
if ke then return nil, ke end
local xl, xe = to_lua(v[2], state)
if xe then return nil, xe end
l[kl] = xl
end
return l
end,
hash = function(val)
local l = {}
for _, v in pairs(val) do
local ks, ke = hash(v[1])
if not ks then return ks, ke end
local vs, ve = hash(v[2])
if not vs then return vs, ve end
table.insert(l, ("%s=%s"):format(ks, vs))
end
table.sort(l)
return ("m(%s)"):format(table.concat(l, ","))
end,
traverse = function(val, callback, pertype_callback)
for _, v in pairs(val) do
local ks, ke = traverse(v[1], callback, pertype_callback)
if not ks then return ks, ke end
local vs, ve = traverse(v[2], callback, pertype_callback)
if not vs then return vs, ve end
end
return true
end,
mark_constant = function(v)
v.constant = true
update_hashes(v)
end,
},
object = {
mutable = true,
format = function(val)
local attributes = {}
for name, v in pairs(val.attributes) do
table.insert(attributes, ("%s=%s"):format(name:gsub("^"..escape(val.class)..".", ""), format(v)))
end
if #attributes > 0 then
table.sort(attributes)
return ("%%%s(%s)"):format(val.class, table.concat(attributes, ", "))
else
return ("%%%s"):format(val.class)
end
end,
to_lua = function(val, state)
local r = {}
local namespacePattern = "^"..escape(val.class).."%."
-- set object properties
for name, v in pairs(val.attributes) do
local var, err = to_lua(v, state)
if err then return nil, err end
r[name:gsub(namespacePattern, "")] = var
end
-- set class properties
local class, err = find_function_variant_from_fqm(val.class, state, nil)
if not class then return nil, err end
assert(#class == 1 and class[1].subtype == "class")
class = class[1]
for _, prop in ipairs(class.properties) do
if not val.attributes[prop] then
local var
var, err = get_variable(state, prop)
if not var then return nil, err end
var, err = to_lua(var, state)
if err then return nil, err end
r[prop:gsub(namespacePattern, "")] = var
end
end
return r
end,
hash = function(val)
local attributes = {}
for name, v in pairs(val.attributes) do
table.insert(attributes, ("%s=%s"):format(name:gsub("^"..escape(val.class)..".", ""), format(v)))
end
table.sort(attributes)
return ("%%(%s;%s)"):format(val.class, table.concat(attributes, ","))
end,
traverse = function(v, callback, pertype_callback)
for _, attrib in pairs(v.attributes) do
local s, e = traverse(attrib, callback, pertype_callback)
if not s then return s, e end
end
return true
end,
mark_constant = function(v)
v.constant = true
end,
},
["function reference"] = {
format = function(val)
if #val > 1 then
return ("&(%s)"):format(table.concat(val, ", "))
else
return ("&%s"):format(table.concat(val, ", "))
end
end,
to_lua = nil,
hash = function(val)
return ("&f(%s)"):format(table.concat(val, ", "))
end,
traverse = function() return true end,
},
["variable reference"] = {
format = function(val)
return ("&%s"):format(val)
end,
to_lua = nil,
hash = function(val)
return ("&v(%s)"):format(val)
end,
traverse = function() return true end,
},
-- event buffer: can only be used outside of Anselme internals for text & flush events (through text buffers)
["event buffer"] = {
format = function(val) -- triggered from subtexts
local v, e = events:write_buffer(anselme.running.state, val)
if not v then return v, e end
return ""
end,
to_lua = function(val, state)
local r = {}
for _, event in ipairs(val) do
if event.type == "text" then
table.insert(r, { "text", post_process_text(state, event.value) })
elseif event.type == "flush" then
table.insert(r, { "flush" })
else
return nil, ("event %q in event buffer can't be converted to a Lua value"):format(event.type)
end
end
return r
end,
hash = function(val)
local l = {}
for _, event in ipairs(val) do
if event.type == "text" then
local text = {}
for _, t in ipairs(event.value) do
local str = ("s(%s)"):format(t.text)
local tags, e = hash(t.tags)
if not tags then return nil, e end
table.insert(text, ("%s#%s"):format(str, tags))
end
table.insert(l, ("text(%s)"):format(table.concat(text, ",")))
elseif event.type == "flush" then
table.insert(l, "flush")
else
return nil, ("event %q in event buffer cannot be hashed"):format(event.type)
end
end
return ("eb(%s)"):format(table.concat(l, ","))
end,
traverse = function(val, callback, pertype_callback)
for _, event in ipairs(val) do
if event.type == "text" then
for _, t in ipairs(event.value) do
local s, e = traverse(t.tags, callback, pertype_callback)
if not s then return s, e end
end
elseif event.type ~= "flush" then
return nil, ("event %q in event buffer cannot be traversed"):format(event.type)
end
end
return true
end,
},
}
package.loaded[...] = types
local common = require((...):gsub("stdlib%.types$", "interpreter.common"))
format, to_lua, from_lua, events, hash, update_hashes, get_variable, post_process_text, traverse = common.format, common.to_lua, common.from_lua, common.events, common.hash, common.update_hashes, common.get_variable, common.post_process_text, common.traverse
anselme = require((...):gsub("stdlib%.types$", "anselme"))
local pcommon = require((...):gsub("stdlib%.types$", "parser.common"))
escape, find_function_variant_from_fqm = pcommon.escape, pcommon.find_function_variant_from_fqm
return types