local net, live = {}, {} local function neuron(name) if not net[name] then net[name] = { name = name, alive = false, sleep = false, arity = 0, saturation = 0, excite = {}, inhibit = {}, } end return net[name] end local function tokenize(program) local tokens, current_token = {}, {} for char in program:gmatch(".") do if char:match "%s" then current_token = {} elseif char:match "[:;.]" then current_token = {} table.insert(tokens, char) elseif char == "*" then if #current_token ~= 0 then table.insert(tokens, table.concat(current_token)) current_token = {} end else table.insert(current_token, char) end end return tokens end local function connect_neurons(lhs, rhs, inhibit) for _, left in ipairs(lhs) do for _, right in ipairs(rhs) do if inhibit then table.insert(left.inhibit, right) else table.insert(left.excite, right) end if not right.alive then right.arity, right.alive = #lhs, true end end end end local function parse(tokens) local is_right, inhibit, chaining = false, false, false local lhs, rhs = {}, {} for _, token in ipairs(tokens) do if token == ":" or token == ";" then is_right = not is_right if chaining then if is_right then connect_neurons(rhs, lhs, inhibit) ; rhs = {} else connect_neurons(lhs, rhs, inhibit) ; lhs = {} end end chaining = true inhibit = token == ";" elseif token == "." then if not chaining then for _, left in ipairs(lhs) do left.alive = true table.insert(live, left) end elseif is_right then connect_neurons(lhs, rhs, inhibit) else connect_neurons(rhs, lhs, inhibit) end lhs, rhs, is_right, inhibit, chaining = {}, {}, false, false, false elseif is_right then table.insert(rhs, neuron(token)) else table.insert(lhs, neuron(token)) end end return net end local function step(count) local excited, dirty = {}, {} -- io.write(("%02d "):format(count)) for _, neuron in ipairs(live) do -- io.write(("%s/%d "):format(neuron.name, neuron.arity)) if neuron.arity == 0 then table.insert(excited, neuron) end for _, inhibit_neuron in ipairs(neuron.inhibit) do inhibit_neuron.sleep = true table.insert(dirty, inhibit_neuron) end for _, excited_neuron in ipairs(neuron.excite) do excited_neuron.saturation = excited_neuron.saturation + 1 table.insert(dirty, excited_neuron) if excited_neuron.saturation == excited_neuron.arity then table.insert(excited, excited_neuron) end end end -- io.write("\n") live = {} for _, excited_neuron in ipairs(excited) do if not excited_neuron.sleep then table.insert(live, excited_neuron) if excited_neuron.hook then excited_neuron.hook(live) end end end for _, dirty_neuron in ipairs(dirty) do dirty_neuron.saturation = 0 dirty_neuron.sleep = false end return #live ~= 0 end parse(tokenize [[ init*; init*: signal*. The signal* will fire once: and activate the cat* program. cat* tiggers a: read* from standard io. no-input* prevents another; no-input* means the program is: done*. if there character-read*; consume the character-read* event: then write* to standard io. if there was a write*: then read*. init* my program. ]]) local char = nil net["read"].hook = function(live) char = io.read(1) if not char then table.insert(live, net["no-input"]) else table.insert(live, net["character-read"]) end end net["write"].hook = function(live) io.write(char) end local count = 0 while step(count) and count < 0x30 do count = count + 1 end