From 55264a1c979b937ba2a2b3aae169414c4e8596c7 Mon Sep 17 00:00:00 2001 From: n451 <2020200706@ruc.edu.cn> Date: Sat, 25 May 2024 02:27:34 +0800 Subject: [PATCH] maxi mostly working! RL repl? --- rockspecs/modal-dev-1.rockspec | 2 +- src/{maxi => core}/maxi.lua | 133 +++++++++++-------- src/core/mini.lua | 210 +++++++++++++++++++++++++++++- src/core/pattern.lua | 228 ++------------------------------- src/maxi/maxi_old.lua | 184 -------------------------- src/repl/repl.lua | 21 +-- src/repl/rrepl.lua | 34 +++++ 7 files changed, 347 insertions(+), 465 deletions(-) rename src/{maxi => core}/maxi.lua (75%) delete mode 100644 src/maxi/maxi_old.lua create mode 100644 src/repl/rrepl.lua diff --git a/rockspecs/modal-dev-1.rockspec b/rockspecs/modal-dev-1.rockspec index 323d444..a4232dc 100644 --- a/rockspecs/modal-dev-1.rockspec +++ b/rockspecs/modal-dev-1.rockspec @@ -49,7 +49,7 @@ build = { ["modal.chords"] = "src/core/chords.lua", ["modal.mini"] = "src/core/mini.lua", - ["modal.maxi"] = "src/maxi/maxi.lua", + ["modal.maxi"] = "src/core/maxi.lua", ["modal.repl"] = "src/repl/repl.lua", }, diff --git a/src/maxi/maxi.lua b/src/core/maxi.lua similarity index 75% rename from src/maxi/maxi.lua rename to src/core/maxi.lua index 071a751..0cdf422 100644 --- a/src/maxi/maxi.lua +++ b/src/core/maxi.lua @@ -1,13 +1,12 @@ -local P, S, V, R, C, Ct -do - local _obj_0 = require("lpeg") - P, S, V, R, C, Ct, Cc = _obj_0.P, _obj_0.S, _obj_0.V, _obj_0.R, _obj_0.C, _obj_0.Ct -end --- local tinsert, sequence, group, slice, sub_cycle, polymeter, slow_sequence, polymeter_steps, stack, stack_or_choose, polymeter_stack, dotStack, choose, step, slice_with_ops, op, fast, slow, replicate, degrade, weight, euclid, tail, range, AtomStub, PatternStub, ElementStub, id, seed, ws, comma, pipe, dot, quote, parseNumber, parseStep, step_char, minus, plus, zero, digit, decimal_point, digit1_9, e, int, intneg, exp, frac, number, parseFast, parseSlow, parseTail, parseRange, parseDegrade, parseEuclid, parseWeight, parseReplicate, parseSlices, parsePolymeter, parseSlowSeq, parseDotTail, parseStack, parseDotStack, parseChoose, parseStackOrChoose, parseSubCycle, grammar +local lpeg = require("lpeg") +P, S, V, R, C, Ct = lpeg.P, lpeg.S, lpeg.V, lpeg.R, lpeg.C, lpeg.Ct +local reduce = require("modal.utils").reduce +local filter = require("modal.utils").filter --- local pp = require("metalua.pprint").print +local mlc = require("metalua.compiler").new() +local mpp = require("metalua.pprint").print local moon = require("moon") -local tinsert = table.insert + local sequence = V("sequence") local slice = V("slice") local sub_cycle = V("sub_cycle") @@ -15,9 +14,6 @@ local polymeter = V("polymeter") local slow_sequence = V("slow_sequence") local polymeter_steps = V("polymeter_steps") local stack = V("stack") -local polymeter_stack = V("polymeter_stack") -local dotStack = V("dotStack") --- local step = V("step") local slice_with_ops = V("slice_with_ops") local op = V("op") local fast = V("fast") @@ -29,7 +25,10 @@ local euclid = V("euclid") local tail = V("tail") local range = V("range") local list = V("list") -local fn = V("fn") +local dollar = V("dollar") +local tailop = V("tailop") +-- local topcall = V("topcall") +-- local fn = V("fn") local set = V("set") Id = function(a) @@ -79,8 +78,8 @@ local parseStep = function(chars) return { tag = "String", chars } end --- local step_char = R("09", "AZ", "az") + P("-") + P("#") + P(".") + P("^") + P("_") + P("~") / id -local step_char = R("09", "AZ", "az") + P("'") + P("-") + P("#") + P(".") + P("^") + P("_") + P("~") / id +-- local step_char = R("09", "AZ", "az") + P("'") + P("-") + P("#") + P(".") + P("^") + P("_") + P("~") / id +local step_char = R("09", "AZ", "az") + P("'") + P("-") + P(".") + P("^") + P("_") + P("~") / id local step = ws * (((step_char ^ 1) + P("+") + P("-") + P("*") + P("/") + P("%")) / parseStep) * ws - P(".") local minus = P("-") local plus = P("+") @@ -117,12 +116,12 @@ local parseDegrade = function(a) end local parseTail = function(s) - return function(x) - return tinsert(x.options.ops, { - type = "tail", - arguments = { element = s }, - }) - end + -- return function(x) + -- return tinsert(x.options.ops, { + -- type = "tail", + -- arguments = { element = s }, + -- }) + -- end end local parseRange = function(s) @@ -191,7 +190,15 @@ local pstack = function(...) return args, true end -local reduce = require("modal.utils").reduce +-- TODO: expand to all tidal ops +local ptailop = function(...) + local args = { ... } + args.tag = "Call" + args[1].tag = "Id" + return function(x) + return { tag = "Op", "concat", x, args } + end +end local use_timecat = function(args) local addWeight = function(a, b) @@ -262,9 +269,26 @@ local function is_op(a) return opsymb[a] end +local resolvetails = function(args, fname) + local params = filter(function(a) + return type(a) ~= "function" + end, args) + local tails = filter(function(a) + return type(a) == "function" + end, args) + local main = { tag = "Call", fname, unpack(params) } + for i = 1, #tails do + main = tails[i](main) + end + return main +end + +-- TODO: set into this local function plist(...) local args = { ... } - if #args == 3 then + if #args == 1 then + return args + elseif #args == 3 then if is_op(args[2][1]) then local opname = opsymb[args[2][1]] table.remove(args, 2) @@ -278,7 +302,15 @@ local function plist(...) local fname = args[1] fname.tag = "Id" table.remove(args, 1) - return { tag = "Call", fname, unpack(args) } + return resolvetails(args, fname) +end + +local function pdollar(...) + local args = { ... } + local fname = args[1] + fname.tag = "Id" + table.remove(args, 1) + return resolvetails(args, fname) end -- TODO: weight in polymeter @@ -290,19 +322,19 @@ local grammar = { "root", -- root = fn + set + list + slice_with_ops, - root = set + list + slice_with_ops, + root = set + list + slice_with_ops + dollar, -- fn = P"fn" * ws * step * param * body, - list = P("(") * ws * (step + list + slice_with_ops) ^ 0 * ws * P(")") / plist, - -- + dotStack + choose + stack + sequence, - set = step * ws * P("=") * ws * (step + list + slice_with_ops) / pset, + list = P("(") * ws * (step + list + slice_with_ops + dollar + tailop) ^ 0 * ws * P(")") / plist, + dollar = P("$") * ws * (step + list + slice_with_ops + dollar + tailop) ^ 0 * ws / pdollar, + set = P("(") * ws * step * ws * P("=") * ws * (step + list + slice_with_ops) * ws * P(")") / pset, sequence = (slice_with_ops ^ 1) / pseq, stack = slice_with_ops * (comma * slice_with_ops) ^ 1 / pstack, -- choose = sequence * (pipe * sequence) ^ 1 / parseChoose, -- dotStack = sequence * (dot * sequence) ^ 1 / parseDotStack, + tailop = P("#") * ws * step * ws * slice_with_ops * ws / ptailop, slice_with_ops = (slice * op ^ 0) / parseSlices, slice = step + sub_cycle + polymeter + slow_sequence, sub_cycle = P("[") * ws * (stack + sequence) * ws * P("]") / parseSubCycle, - -- sub_cycle = P("[") * ws * sequence * ws * P("]") / parseSubCycle, slow_sequence = P("<") * ws * sequence * ws * P(">") / parseSlowSeq, polymeter = P("{") * ws * sequence * ws * P("}") * polymeter_steps ^ -1 * ws / parsePolymeter, polymeter_steps = P("%") * slice, @@ -325,36 +357,37 @@ local read = function(str) return grammar:match(str)[2] end -local mlc = require("metalua.compiler").new() - -local mpp = require("metalua.pprint").print - local function eval(src, env) - env = env and env or _G - local ast = read(src) - -- mpp(ast) - ast = { tag = "Return", ast } - + -- env = env and env or _G + local ok, res, ast, f + ok, ast = pcall(read, src) + if not ok then + return ast, false + end + if ast.tag ~= "Set" then + ast = { tag = "Return", ast } + end local lua_src = mlc:ast_to_src(ast) - local f = loadstring(lua_src) - f = setfenv(f, _G) - return f() -end - --- setfenv - -if _VERSION == "Lua 5.2" then - function setfenv(f, env) - return load(string.dump(f), nil, nil, env) + ok, f = pcall(loadstring, lua_src) + if not ok then + return f, false end + f = setfenv(f, env) + ok, res = pcall(f) + return res, ok end +-- if _VERSION == "Lua 5.2" then +-- function setfenv(f, env) +-- return load(string.dump(f), nil, nil, env) +-- end +-- end + local function to_lua(src) local ast = read(src) local lua_src = mlc:ast_to_src(ast) return lua_src end +-- TODO: > fast 2 $ s [bd sd] # room 0.2 --- print(eval("[ 37 'a ]")) - -return { eval = eval, read = read, to_lua = to_lua } +return { eval = eval, to_lua = to_lua } diff --git a/src/core/mini.lua b/src/core/mini.lua index 5db1197..1ea21b1 100644 --- a/src/core/mini.lua +++ b/src/core/mini.lua @@ -30,6 +30,8 @@ euclid = V("euclid") tail = V("tail") range = V("range") +local map = require("modal.utils").map + AtomStub = function(source) return { type = "atom", source = source } end @@ -138,6 +140,7 @@ parseWeight = function(a) x.options.weight = (x.options.weight or 1) + (tonumber(a) or 2) - 1 end end + parseReplicate = function(a) return function(x) x.options.reps = (x.options.reps or 1) + (tonumber(a) or 2) - 1 @@ -221,4 +224,209 @@ parse = function(str) return grammar:match(str)[2] end -return { parse = parse } +local function applyOptions(parent, enter) + return function(pat, i) + local ast = parent.source[i] + local ops = nil + if ast.options then + ops = ast.options.ops + end + if ops then + for _index_0 = 1, #ops do + local op = ops[_index_0] + local _exp_0 = op.type + if "stretch" == _exp_0 then + local type_, amount = op.arguments.type, op.arguments.amount + local _exp_1 = type_ + if "fast" == _exp_1 then + pat = fast(enter(amount), pat) + elseif "slow" == _exp_1 then + pat = slow(enter(amount), pat) + else + print("mini: stretch: type must be one of fast of slow") + end + elseif "degradeBy" == _exp_0 then + local amount = op.arguments.amount or 0.5 + pat = degradeBy(amount, pat) + elseif "euclid" == _exp_0 then + local steps, pulse, rotation = op.arguments.steps, op.arguments.pulse, op.arguments.rotation + if rotation == 0 then + pat = euclid(enter(pulse), enter(steps), 0, pat) + else + pat = euclid(enter(pulse), enter(steps), enter(rotation), pat) + end + elseif "tail" == _exp_0 then + local friend = enter(op.arguments.element) + pat = appLeft( + fmap(pat, function(a) + return function(b) + if T(a) == "table" then + tinsert(a, b) + return a + else + return { + a, + b, + } + end + end + end), + friend + ) + --- TODO: broken! + elseif "range" == _exp_0 then + local friend = enter(op.arguments.element) + local makeRange + makeRange = function(start, stop) + local _accum_0 = {} + local _len_0 = 1 + for _ = start, stop do + _accum_0[_len_0] = i + _len_0 = _len_0 + 1 + end + return _accum_0 + end + local f + f = function(apat, bpat) + return squeezeBind(apat, function(a) + return bind(bpat, function(b) + return fastcat(makeRange(a, b), friend) + end) + end) + end + pat = f(pat, friend) + end + end + end + return pat + end +end + +local function resolveReplications(ast) + local repChild = function(child) + if child.options == nil then + return { child } + end + local reps = child.options.reps + child.options.reps = nil + local _accum_0 = {} + local _len_0 = 1 + for i = 1, reps do + _accum_0[_len_0] = child + _len_0 = _len_0 + 1 + end + return _accum_0 + end + local unflat + do + local _accum_0 = {} + local _len_0 = 1 + local _list_0 = ast.source + for _index_0 = 1, #_list_0 do + local child = _list_0[_index_0] + _accum_0[_len_0] = repChild(child) + _len_0 = _len_0 + 1 + end + unflat = _accum_0 + end + local res = {} + for _index_0 = 1, #unflat do + local element = unflat[_index_0] + for _index_1 = 1, #element do + local elem = element[_index_1] + tinsert(res, elem) + end + end + ast.source = res + return ast +end + +local function patternifyAST(ast, M) + local enter = function(node) + return patternifyAST(node) + end + local _exp_0 = ast.type + if "element" == _exp_0 then + return enter(ast.source) + elseif "atom" == _exp_0 then + if ast.source == "~" then + return silence() + end + local value = ast.source + if tonumber(value) then + value = tonumber(value) + end + return pure(value) + elseif "pattern" == _exp_0 then + ast = resolveReplications(ast) + local children = ast.source + children = map(enter, children) + do + local _accum_0 = {} + local _len_0 = 1 + for index, child in pairs(children) do + _accum_0[_len_0] = applyOptions(ast, enter)(child, index) + _len_0 = _len_0 + 1 + end + children = _accum_0 + end + local alignment = ast.arguments.alignment + local _exp_1 = alignment + if "stack" == _exp_1 then + return stack(children) + elseif "polymeter_slowcat" == _exp_1 then + local aligned = map(function(child) + return slow(#(firstCycle(child)), child) + end, children) + return stack(aligned) + elseif "polymeter" == _exp_1 then + local stepsPerCycle = ast.arguments.stepsPerCycle and enter(ast.arguments.stepsPerCycle) or 1 + print(stepsPerCycle) + local aligned = map(function(child) + return fast( + fmap(reify(stepsPerCycle), function(x) + if x == 1 then + return 1 + end -- HACK: + return x / #(firstCycle(child)) + end), + child + ) + end, children) + return stack(aligned) + elseif "rand" == _exp_1 then + return randcat(children) + end + local addWeight + addWeight = function(a, b) + b = b.options and b.options.weight or 1 + return a + b + end + local weightSum = reduce(addWeight, 0, ast.source) + if weightSum > #children then + local atoms = ast.source + local pat = timecat((function() + local _accum_0 = {} + local _len_0 = 1 + for i, v in pairs(atoms) do + _accum_0[_len_0] = { + v.options.weight or 1, + children[i], + } + _len_0 = _len_0 + 1 + end + return _accum_0 + end)()) + return pat + end + return fastcat(children) + end +end + +return function(M) + return function(code) + local ast = parse(code) + setfenv(patternifyAST, M) + return patternifyAST(ast) + end +end diff --git a/src/core/pattern.lua b/src/core/pattern.lua index 2e248ee..90a49c7 100644 --- a/src/core/pattern.lua +++ b/src/core/pattern.lua @@ -29,7 +29,7 @@ do Event, Span, State = types.Event, types.Span, types.State end -local parse = require("modal.mini").parse +-- local parse = require("modal.mini").parse local reify, pure, silence local bindWhole, bind, innerBind, outerBind, innerJoin, outerJoin local fmap, firstCycle, querySpan @@ -50,210 +50,7 @@ local tinsert = table.insert local M = {} local U = {} -local function applyOptions(parent, enter) - return function(pat, i) - local ast = parent.source[i] - local ops = nil - if ast.options then - ops = ast.options.ops - end - if ops then - for _index_0 = 1, #ops do - local op = ops[_index_0] - local _exp_0 = op.type - if "stretch" == _exp_0 then - local type_, amount = op.arguments.type, op.arguments.amount - local _exp_1 = type_ - if "fast" == _exp_1 then - pat = M.fast(enter(amount), pat) - elseif "slow" == _exp_1 then - pat = M.slow(enter(amount), pat) - else - print("mini: stretch: type must be one of fast of slow") - end - elseif "degradeBy" == _exp_0 then - local amount = op.arguments.amount or 0.5 - pat = M.degradeBy(amount, pat) - elseif "euclid" == _exp_0 then - local steps, pulse, rotation = op.arguments.steps, op.arguments.pulse, op.arguments.rotation - if rotation == 0 then - pat = M.euclid(enter(pulse), enter(steps), 0, pat) - else - pat = M.euclid(enter(pulse), enter(steps), enter(rotation), pat) - end - elseif "tail" == _exp_0 then - local friend = enter(op.arguments.element) - pat = appLeft( - fmap(pat, function(a) - return function(b) - if T(a) == "table" then - tinsert(a, b) - return a - else - return { - a, - b, - } - end - end - end), - friend - ) - --- TODO: broken! - elseif "range" == _exp_0 then - local friend = enter(op.arguments.element) - local makeRange - makeRange = function(start, stop) - local _accum_0 = {} - local _len_0 = 1 - for _ = start, stop do - _accum_0[_len_0] = i - _len_0 = _len_0 + 1 - end - return _accum_0 - end - local f - f = function(apat, bpat) - return squeezeBind(apat, function(a) - return bind(bpat, function(b) - return M.fastcat(makeRange(a, b), friend) - end) - end) - end - pat = f(pat, friend) - end - end - end - return pat - end -end - -local function resolveReplications(ast) - local repChild = function(child) - if child.options == nil then - return { child } - end - local reps = child.options.reps - child.options.reps = nil - local _accum_0 = {} - local _len_0 = 1 - for i = 1, reps do - _accum_0[_len_0] = child - _len_0 = _len_0 + 1 - end - return _accum_0 - end - local unflat - do - local _accum_0 = {} - local _len_0 = 1 - local _list_0 = ast.source - for _index_0 = 1, #_list_0 do - local child = _list_0[_index_0] - _accum_0[_len_0] = repChild(child) - _len_0 = _len_0 + 1 - end - unflat = _accum_0 - end - local res = {} - for _index_0 = 1, #unflat do - local element = unflat[_index_0] - for _index_1 = 1, #element do - local elem = element[_index_1] - tinsert(res, elem) - end - end - ast.source = res - return ast -end - -local function patternifyAST(ast) - local enter = function(node) - return patternifyAST(node) - end - local _exp_0 = ast.type - if "element" == _exp_0 then - return enter(ast.source) - elseif "atom" == _exp_0 then - if ast.source == "~" then - return M.silence() - end - local value = ast.source - if tonumber(value) then - value = tonumber(value) - end - return pure(value) - elseif "pattern" == _exp_0 then - ast = resolveReplications(ast) - local children = ast.source - children = map(enter, children) - do - local _accum_0 = {} - local _len_0 = 1 - for index, child in pairs(children) do - _accum_0[_len_0] = applyOptions(ast, enter)(child, index) - _len_0 = _len_0 + 1 - end - children = _accum_0 - end - local alignment = ast.arguments.alignment - local _exp_1 = alignment - if "stack" == _exp_1 then - return M.stack(children) - elseif "polymeter_slowcat" == _exp_1 then - local aligned = map(function(child) - return M.slow(#(firstCycle(child)), child) - end, children) - return M.stack(aligned) - elseif "polymeter" == _exp_1 then - local stepsPerCycle = ast.arguments.stepsPerCycle and enter(ast.arguments.stepsPerCycle) or 1 - print(stepsPerCycle) - local aligned = map(function(child) - return M.fast( - fmap(reify(stepsPerCycle), function(x) - if x == 1 then - return 1 - end -- HACK: - return x / #(firstCycle(child)) - end), - child - ) - end, children) - return M.stack(aligned) - elseif "rand" == _exp_1 then - return M.randcat(children) - end - local addWeight - addWeight = function(a, b) - b = b.options and b.options.weight or 1 - return a + b - end - local weightSum = reduce(addWeight, 0, ast.source) - if weightSum > #children then - local atoms = ast.source - local pat = M.timecat((function() - local _accum_0 = {} - local _len_0 = 1 - for i, v in pairs(atoms) do - _accum_0[_len_0] = { - v.options.weight or 1, - children[i], - } - _len_0 = _len_0 + 1 - end - return _accum_0 - end)()) - return pat - end - return M.fastcat(children) - end -end - -local function mini(code) - local ast = parse(code) - return patternifyAST(ast) -end -M.mini = mini +M.mini = require("modal.mini")(M) function fmap(pat, func) return withValue(pat, func) @@ -476,9 +273,9 @@ splitQueries = function(pat) end return Pattern(query) end + withValue = function(pat, func) - local query - query = function(_, state) + local query = function(_, state) local events = pat:query(state) local f f = function(event) @@ -488,6 +285,7 @@ withValue = function(pat, func) end return Pattern(query) end + withQuerySpan = function(pat, func) local query query = function(_, state) @@ -704,7 +502,8 @@ M.pure = pure reify = function(thing) local t = T(thing) if "string" == t then - return mini(thing) + -- return M.mini(thing, M) + return pure(thing) elseif "pattern" == t then return thing else @@ -721,7 +520,7 @@ patternify[2] = function(func) if pat == nil then return curry(func, 2)(apat) end - apat = reify(apat) + apat, pat = reify(apat), reify(pat) local mapFn = function(a) return func(a, pat) end @@ -1124,7 +923,6 @@ function M.struct(boolpat, pat) end register("euclid", function(n, k, offset, pat) - print(n, k, offset, pat) return M.struct(bjork(n, k, offset), reify(pat)) end) @@ -1238,8 +1036,8 @@ end M.sl = string_lambda -pp = function(x) - if T(x) == "table" then +M.print = function(x) + if type(x) == "table" then for k, v in pairs(x) do print(k, v) end @@ -1247,10 +1045,10 @@ pp = function(x) return print(x) end end -M.print = pp + M.id = id +M.maxi = require("modal.maxi").eval --- pp(M.mini("1 .. 9")) --- pp(M.iota(1, 9)) +M.T = T return M diff --git a/src/maxi/maxi_old.lua b/src/maxi/maxi_old.lua deleted file mode 100644 index 6f7870c..0000000 --- a/src/maxi/maxi_old.lua +++ /dev/null @@ -1,184 +0,0 @@ -require("moon.all") - -local pp = require("metalua.pprint").print -local lpeg = require("lpeg") - -local V, R, P, S = lpeg.V, lpeg.R, lpeg.P, lpeg.S -local C, Ct = lpeg.C, lpeg.Ct - -local root = V("root") -local symb = V("symb") -local str = V("str") -local call = V("call") -local def = V("def") -local unquote = V("unquote") -local dollar = V("dollar") -local list = V("list") -local op = V("op") -local item = V("item") -local mini = V("mini") -local pr = V("pr") -local pm = V("pm") -local spm = V("spm") - -local slice_with_ops = V("slice_with_ops") -local mop = V("mop") -local fast = V("fast") -local slow = V("slow") - -local function Id(a) - return { tag = "Id", a } -end - -local function unquote(a) - for i, v in pairs(a) do - if v.tag == "Quote" then - if v[1].tag == "Id" then - v[1].tag = "String" - elseif v[1].tag == "String" then - v[1].tag = "Id" - end - a[i] = v[1] - end - if v.tag == "Id" then - v.tag = "String" - a[i] = v - end - end - return a -end - -local function ppr(...) - local res = unquote({ ... }) - return { tag = "Call", Id("fastcat"), unpack(res) } -end - -local function pspm(...) - local res = unquote({ ... }) - return { tag = "Call", Id("slowcat"), unpack(res) } -end - -local function psymb(s) - return { tag = "Id", s } -end - -local function pnum(n) - return { tag = "Number", n } -end - -local opsymb = { - ["+"] = "add", - ["-"] = "sub", - ["*"] = "mul", - ["/"] = "div", - ["^"] = "pow", - ["%"] = "mod", - -- TODO: tidal ops!! -} - -local function is_op(a) - return opsymb[a] -end - -local function pdot(...) - return { ... } -end - -local function plist(args) - if #args == 3 then - if is_op(args[2][1]) then - local opname = opsymb[args[2][1]] - table.remove(args, 2) - return { tag = "Op", opname, unpack(args) } - elseif is_op(args[1][1]) then - local opname = opsymb[args[1][1]] - table.remove(args, 1) - return { tag = "Op", opname, unpack(args) } - elseif args[1].tag == "Id" then - local fname = args[1] - table.remove(args, 1) - return { tag = "Call", fname, unpack(args) } - end - end -end - -local function punquote(a) - return { tag = "Quote", a } -end - -local function pslice(...) - local args = { ... } - local acc = args[1] - table.remove(args, 1) - for _, v in pairs(args) do - acc = { tag = "Call", Id(v.tag), v[1], acc } - end - return acc -end - -local ws = S(" \n\r\t") ^ 0 -local minus = P("-") -local plus = P("+") -local zero = P("0") -local digit = R("09") -local decimal_point = P(".") -local digit1_9 = R("19") -local e = S("eE") -local int = zero + (digit1_9 * digit ^ 0) -local intneg = minus ^ -1 * int -local exp = e * (minus + plus) ^ -1 * digit ^ 1 -local frac = decimal_point * digit ^ 1 -local num = (minus ^ -1 * int * frac ^ -1 * exp ^ -1) / pnum -local slice = V("slice") -local sequence = V("sequence") - -local pseq = function(...) - return { ... } -end - -local rules = { - [1] = root, - op = S("-+/&%^"), - root = ws * (dollar + call + symb + list + mini) * ws, - symb = ws * ((R("AZ", "az", "09") + op) ^ 1 / psymb) * ws, - - unquote = P("`") * (num + symb + list) / punquote, - item = num + symb + list + mini + unquote + dollar, - - sequence = (slice_with_ops ^ 1) / pseq, - slice = num + symb + mini + unquote, - call = (ws * item * ws) ^ 1 / pdot, - mini = pr + spm, - - pr = P("[") * ws * sequence * ws * P("]") / ppr, - spm = P("<") * ws * sequence * ws * P(">") / pspm, - - list = P("(") * call * P(")") / plist, - dollar = P("$") * ws * call / plist, - - slice_with_ops = (item * mop ^ 0) / pslice, - mop = fast + slow, - fast = P("*") * item / function(a) - return { tag = "fast", a } - end, - slow = P("/") * item / function(a) - return { tag = "slow", a } - end, -} - -local pat = Ct(C(rules)) - -local function read(src) - return pat:match(src)[2] -end - -local mlc = require("metalua.compiler").new() - -local function eval(src) - local ast = read(src) - local lua_src = mlc:ast_to_src(ast) - return loadstring("require'modal'(); a = fast(2, reify(1)); return " .. lua_src)() -end - -pp(eval([[ [a b] ]])) -return eval diff --git a/src/repl/repl.lua b/src/repl/repl.lua index ab94cf4..2c27afa 100644 --- a/src/repl/repl.lua +++ b/src/repl/repl.lua @@ -1,8 +1,7 @@ -require("modal")() +-- require("modal")() +M = require("modal") local uv = require("luv") -local yue = require("yue") --- local inspect = require("modal.inspect") -clock = DefaultClock +local clock = M.DefaultClock clock:start() @@ -10,17 +9,11 @@ clock:start() -- TODO: completion local eval = function(a) if a then - local lua_code = yue.to_lua(a) - local func, err = loadstring(lua_code) - if func then - local ok, res = pcall(func) - if ok then - -- print(res) - else - print("Execution error: not a meaningful function to call") - end + local res, ok = M.maxi("(" .. a .. ")", M) + if ok then + print(res) else - print("Compilation error: " .. err) + print("Compilation error: " .. res) end end return ">" diff --git a/src/repl/rrepl.lua b/src/repl/rrepl.lua new file mode 100644 index 0000000..e351a6a --- /dev/null +++ b/src/repl/rrepl.lua @@ -0,0 +1,34 @@ +local RL = require("readline") +local M = require("modal") +local clock = M.DefaultClock + +local keywords = {} +for i, _ in pairs(M) do + table.insert(keywords, i) +end + +RL.set_complete_list(keywords) + +clock:start() + +local eval = function(a) + if a then + local res, ok = M.maxi("(" .. a .. ")", M) + if ok then + print(res) + else + print("Compilation error: " .. res) + end + end +end + +local line + +while true do + coroutine.resume(clock.notifyCoroutine) + line = RL.readline("modal> ") + if not line then + break + end + eval(line) +end