add FSM generator (orange book)

This commit is contained in:
Deet Mit 2022-01-28 14:53:26 +01:00
parent 909dc5715f
commit 7e77480fc7
26 changed files with 3002 additions and 83 deletions

@ -266,6 +266,62 @@ function flip(v)
end
function paste_circuit_from_table(sel,circ,direction)
if sel.pos1 == nil then return end
if sel.pos2 == nil then return end
local nodes = circ.nodes
local metas = circ.metas
local sx = nodes.sx
local sy = nodes.sy
local sz = nodes.sz
local right = rotate_direction_right(direction)
local start_pos = get_corner00(sel,direction)
for xi=1,sx do
for zi=1,sz do
for yi=1,sy do
local shift= add_vectors(
vector.multiply(right,xi-1),
vector.multiply(direction,zi-1),
vector.multiply({x=0,y=1,z=0},yi-1))
local pos = vector.add(start_pos, shift)
if is_in_selection(sel,pos) then
local node = m3_get(nodes,xi,yi,zi) if node ~= nil then
local meta = m3_get(metas,xi,yi,zi)
node = rotate_node(node,{x=0,y=0,z=1},direction)
--minetest.set_node(pos, node)
--mesecons_autotools.set_node(pos,node,"paste_circuit")
minetest.set_node(pos,node)
minetest.get_meta(pos):from_table(meta)
end
end
end
end
end
end
function paste_circuit(sel,file,direction)
if sel.pos1 == nil then return end
if sel.pos2 == nil then return end
local circ = read_table_from_file(file)
paste_circuit_from_table(sel,circ,direction)
end
--[[
function paste_circuit(sel,file,direction)
if sel.pos1 == nil then return end
if sel.pos2 == nil then return end
@ -275,7 +331,7 @@ function paste_circuit(sel,file,direction)
--local rotate_direction = diff_directions(direction)
--local nodes = rotate_m3(info.nodes,direction)
local nodes = info.nodes
local metas = info.metas
local metas = info.metas
local sx = nodes.sx
local sy = nodes.sy
@ -311,7 +367,7 @@ function paste_circuit(sel,file,direction)
end
end
end
]]--
local function make_selection(user,file,direction,pos)

@ -9,6 +9,15 @@ function generate_file_name(user)
return file
end
function generate_file_name_fsm(user)
local days = minetest.get_day_count()
local sec = minetest.get_gametime()
local rand = math.abs(mesecons_autotools.rand:next())
local file = "fsm-"..user.."-"..sec.."-" .. rand
return file
end
function generate_file_name_library(user)
local days = minetest.get_day_count()
local sec = minetest.get_gametime()

@ -79,7 +79,6 @@ function traverse_list(list,action)
if list == nil then return end
for _,v in ipairs(list) do
print("traverser.id =" .. v.id)
if v.type == "circuit" then
action(v)
elseif v.type == "library" then

@ -281,6 +281,25 @@ function iterate_m3(m,action)
end
function m3_insert(mbase, melement, x,y,z)
local sx = melement.sx
local sy = melement.sy
local sz = melement.sz
for xi=1,sx do
for yi=1,sy do
for zi=1,sz do
if m3_get(melement,xi,yi,zi) ~= nil then
m3_set(mbase,x+xi-1,y+yi-1,z+zi-1, m3_get(melement,xi,yi,zi))
end
end
end
end
mbase.sx = math.max(mbase.sx, x+sx-1)
mbase.sy = math.max(mbase.sy, y+sy-1)
mbase.sz = math.max(mbase.sz, z+sz-1)
end

@ -4,78 +4,78 @@ local n10 = "mesecons_extrawires:crossover_10"
-- axuliary function
function switch (name)
if( name == n01 ) then
return n10
else
return n01
end
end
if( name == n01 ) then
return n10
else
return n01
end
end
function rotate_node_to_direction(node,direction)
local add = 0
if direction.x == 1 then add = 3 end
if direction.x == -1 then add = 1 end
if direction.z == 1 then add = 0 end
if direction.z == -1 then add = 2 end
local param2 = node.param2
param2 = (param2+add)% 4
-- special treatement for crossover wire
if( node.name == n01 or node.name == n10 ) then
if( add % 2 == 0 ) then
return { name = node.name, param2=param2}
else
return { name = switch(node.name), param2=param2}
end
end
local add = 0
if direction.x == 1 then add = 3 end
if direction.x == -1 then add = 1 end
if direction.z == 1 then add = 0 end
if direction.z == -1 then add = 2 end
local param2 = node.param2
param2 = (param2+add)% 4
-- special treatement for crossover wire
if( node.name == n01 or node.name == n10 ) then
if( add % 2 == 0 ) then
return { name = node.name, param2=param2}
else
return { name = switch(node.name), param2=param2}
end
end
return { name = node.name , param2 = param2 }
return { name = node.name , param2 = param2 }
end
local function direction_to_number(direction)
if( direction.z == 1 ) then
return 1
elseif( direction.x == 1 ) then
return 2
elseif(direction.z == -1 )then
return 3
else
return 4
end
if( direction.z == 1 ) then
return 1
elseif( direction.x == 1 ) then
return 2
elseif(direction.z == -1 )then
return 3
else
return 4
end
end
function rotate_node(node, saved_direction,direction)
local values =
{
{0,1,2,3},
{3,0,1,2},
{2,3,0,1},
{1,2,3,0}
}
local rotate =
values[direction_to_number(saved_direction)][direction_to_number(direction)]
local new_node = {}
new_node.name = node.name
new_node.param2 = (node.param2+rotate)%4
-- an exception, special treatement of crossover wire
if( node.name == n01 or node.name == n10 ) then
if( rotate % 2 == 0 ) then
new_node = node
else
new_node = {name = switch(node.name), param2=node.param2}
end
end
if node == nil then return nil end
local values =
{
{0,1,2,3},
{3,0,1,2},
{2,3,0,1},
{1,2,3,0}
}
return new_node
local rotate =
values[direction_to_number(saved_direction)][direction_to_number(direction)]
local new_node = {}
new_node.name = node.name
new_node.param2 = (node.param2+rotate)%4
-- an exception, special treatement of crossover wire
if( node.name == n01 or node.name == n10 ) then
if( rotate % 2 == 0 ) then
new_node = node
else
new_node = {name = switch(node.name), param2=node.param2}
end
end
return new_node
end

@ -1,4 +1,5 @@
function is_gate(node)
if node == nil then return false end
local gates = {
"mesecons_gates:and",
"mesecons_gates:or",
@ -39,6 +40,7 @@ function is_wire_node(node)
"mesecons_morewires:xjunction_off", "mesecons_morewires:xjunction_on",
}
if node == nil then return false end
local pos_name = node.name
for i,name in ipairs(list) do
if name == pos_name then
@ -54,6 +56,7 @@ end
function get_stats(m)
local blocks,gates,wires = 0,0,0
iterate_m3(m, function(node)
if node == nil then return end
if is_wire_node(node) then
wires = wires + 1
end

@ -0,0 +1 @@
Formula tool (still in progress, not published in mod)

@ -0,0 +1,33 @@
local function on_use_formula_book(itemstack, player, pointed_thing)
local user = player:get_player_name()
local formspec =
"formspec_version[3]"..
"size[32,25]"..
"label[15,0.7;FSM generator]"..
"field[0.5,2;15,1;title;Name;]"..
"textarea[0.5,4;15,15;text;" .. "Code:" .. ";" .. "" .. "]" ..
"textarea[16.5,2;15,17;d;" .. "Compilation Logs:" .. ";" .. "" .. "]" ..
"button_exit[0.5,19;3,1;save;" .. "Save" .. "]" ..
"button_exit[14.5,20;3,1;compile;" .. "Compile" .. "]" ..
"button_exit[28.5,20;3,1;generate;" .. "Generate" .. "]" ..
""
minetest.show_formspec(user, "mesecons_autotools:formula_show", formspec)
end
--minetest.register_craftitem("mesecons_autotools:circuit_empty", {
minetest.register_tool("mesecons_autotools:formula", {
description = "Logic formula generator",
inventory_image = "formula_book.png",
stack_max = 1,
on_use = on_use_formula_book,
--[[
on_place = none,
on_secondary_use = none,
--]]
})

@ -0,0 +1,163 @@
------------------------------------------------------------------------
-- logic functions
------------------------------------------------------------------------
function show(area)
local vars = area.vars
local s = ""
dump(area)
if #area.parts == 0 then
s = "(none)"
else
for _,part in pairs(area.parts) do
for _,v in ipairs(vars) do
if ( part[v] == true ) then s = s .. " " .. v end
if part[v] == false then s = s .. " ~" .. v end
if part[v] == nil then s = s .. " x" end
end
s = s .. "\n"
end
return s
end
end
function area_add(a1,a2)
local a3 = area_copy(a1)
for _,v in pairs(a2.parts) do
table.insert(a3.parts,v)
end
return a3
end
function drop_negation_list(list)
local l = {}
for _,v in pairs(list) do
table.insert(l, drop_negation(v))
end
return l
end
function swap_truefalse(v)
if v == true then return false end
return true
end
function part_sub_part(a,b)
local vars = {}
for keya,_ in pairs(b) do
if (a[keya] == true and b[keya] == false ) or
( a[keya] == false and b[keya] == true ) then
local ll = {}
table.insert(ll,a)
return ll
end
if a[keya] == nil and b[keya] ~= nil then
vars[keya] = swap_truefalse(b[keya])
end
end
local parts = {}
for k,v in pairs(vars) do
local new = copy_list(a)
new[k] = v
table.insert(parts,new)
end
return parts
end
function insert_parts_in_area(a,list)
local c = {}
c = area_copy(a)
for _,v in pairs(list) do
table.insert(c.parts, v)
end
return c
end
function area_sub_part(a,p)
local c = {}
c.vars = copy_list(a.vars)
c.parts = {}
for _,part in pairs(a.parts) do
local l = part_sub_part(part,p)
c = insert_parts_in_area(c,l)
end
return c
end
--[[
function copy_list(l)
local nl = {}
for k,v in pairs(l) do
nl[k] = v
end
return nl
end
]]--
function copy_list(l)
return DeepCopy(l)
end
function area_copy(a)
return DeepCopy(a)
end
--[[
function area_copy(a)
local c = {}
c.vars = copy_list(a.vars)
c.parts = {}
for _,v in pairs(a.parts) do
table.insert(c.parts, copy_list(v))
end
return c
end
]]--
function area_sub(a,b)
local c = {}
c = area_copy(a)
for _,v in pairs(b.parts) do
c = area_sub_part(c,v)
end
return c
end
--[[
local a1 = {
vars={"a","b","c","d"},
parts = {
{ d = true} ,
--{c = true }
}
}
local a2 = {
vars={"a","b","c","d"},
parts = { { a=false, b =true,c = true} }
}
]]--
--local a = area_sub(a1,a2)
--[[
print(show(a1))
print(show(a2))
print(show(a))
--]]

@ -0,0 +1,470 @@
dofile(minetest.get_modpath("mesecons_autotools").."/fsm/compiler_general.lua");
dofile(minetest.get_modpath("mesecons_autotools").."/fsm/compiler_parsing.lua");
dofile(minetest.get_modpath("mesecons_autotools").."/fsm/compiler_generating.lua");
dofile(minetest.get_modpath("mesecons_autotools").."/fsm/compiler_checking.lua");
------------------------------------------------------------------------
-- compilation functions
------------------------------------------------------------------------
function compile_init(inits)
if empty(inits) then return nil,"no init state" end
if #inits ~= 1 then return nil,"more than one init states" end
return inits[1]
end
function compile_pin_assigns(pins_assigns)
local db = {}
for _,v in ipairs(pins_assigns) do
-- check if already defined
if e(db,v.pin) then
return nil, "pin " .. v.pin .. " defined two times"
end
-- check if self loop
if is_in_list(v.pins,drop_negation(v.pin)) then
return nil, "self reference in definition " .. v.pin
end
if is_in_list(v.pins,add_negation(v.pin)) then
return nil, "self reference in definition " .. v.pin
end
-- expand pins
local pins_expanded = {}
for _,p in ipairs(v.pins) do
if is_pin_name_n(p) then
table.insert(pins_expanded,p)
elseif is_pin_group_name_n(p) then
local gr = drop_negation(p)
if e(db,gr) then
for _,pp in ipairs(db[gr]) do
if( is_negated(p) ) then
table.insert(pins_expanded,swap_negation(pp))
else
table.insert(pins_expanded,pp)
end
end
else
return nil, "pin " .. p .. " no defined in " .. v.pin .. " definition"
end
end
end
db[v.pin] = remove_duplicates( pins_expanded )
end
return db
end
function compile_state_assigns(state_assigns)
local db = {}
for _,v in ipairs(state_assigns) do
-- check if already defined
if e(db,v.state) then
return nil, "state group " .. v.state .. " defined two times"
end
-- check if self loop
if is_in_list(v.states,v.state) then
return nil, "self reference in definition " .. v.state
end
-- expand pins
local states_expanded = {}
for _,p in ipairs(v.states) do
if is_state_name(p) then
table.insert(states_expanded,p)
elseif is_state_group_name(p) then
if e(db,p) then
for _,pp in ipairs(db[p]) do
table.insert(states_expanded,pp)
end
else
return nil, "state " .. p .. " no defined in definition " .. v.state
end
end
end
db[v.state] =remove_duplicates( states_expanded )
end
return db
end
function expand_pin(pin,db)
if is_negated(pin) then
local pure = drop_negation(pin)
if db[pin] == nil then
local l = {}
table.insert(l,pin)
return l
else
local l = {}
for _,v in pairs(db[pure]) do
table.insert(l,swap_negation(v))
end
return l
end
else
if db[pin] == nil then
local l = {}
table.insert(l,pin)
return l
else
return db[pin]
end
end
end
function expand_pins(list, db)
local l = {}
for _,pin in ipairs(list) do
for _,p in ipairs( expand_pin(pin,db) ) do
table.insert(l,p)
end
end
return l
end
function expand_state(s,db)
local l = {}
if db[s] == nil then
table.insert(l,s)
return l
else
return db[s]
end
end
function expand_states(list, db)
local l = {}
for _,state in ipairs(list) do
for _,p in ipairs( expand_state( state, db) ) do
table.insert(l,p)
end
end
return l
end
function all_defined_states(db,list)
for _,v in pairs(list) do
if is_state_group_name(v) then
if not e(db, v) then
return false,v
end
end
end
return true
end
function all_defined_pins(db,list)
for _,v in pairs(list) do
if is_pin_group_name(v) then
if not e(db, v) then
return false,v
end
end
end
return true
end
function compile_state_outputs(state_outputs, db_pins, db_states)
local db = {}
for _,v in ipairs(state_outputs) do
local states = v.states
local pins = v.pins
local df,err
-- expanding pins
local pins_exp = expand_pins(pins,db_pins)
df,err = all_defined_pins(db_pins,pins_exp)
if df == false then
return nil, "pin '" .. err .. "' not defined in line: " .. v.line
end
--expand states
local states_exp = expand_states(states, db_states)
df,err = all_defined_states(db_states,states_exp)
if df == false then
return nil, "state '" .. err .. "' not defined, error in line: " .. v.line
end
-- check if state already defined
for _,w in pairs(states_exp) do
if e(db,w) then
return nil, "state output '" .. w .. "' already defined, duplicate in line: " .. v.line
else
db[w] = pins_exp
end
end
end
return db
end
function compile_state_transitions(state_transitions, db_pins, db_states)
local db = {}
if state_transitions == nil then
return nil,"no transitions"
end
if #state_transitions == 0 then
return nil, "no transitions"
end
for _,v in pairs(state_transitions) do
local states = v.states
local pins = v.pins
local state = v.state
--expand
local df,err
-- expanding pins
local pins_exp = expand_pins(pins,db_pins)
df,err = all_defined_pins(db_pins,pins_exp)
if df == false then
return nil, "pin " .. err .. " not defined, error in line: " .. v.line
end
--expand states
local states_exp = expand_states(states, db_states)
df,err = all_defined_states(db_states,states_exp)
if df == false then
return nil, "state " .. err .. " not defined, error in line: " .. v.line
end
-- building db
for _,w in ipairs(states_exp) do
if db[w] == nil then db[w] = {} end
table.insert(db[w], { pins = pins_exp, state = state } )
end
end
if count_hash(db) == 0 then
return nil,"coudn't find any transitions"
end
return db
end
function compile_state_values(state_values)
local db = {}
if state_values == nil then return db end
for _,v in pairs(state_values) do
local state = v.state
local value = v.value
if db[state] ~= nil then
return nil, "duplicate state value definition, ".. state ..
" := " .. db[state] .. " and " .. state .. " := " .. v.value
else
db[state] = value
end
end
-- checking if all values the same length
local min = 0
-- seting initial value of min (bigger than any of the elment)
for k,v in pairs(db) do
min = min + string.len(v)
end
local max = 0
local maxs,mins
for k,v in pairs(db) do
if string.len(v) < min then
min = string.len(v)
mins = k
end
if string.len(v) > max then
max = string.len(v)
maxs = k
end
end
if (min ~= 0 and min ~= max) then
return nil, "values of states have different length, " .. mins .. " := " .. db[mins] ..
" and " .. maxs .. " := " .. db[maxs]
end
-- checking if values are uniq
local vdb = {}
for k,v in pairs(db) do
if vdb[v] ~= nil then
return nil, "duplicate state value, " .. k .. " := " .. v .. " and " .. vdb[v] .. " := " .. v
else
vdb[v] = k
end
end
return db
end
function compile_in_pins(in_pins)
if #in_pins > 1 then return nil, "multiple input pins definitions" end
local duplicate = get_duplicate(in_pins[1])
if duplicate == nil then
return (in_pins[1] or {})
else
return nil, "input pins duplicated: " .. duplicate
end
end
function compile_out_pins(out_pins)
if #out_pins > 1 then return nil, "multiple output pins definitions" end
local duplicate = get_duplicate(out_pins[1])
if duplicate == nil then
return (out_pins[1] or {} )
else
return nil, "output pins duplicated: " .. duplicate
end
end
function compile_code(code)
local data = parse_code(code)
local db = {}
local init,erroi = compile_init(data.inits)
local bin_pins,errop = compile_pin_assigns(data.pin_assigns)
local bin_states,erros = compile_state_assigns(data.state_assigns)
local bin_outputs,erroo = compile_state_outputs(data.state_outputs, bin_pins,bin_states)
local bin_trans,errot = compile_state_transitions(data.state_transitions, bin_pins,bin_states)
local bin_state_values, errosv = compile_state_values(data.state_values)
local bin_in_pins,erroip = compile_in_pins(data.in_pins)
local bin_out_pins,erroop = compile_out_pins(data.out_pins)
db.init = init
db.init_err = erroi
db.pins = bin_pins
db.pins_err = errop
db.states = bin_states
db.states_err = erros
db.outputs = bin_outputs
db.outputs_err = erroo
db.trans = bin_trans
db.trans_err = errot
db.state_values = bin_state_values
db.state_values_err = errosv
db.in_pins = bin_in_pins
db.in_pins_err = erroip
db.out_pins = bin_out_pins
db.out_pins_err = erroop
db.unknowns = data.unknowns
if not is_compilation_success(db) then
return nil,generate_compilation_error_message(db)
end
-- further processing
-- warning: orders of executing functions is important, they depend on the db data
-- I know, it's a huge weld :)
local errs = {}
local err
local msg = ""
function insert_error(err)
if err ~= nil then
if type(err) == "table" then
for _,i in pairs(err) do
table.insert(errs,i)
end
else
table.insert(errs,err)
end
end
end
function err_string()
-- show errors
if #errs > 0 then
msg = ""
for _,v in pairs(errs) do
msg = msg .. "error: " .. v .. "\n"
end
msg = "Errors(".. #errs .. "):\n" .. msg
return msg
end
return ""
end
db.reachables = get_all_reachable_states(db)
_,err = get_not_used_states(db)
insert_error(err); if #errs ~= 0 then return nil, err_string() end
db.state_bitsize,err = compute_bitsize_of_state(db)
insert_error(err); if #errs ~= 0 then return nil, err_string() end
db.state_values = only_reachable_state_values(db)
_,err = check_dead_states(db)
insert_error(err); if #errs ~= 0 then return nil, err_string() end
db.state_values,err = fill_states_values(db)
insert_error(err); if #errs ~= 0 then return nil, err_string() end
db.inputs_used,err = get_inputs(db)
insert_error(err); if #errs ~= 0 then return nil, err_string() end
db.outputs_used,err = get_outputs(db)
insert_error(err); if #errs ~= 0 then return nil, err_string() end
--- further checking: applying priority and others
add_default_loops(db)
apply_priority(db)
clear_empty_transitions(db)
-- the part above must be rewritten, too many welds here
-- additionally, algorithm does not remove all unused states,
-- therefore sometimes it generates dead transistions
-- the FSM is correct, but when generated has unused subcircuits
-- TODO: fix this
----------------------------------------
return db,"\nCompilation successful\n\n" ..
msg_state_values(db) .. "\n" ..
"\n" .. msg_inout_values(db)
end

@ -0,0 +1,383 @@
function msg_state_values(db)
local l = db.state_values
if l == nil then return "" end
local msg = ""
for k,v in pairs(l) do
msg = msg .. k .. ":=" .. v .. "\n"
end
return msg
end
function msg_inout_values(db)
local mi = "Inputs: " .. table.concat(db.inputs_used,", ")
local mo = "Outputs: " .. table.concat(db.outputs_used,", ")
return mi .. "\n" .. mo
end
function is_compilation_success(db)
local list = { "init_err" , "pins_err", "states_err" ,"trans_err",
"state_values_err", "in_pins_err" , "out_pins_err", "outputs_err" }
for _,v in pairs(list) do
if db[v] ~= nil then return false end
end
if db.unknowns ~= nil then
if #db.unknowns ~= 0 then return false end
end
return true
end
function generate_compilation_error_message(db)
local list = { "init_err" , "pins_err", "states_err" ,"trans_err",
"state_values_err", "in_pins_err" , "out_pins_err" , "outputs_err" }
local counter = 0
local msg = ""
for _,v in pairs(list) do
if db[v] ~= nil then
counter = counter + 1
msg = msg .. "error: " .. db[v] .. "\n"
end
end
-- adding unknows lines
if db.unknowns ~= nil then
if #db.unknowns ~= 0 then
for _,v in pairs(db.unknowns) do
msg = msg .. "error: unknown line '" .. v.line .. "'\n"
counter = counter + 1
end
end
end
return "Errors (" .. counter .. ")\n" .. msg
end
------------------------------------------------------------------------
-- checking fsm functions
------------------------------------------------------------------------
-- TODO: redesign queue functions
function is_empty_queue(q)
local count = 0
for _,v in pairs(q) do
count = count +1
end
if count == 0 then return true end
return false
end
-- TODO: redesign queue functions
function push_on_queue(q,e)
local qq = DeepCopy(q)
table.insert(qq,e)
return qq
end
-- TODO: redesign queue functions
function pop_from_queue(q)
local e = DeepCopy(q[1])
q[1] = nil
local newq = {}
for _,v in pairs(q) do
table.insert(newq,v)
end
return e,newq
end
function get_all_reachable_states(db)
local init = db.init
local queue = {}
local color = {}
local next_state = {}
local curr
queue = push_on_queue(queue,init)
while not is_empty_queue(queue) do
curr,queue = pop_from_queue(queue)
if color[curr] ~= true then
color[curr] = true
if( db.trans[curr] ~= nil ) then
for _,ifthen in pairs(db.trans[curr]) do
next_state = ifthen.state
queue = push_on_queue(queue,next_state)
end
end
end
end
return color
end
function count_hash(h)
local c = 0
for _,_ in pairs(h) do
c=c+1
end
return c
end
-- math.log is computins some trash!!!!!!
-- is that a bug or something
-- making my own binary log function
function lg2(n)
local c = 0
local mul = 1
while (mul < n ) do
mul = 2* mul
c=c+1
end
return c
end
function get_first_hash_element(h)
if h == nil then return nil end
local e = nil
for k,v in pairs(h) do
return k,v
end
return nil,nil
end
function compute_bitsize_of_state(db)
local reachable = get_all_reachable_states(db)
local count = count_hash(reachable)
local bits_reachable = lg2(count)
local state_values = db.state_values
local count_values = count_hash(state_values)
if( count_values == 0 ) then
if bits_reachable == 0 then
return nil,"no reachable states or only one state"
end
return bits_reachable
end
-- check if all values have the same size
local fk,fv = get_first_hash_element(state_values)
local flen = string.len(fv)
for k,v in pairs(state_values) do
if string.len(v) ~= flen then
return nil, "states valuse do not have the same length, eg " ..
fk .. " := " .. fv .. " and " .. k .. " := " .. v
end
end
-- checking if enoguth bits for reachable states
if ( bits_reachable > flen ) then
return nil, "lenght of state value is too small, need " ..
bits_reachable .. " bits to code " ..
count .. " states, but got only " .. flen ..
"eg. " .. fk .. " := " .. fv
end
if flen == 0 then
return nil,"no reachable states or only one state"
end
return flen
end
function list_to_hash(list)
local hash = {}
for _,v in pairs(list) do
hash[v] = true
end
return hash
end
function hash_in_hash(h1,h2)
for k,_ in pairs(h1) do
if h2[k] == nil then return false end
end
return true
end
function same_hash(h1,h2)
if hash_in_hash(h1,h2) and hash_in_hash(h2,h1) then return true else return false end
end
--[[ not used
function hash_cut_hash(h1,h2)
local h3 = {}
for k,_ in pairs(h1) do
if h2[k] ~= nil then
h3[k] = true
end
end
return h3
end
]]--
function hash_minus_hash(h1,h2)
local out = {}
for k,_ in pairs(h1) do
if h2[k] == nil then
out[k] = true
end
end
return out
end
function hash_to_list(h)
local list = {}
for k,v in pairs(h) do
table.insert(list,k)
end
return list
end
function check_dead_states(db)
-- states reachable from init
local hreachable = db.reachables
local trans = {}
-- all states in trans
for state,v in pairs(db.trans) do
table.insert(trans,state)
end
local htrans = list_to_hash(trans)
if same_hash(hreachable,htrans) then
return nil,nil
else
local hdiff = hash_minus_hash(htrans,hreachable)
if count_hash(hdiff) ~= 0 then
local ldiff = hash_to_list(hdiff)
return nil, "following states '" .. table.concat(ldiff,", ") ..
"' are used in transitions but are not reachable from init state"
end
return nil,nil
end
end
function cmp_list_eq(a,b)
local ha = list_to_hash(a)
local hb = list_to_hash(b)
return same_hash(ha,hb)
end
function list_minus_list(l1,l2)
local h1 = list_to_hash(l1)
local h2 = list_to_hash(l2)
local minus = hash_minus_hash(h1,h2)
return hash_to_list(minus)
end
function get_all_inputs_from_reachable_transitions(db)
local list = {}
for state,v in pairs(db.trans) do
if db.reachables[state] then
for _,w in pairs(v) do
append(list,drop_negation_list(w.pins))
end
end
end
local sorted_by_name = DeepCopy(remove_duplicates(list))
table.sort(sorted_by_name)
return sorted_by_name
end
function get_inpus_from_transistions_and_check_with_declared(db)
local ins_from_trans = get_all_inputs_from_reachable_transitions(db)
local ins_from_def = db.in_pins
if cmp_list_eq(ins_from_def,ins_from_trans) then
return ins_from_def
else
local more_in_def = list_minus_list(ins_from_def,ins_from_trans)
local more_in_trans = list_minus_list(ins_from_trans, ins_from_def)
if #more_in_def ~= 0 then
return nil, "pins '" .. table.concat(more_in_def, ", ") ..
"' declared in 'in:' but never used in transitions";
end
if #more_in_trans ~= 0 then
return nil, "pins '" .. table.concat(more_in_trans, ", " ) ..
"' used in transitions, but not declared in 'in:'"
end
return nil, {"pins declared and pins used in transitions are not the same"}
end
end
function get_inputs(db)
-- checking if input pins are not declared
if db.in_pins == nil or #db.in_pins == 0 then
return get_all_inputs_from_reachable_transitions(db)
end
-- checing if the same as from transistion
return get_inpus_from_transistions_and_check_with_declared(db)
end
function get_all_output_from_reachable_transitions(db)
local list = {}
for state,v in pairs(db.trans) do
if db.reachables[state] then
for _,w in pairs(v) do
if db.outputs[w.state] ~= nil then
append(list,db.outputs[w.state])
end
end
end
end
local sorted_by_name = DeepCopy(remove_duplicates(list))
table.sort(sorted_by_name)
return sorted_by_name
end
function get_outputs_from_transistions_and_check_with_declared(db)
local outs_from_trans = get_all_output_from_reachable_transitions(db)
local outs_from_def = db.out_pins
if cmp_list_eq(outs_from_trans,outs_from_def) then
return outs_from_def
else
local more_in_def = list_minus_list(outs_from_def,outs_from_trans)
local more_in_trans = list_minus_list(outs_from_trans, outs_from_def)
if #more_in_def ~= 0 then
return nil, "pins '" .. table.concat(more_in_def, ", ") ..
"' declared in 'out:' but never used in output states";
end
if #more_in_trans ~= 0 then
return nil, "pins '" .. table.concat(more_in_trans, ", " ) ..
"' used in output state definitons, but not declared in 'out:'"
end
return nil, {"pins declared and pins used in transitions are not the same"}
end
end
function get_outputs(db)
-- check if output pins are declared
if db.out_pins == nil or #db.out_pins == 0 then
return get_all_output_from_reachable_transitions(db)
end
return get_outputs_from_transistions_and_check_with_declared(db)
end

@ -0,0 +1,191 @@
------------------------------------------------------------------------
-- general functions
------------------------------------------------------------------------
function trim(s)
return s:match( "^%s*(.-)%s*$" )
end
function get_list_from_string(str)
local l = {}
for i in string.gmatch(str,"%S+") do
table.insert(l,trim(i))
end
return l
end
function get_lines_from_string(str)
local l = {}
for i in string.gmatch(str,"[^\n]+") do
table.insert(l,trim(i))
end
return l
end
function empty(list)
if #list == 0 then return true end
return false
end
-- exists
function e(db,key)
if db == nil then return false end
if db[key] ~= nil then return true end
return false
end
function is_in_list(list,elem)
for _,v in ipairs(list) do
if v == elem then return true end
end
return false
end
function remove_duplicates(list)
local l = {}
local db = {}
for key,value in pairs(list) do
db[value] = true
end
for key,_ in pairs(db) do
table.insert(l,key)
end
return l
end
------------------------------------------------------------------------
-- help functions
------------------------------------------------------------------------
local name_reg = "[a-zA-Z][a-zA-Z0-9_]*"
function is_name(str)
return string.match(str,"^" .. name_reg .. "$")
end
function is_negated(n)
if string.match(n,"^~.*$") == nil then
return false
else
return true
end
end
function drop_negation(n)
local name = string.match(n,"^~?(.*)$")
return name
end
function add_negation(n)
return "~" .. drop_negation(n)
end
function swap_negation(n)
if is_negated(n) then
return drop_negation(n)
else
return add_negation(n)
end
end
function is_pin_name(n)
if is_name(n) == nil then return false end
return true
end
function is_pin_group_name(n)
local name = string.match(n,"^#(.*)")
if name == nil then return false end
return is_pin_name(name)
end
function is_pin_name_n(n)
if is_pin_name(n) then return true end
local name = string.match(n,"^~(.*)$")
if name == nil then return false end
return is_pin_name(name)
end
function is_pin_group_name_n(n)
if is_pin_group_name(n) then return true end
local name = string.match(n,"^~(.*)$")
if name == nil then return false end
return is_pin_group_name(name)
end
function is_pin_and_group_list(list)
for _,v in pairs(list) do
if not is_pin_name(v) and not is_pin_group_name(v) then return false end
end
return true
end
function is_pin_name_list(list)
for _,v in pairs(list) do
if not is_pin_name(v) then return false end
end
return true
end
function is_pin_and_group_list_n(list)
for _,v in pairs(list) do
if not is_pin_name_n(v) and
not is_pin_group_name_n(v) then
return false
end
end
return true
end
function is_state_name(n)
if is_name(n) == nil then return false end
return true
end
function is_state_group_name(n)
local name = string.match(n,"^@(.*)")
if name == nil then return false end
return is_state_name(name)
end
function is_state_and_group_list(list)
for _,v in pairs(list) do
if (not is_state_name(v)) and
(not is_state_group_name(v)) then
return false
end
end
return true
end
--[[ not used, for delete
function is_uniq_list(list)
local db = {}
for _,v in pairs(list) do
if db[v] == nil then
db[v] = true
else
return false
end
end
end
]]--
-- find first duplicate
function get_duplicate(list)
if list == nil then return nil end
local db = {}
for _,v in pairs(list) do
if db[v] == nil then
db[v] = true
else
return v
end
end
return nil
end

@ -0,0 +1,225 @@
------------------------------------------------------------------------
-- generating fsm functions
------------------------------------------------------------------------
function get_list(list)
if list == nil then
return {}
else
return list
end
end
--[[
function fsm_generate_trans(db)
-- from ins -> to outs
local results = {}
local states = get_all_reachable_states(db)
for state,_ in pairs(states) do
if( db.trans[state] == nilv) then
table.insert(results, { from = state, to = state, ins = {} , outs = {} })
else
for _,ifthens in pairs(db.trans[state]) do
local pins_in = ifthens.pins
local next_state = ifthens.state
local pins_out = db.outputs[next_state]
if pins_out == nil then pins_out = {} end
table.insert(results, { from = state, to = next_state, ins = pins_in, outs = pins_out } )
end
end
end
return results
end
]]--
function value_inc(v)
local len = #v
local carry = true
local new = ""
for i=len,1,-1 do
local ch = string.sub(v,i,i)
if carry == false then
new = ch .. new
else
if ch == "0" then
new = "1" .. new
carry = false
else
new = "0" .. new
carry = true
end
end
end
return new
end
function next_free_value(len,db)
local nv = string.rep("0",len)
vals = {}
-- get only vlaues
for k,v in pairs(db) do
vals[v] = true
end
for i=1,math.pow(2,len) do
if vals[nv] == nil then
return nv
end
nv = value_inc(nv)
end
-- should never get here
return nil
end
function get_not_used_states(db)
local reachables = db.reachables
local state_values = db.state_values
local not_used_states = {}
-- checking if some states are not used
for k,v in pairs(state_values) do
if reachables[k] == nil then
table.insert(not_used_states,k)
end
end
if #not_used_states == 0 then
return {}
end
local list = {}
for _,v in pairs(not_used_states) do
table.insert(list, "state '" .. v .. "' is not used, but declared " .. v ..
" := " .. state_values[v])
end
return nil,list
end
function fill_states_values(db)
local states_values = db.state_values
local states = db.reachables
local filled = {}
-- filling existing values
for k,_ in pairs(states) do
filled[k] = states_values[k]
end
local len = db.state_bitsize
local zerostate = string.rep("0",len)
-- checkint if init already has value, and if it is 0000
if filled[init] ~= nil and filled[init] ~= zerostate then
return nil, "init state " .. filled[init] .. " has value " ..
filled[init] .. ", only value 0 is allovwd for init state"
end
-- checking if state 0000 is used by other state
for k,v in pairs(filled) do
if v == zerostate then
if k ~= db.init then
return nil, "value " .. zerostate ..
" is reserved for init state, but already used by " ..
k .. " := " .. v
end
end
end
-- adding default init state as 0000
local init = db.init
if filled[init] ~= nil then
if filled[init] ~= zerostate then
return nil,"init state must be " .. zerostate .. ", but is " ..
init .. " := " .. filled[init]
end
end
if filled[init] == nil then
filled[init] = zerostate
end
-- adding rest
for k,v in pairs(states) do
if filled[k] == nil then
filled[k] = next_free_value(len,filled)
end
end
return filled
end
function only_reachable_state_values(db)
local reach = db.reachables
local values = db.state_values
local new = {}
for k,_ in pairs(values) do
if reach[k] ~= nil then
new[k] = values[k]
end
end
return new
end
function fsm_get_all_used_inputs(db)
local pins = {}
for _,v in pairs(db.trans) do
local ins = v.ins
for _,pin in pairs(ins) do
pins[drop_negation(pin)] = true
end
end
return pins
end
-- appends list 2 to lits 1
-- probably there is a function for that
-- couldn't bother to find it
function append(list1,list2)
for _,v in pairs(list2) do
table.insert(list1,v)
end
return list1
end
function fsm_get_all_used_outputs(reachable_trans,db)
local list = {}
-- get all pins
for _,v in pairs(reachable_trans) do
local ps = db.outputs[v.to]
apend(list,ps)
end
-- remove negations
local list2 = {}
for _,v in pairs(list) do
table.insert(list2, drop_negation(v))
end
return remove_duplicates(list2)
end
function check_inputs(db)
end
------------------------------------------------------------------------
-- generating error messages
------------------------------------------------------------------------
function error_unknows(unknows)
local msg = ""
for _,v in pairs(unknows) do
msg = msg .. "line: .. " .. v.nr .. " " .. v.line .. "\n"
end
return msg
end

@ -0,0 +1,216 @@
------------------------------------------------------------------------
-- parsing functions
------------------------------------------------------------------------
function is_pin_assign(line)
if line == nil then return nil end
local pin,pins = string.match(line,"^%s*([#].*)%s*=(.*)$")
if pin == nil or pins == nil then return nil end
pin = trim(pin)
if( is_pin_group_name(trim(pin)) ~= true ) then return nil end
local list = get_list_from_string(pins)
if( not is_pin_and_group_list_n(list) ) then return nil end
local data = {}
data.pin = pin
data.pins = list
return data
end
function is_state_assign(line)
if line == nil then return nil end
local state,states = string.match(line, "^%s*(@.*)%s*=(.*)$")
if state == nil or states == nil then return nil end
state = trim(state)
if( not is_state_group_name(state) ) then return nil end
local list = get_list_from_string(states)
if( not is_state_and_group_list(list) ) then return nil end
local data = {}
data.state = state
data.states = list
data.line = line
return data
end
function is_bin_value(value)
if value == nil then return false end
for i=1,#value do
local ch = string.sub(value,i,i)
if (ch ~= "0" ) and (ch ~= "1" ) then return false end
end
return true
end
function is_state_value_assign(line)
if line == nil then return nil end
local state,value = string.match(line, "^%s*(.*)%s*:=(.*)$")
if state == nil then return nil end
if value == nil then return nil end
state = trim(state)
value = trim(value)
if not is_state_name(state) then return nil end
if not is_bin_value(value) then return nil end
local data = {}
data.state = state
data.value = value
return data
end
function is_in_assign(line)
if line == nil then return nil end
local ins = string.match(line, "^%s*in:(.*)$")
if ins == nil then return nil end
local list = get_list_from_string(ins)
if( not is_pin_name_list(list) ) then return nil end
local data = {}
data = list
return data
end
function is_out_assign(line)
if line == nil then return nil end
local ins = string.match(line, "^%s*out:(.*)$")
if ins == nil then return nil end
local list = get_list_from_string(ins)
if( not is_pin_name_list(list) ) then return nil end
local data = {}
data = list
return data
end
function is_init_assign(line)
if line == nil then return nil end
local init = string.match(line,"^%s*[[][[]%s*(.*)%s*[]][]]%s*$")
if init == nil then return nil end
if( not is_state_name(init)) then return nil end
return trim(init)
end
function is_state_output_assign(line)
if line == nil then return nil end
local states,pins = string.match(line, "^%s*[[]%s*(.*)%s*[]](.*)$")
if( states == nil or pins == nil ) then return nil end
local state_list = get_list_from_string(states)
if empty(state_list) then return nil end
if not is_state_and_group_list(state_list) then return nil end
local pin_list = get_list_from_string(pins)
if empty(pin_list) then return nil end
--if not is_pin_and_group_list_n(pin_list) then return nil end
if not is_pin_and_group_list(pin_list) then return nil end
--if not is_pin_name_list(pin_list) then return nil end
local data = {}
data.states = state_list
data.pins = pin_list
data.line = line
return data
end
function is_state_transition_assign(line)
if line == nil then return nil end
local states,pins,state = string.match(line,"^%s*[[](.*)[]](.*)->%s*[[](.*)[]]%s*$")
if states == nil or state == nil or pins == nil then return nil end
local state_list = get_list_from_string(states)
if empty(state_list) then return nil end
if not is_state_and_group_list(state_list) then return nil end
local pin_list = get_list_from_string(pins)
if not is_pin_and_group_list_n(pin_list) then return nil end
if not is_state_name(trim(state)) then return nil end
local data = {}
data.states = state_list
data.pins = pin_list
data.state = trim(state)
data.line = line
return data
end
function is_comment(line)
if line == nil then return nil end
local comment = string.match(line,"^%s*;.*$")
return comment
end
function parse_code(text)
local inits = {}
local pin_assigns = {}
local state_assigns = {}
local state_outputs = {}
local state_transitions = {}
local state_values = {}
local in_pins = {}
local out_pins = {}
local unknowns = {}
local lines = get_lines_from_string(text)
for i,line in ipairs(lines) do
local init = is_init_assign(line)
local pin_assgn = is_pin_assign(line)
local state_assign = is_state_assign(line)
local state_output = is_state_output_assign(line)
local state_transition = is_state_transition_assign(line)
local state_value = is_state_value_assign(line)
local in_pin = is_in_assign(line)
local out_pin = is_out_assign(line)
local comment = is_comment(line)
if( init ~= nil ) then
table.insert(inits,init)
elseif pin_assgn ~= nil then
table.insert(pin_assigns,pin_assgn)
elseif state_assign ~= nil then
table.insert(state_assigns,state_assign)
elseif state_output ~=nil then
table.insert(state_outputs,state_output)
elseif state_transition ~= nil then
table.insert(state_transitions, state_transition)
elseif state_value ~= nil then
table.insert(state_values,state_value)
elseif in_pin ~= nil then
table.insert(in_pins,in_pin)
elseif out_pin ~= nil then
table.insert(out_pins,out_pin)
elseif comment ~= nil then
-- drop comment
else
table.insert(unknowns,{nr=i, line = line})
end
end
local data = {}
data.inits = inits
data.pin_assigns = pin_assigns
data.state_assigns = state_assigns
data.state_outputs = state_outputs
data.state_transitions = state_transitions
data.state_values = state_values
data.in_pins = in_pins
data.out_pins = out_pins
data.unknowns = unknowns
return data
end

@ -0,0 +1,275 @@
dofile(minetest.get_modpath("mesecons_autotools").."/fsm/lib.lua");
dofile(minetest.get_modpath("mesecons_autotools").."/fsm/alg.lua");
dofile(minetest.get_modpath("mesecons_autotools").."/fsm/compiler.lua");
dofile(minetest.get_modpath("mesecons_autotools").."/fsm/generate.lua");
dofile(minetest.get_modpath("mesecons_autotools").."/fsm/priority.lua");
local esc = minetest.formspec_escape
function get_stats_string(data)
if data == nil then return "<no circuit>" end
if data.circ == nil then return "<no circuit>" end
if data.circ.nodes == nil then return "<no circuit>" end
local nodes = data.circ.nodes
local stats = "<no circuit>"
local block, gate, wire
local sx,xy,sz
block, gate, wire = get_stats(nodes)
sx = nodes.sx
sy = nodes.sy
sz = nodes.sz
stats = "size : " .. sx .. "x"..sy .. "x"..sz .. "(=".. sx*sy*sz .. ")\n" ..
"wires : " .. wire .. "\n" ..
"gates : " .. gate .. "\n" ..
"others : ".. block - wire - gate .. "\n" ..
"blocks: " .. block
return stats
end
local function get_formspec(data)
local code = minetest.formspec_escape(data.code or "")
local name = minetest.formspec_escape(data.name or "")
local log = minetest.formspec_escape(data.log or "")
local stats_string = get_stats_string(data)
local create_button = ""
if data.circ ~= nil then
if data.circ.nodes ~= nil then
create_button = "image[27.5,23;1,1;circuit_full.png]" ..
"button[28.5,23;3,1;create;" .. "Create" .. "]"
end
end
local formspec =
"formspec_version[3]"..
"size[32,25]"..
"style_type[textarea;font=mono]" ..
"label[15,0.7;FSM generator]"..
--"label[20,0.7;" .. file .. "]" ..
"field[0.5,2;15,1;name;Name;" .. name .. "]"..
"textarea[0.5,4;15,15;code;" .. "Code:;" .. code .. "]" ..
"textarea[16.5,2;15,17;;" .. "Compilation Logs:" .. ";" .. log .. "]" ..
"button[0.5,19;3,1;save;" .. "Save" .. "]" ..
"button[14.5,20;3,1;compile;" .. "Compile" .. "]" ..
"textarea[23.5,21;8,5;;Circuit Parameters;" .. esc(stats_string) .. "]" ..
"label[0.5,21;Generation Options:]" ..
"label[1,22;Circuit Type:]" ..
"dropdown[4,21.5;5,1;type;flat_raw;1]" ..
create_button ..
""
return formspec
end
function on_place_fsm_book(itemstack, player, pointed_thing)
local user = player:get_player_name()
local rad = player:get_look_horizontal()
local direction = radians_to_direction_looking_forward(rad)
local fields = itemstack:get_meta():to_table().fields
local file = fields.file
if not mesecons_autotools.is_full_selection(user) then return nil end
local pos1 = mesecons_autotools.get_pos(user,1)
local pos2 = mesecons_autotools.get_pos(user,2)
local sel = {pos1=pos1,pos2=pos2}
local data = read_table_from_file(file)
if data.circ == nil then return end
local circ = data.circ
paste_circuit_from_table(sel,circ,direction)
end
local function show_dialog_fsm(player,itemstack)
local user = player:get_player_name()
local fields = itemstack:get_meta():to_table().fields
local file = fields.file
local stack = player:get_wielded_item()
local db = {}
local data = {}
if file == nil then
file = generate_file_name_fsm(user)
fields.file = file
stack:get_meta():from_table({ fields = fields})
player:set_wielded_item(stack)
data.code = ""
data.name = ""
data.log = ""
save_table_to_file(file,data)
else
data = read_table_from_file(file)
if data == nil then data = {} end
end
local formspec = get_formspec(data)
minetest.show_formspec(user, "mesecons_autotools:fsm_show", formspec)
end
function fsm_make_selection(user,file,direction,pos)
if file == nil then return end
local data = read_table_from_file(file)
if data.circ == nil then return end
local nodes = data.circ.nodes
if nodes == nil then return end
local sx = nodes.sx
local sy = nodes.sy
local sz = nodes.sz
local pos2 = make_pos2(pos,direction,sx,sy,sz)
-- Updatecd
mesecons_autotools.set_pos(user,1,pos)
mesecons_autotools.set_pos(user,2,pos2)
mesecons_autotools.render(user)
mesecons_autotools.zero_stack_counter(user)
mesecons_autotools.zero_stack_direction(user)
end
local function on_use_fsm_book(itemstack, player, pointed_thing)
local user = player:get_player_name()
local data = itemstack:get_meta():to_table().fields
local rad = player:get_look_horizontal()
local direction = radians_to_direction_looking_forward(rad)
local file = data.file
-- local stack = player:get_wielded_item()
if( pointed_thing.type == "node" ) then
fsm_make_selection(user,file,direction,pointed_thing.above)
else
show_dialog_fsm(player,itemstack)
end
return nil
end
minetest.register_on_player_receive_fields(function(player, formname, fields)
if formname ~= "mesecons_autotools:fsm_show" then return end
local user = player:get_player_name()
local stack = player:get_wielded_item()
local ffields = stack:get_meta():to_table().fields
local data = {}
local file = ffields.file
if file == nil then
file = generate_file_name_fsm(user)
ffields.file = file
data.name =""
data.code =""
data.log =""
save_table_to_file(file,data) -- just to generate file
else
data = read_table_from_file(file)
if data == nil then data = {} end
end
data.name = fields.name or ""
data.code = fields.code or ""
if (fields.save) or fields.key_enter_field == "name" then
ffields.description = data.name
stack:get_meta():from_table({ fields = ffields})
player:set_wielded_item(stack)
save_table_to_file(file,data)
elseif fields.compile then
ffields.description = data.name
stack:get_meta():from_table({ fields = ffields})
player:set_wielded_item(stack)
--compilation area
local bin,err = compile_code(data.code)
if bin == nil then
data.log = err
data.circ = nil
else
local circ = generate_circuit(bin,{})
data.circ = circ
data.log = err
end
--end of copmilation area
save_table_to_file(file,data)
local formspec = get_formspec(data)
minetest.show_formspec(user, "mesecons_autotools:fsm_show", formspec)
elseif fields.create then
if data.circ == nil then return end
if data.circ.nodes == nil then return end
-- create file
local blue = {}
blue.nodes = data.circ.nodes
blue.metas = data.circ.metas
blue.title = data.name
blue.text = ""
blue.direction = {x=0,y=0,z=1}
-- save file
local blue_file = generate_file_name(user)
save_table_to_file(blue_file,blue)
-- create item in inventory
local new_stack = ItemStack("mesecons_autotools:circuit_full")
local b = {}
b.file = blue_file
b.description = blue.title
new_stack:get_meta():from_table({ fields = b})
local inv = player:get_inventory()
if inv:room_for_item("main", new_stack) then
inv:add_item("main", new_stack)
else
minetest.add_item(player:get_pos(), new_stack)
end
end
end)
minetest.register_tool("mesecons_autotools:fsm", {
description = "FSM generator",
inventory_image = "fsm_book.png",
stack_max = 1,
on_use = on_use_fsm_book,
on_place = on_place_fsm_book,
on_secondary_use = none,
})

@ -0,0 +1,742 @@
------------------------------------------------------------------------
-- generating wires
------------------------------------------------------------------------
function g_wire_line(rotation,state)
local r = 0
if rotation == "h" then
r = 0
else -- == "v"
r = 1
end
return {
node = {name="mesecons_insulated:insulated_".. state , param2 = r},
meta = {}
}
end
function g_wire_bend(rotation,state)
local rot = 0
if rotation == "lu" then
rot = 1
elseif rotation == "ru" then
rot = 2
elseif rotation == "rd" then
rot = 3
elseif rotation == "ld" then
rot = 0
else
rot = 0
end
return {
node = {name="mesecons_extrawires:corner_" .. state, param2=rot},
meta = {}
}
end
function g_wire_cross(state)
local s = "off"
if state == "off" then
s = "off"
elseif state == "on" then
s = "on"
elseif state == "h" then
s = "01"
elseif state == "v" then
s = "10"
else
s = "off"
end
return {
node = {name="mesecons_extrawires:crossover_" .. s, param2=0},
meta = {}
}
end
function g_wire_t(rotation,state)
local p = 0
if rotation == "u" then
p = 2
elseif rotation == "d" then
p = 0
elseif rotation == "l" then
p = 1
else -- "r"
p = 3
end
return {
node = {name="mesecons_extrawires:tjunction_" .. state, param2=p},
meta = {}
}
end
function g_wire_xjunction(state)
return {
node = {name="mesecons_morewires:xjunction_" .. state, param2=0},
meta = {}
}
end
------------------------------------------------------------------------
-- generating gates
------------------------------------------------------------------------
function g_gate_not(state,rot)
local p
if rot == "up" then
p = 3
elseif rot == "left" then
p = 2
elseif rot == "right" then
p = 0
else --down
p = 1
end
return {
node = {name="mesecons_gates:not_" .. state,param2=p},
meta = { ["inventory"] = { } ,["fields"] = { } ,}
}
end
function g_gate_diode(state,rot)
local p
if rot == "up" then
p = 3
elseif rot == "left" then
p = 2
elseif rot == "right" then
p = 0
else --down
p = 1
end
return {
node = {name="mesecons_gates:diode_" .. state, param2=p},
meta = { ["inventory"] = { } ,["fields"] = { } ,}
}
end
function g_ff(state,rot,data) -- din = data in
local p
if rot == "up" then
p = 3
elseif rot == "left" then
p = 2
elseif rot == "right" then
p = 0
else --down
p = 1
end
local din
if data == "off" then din = 0 else din =1 end
return {
node={ name = "mesecons_regs:flipflop_off" ,param2 = p} ,
meta={ ["inventory"] = { } ,["fields"] = { ["enable"] = 0,["data"] = din,} }
}
end
------------------------------------------------------------------------
-- help functions
------------------------------------------------------------------------
function set(circ,x,z,elem)
local node = elem.node
local meta = elem.meta
m3_set(circ.nodes,x,1,z,node)
m3_set(circ.metas,x,1,z,meta)
end
function is_bl_empty(c)
if c == nil then return true end
if c.nodes == nil then return true end
if c.nodes.sx == 0 or c.nodes.sy == 0 or c.nodes.sz==0 then return true end
return false
end
-- block insert
function blinsert(chost,celem,x,y,z)
if is_bl_empty(celem) then return end
m3_insert(chost.nodes,celem.nodes,x,y,z)
m3_insert(chost.metas,celem.metas,x,y,z)
end
function is_all_zeros(v)
for i=1,string.len(v) do
if string.sub(v,i,i) ~= "0" then
return false
end
end
return true
end
function new_circ()
return { nodes = { sx=0,sy=0,sz=0} , metas = {sx=0,sy=0,sz=0} }
end
function gnode_to_circ(n)
local c = new_circ()
set(c,1,1,n)
c.nodes.sx = 1
c.nodes.sy = 1
c.nodes.sz = 1
c.metas.sx = 1
c.metas.sy = 1
c.metas.sz = 1
return c
end
function inverse_state(v)
if v == "on" then return "off" else return "on" end
end
function switch_01_to_onoff(c)
if c == "0" then
return "off"
else
return "on"
end
end
--[[ not used
function has_negation(list)
for _,v in pairs(list) do
if is_negated(v) then
return true
end
end
return false
end
]]--
function has_positive_pin(list)
for _,v in pairs(list) do
if not is_negated(v) then
return true
end
end
return false
end
function or_state(s1,s2)
if s1 == "off" and s2 == "off" then return "off" else return "on" end
end
function or_value(v1,v2)
local s = ""
for i=1,string.len(v1) do
if string.sub(v1,i,i) == "0" and string.sub(v2,i,i) == "0" then
s = s .. "0"
else
s = s .. "1"
end
end
return s
end
------------------------------------------------------------------------
-- generating bundles of wires
------------------------------------------------------------------------
function bl_bend_1wire_from_down_to_right(x0,y0, x1,y1)
-- (x0,y0) to (x1,y1) first going up, then going right
local c = new_circ()
-- vertical
for i=y0,y1-1 do
blinsert(c,gnode_to_circ( g_wire_line("v","off") ), x0,1,i)
end
-- curve
blinsert(c,gnode_to_circ(g_wire_bend("rd","off")), x0,1,y1)
-- horizontal
for i=x0+1,x1 do
blinsert(c,gnode_to_circ(g_wire_line("h","off")),i,1,y1)
end
return c
end
function bl_bend_bundle_from_down_to_right_step2(size)
local c = new_circ()
for i=1,size do
local shifth = (i-1)*2+1
local shiftv = size - i +1
local maxshift = size*2-1
blinsert(c, bl_bend_1wire_from_down_to_right(shifth,1,maxshift,shiftv),1,1,1)
end
return c
end
function bl_t_bundle_to_down_step2(size)
-- crossing
local c = new_circ()
for i=1,size do
local hshift = (i-1)*2+1
for k=1,size-i do
blinsert(c,gnode_to_circ(g_wire_cross("off")),hshift,1,k)
end
local vstart = size-i+1
blinsert(c,gnode_to_circ(g_wire_t("d","off")),hshift,1,vstart)
for k=size-i+2,size do
blinsert(c, gnode_to_circ(g_wire_line("h","off")),hshift,1,k)
end
end
for i=1,size do
local hsift = (i-1)*2+1+1
for k=1,size do
blinsert(c,gnode_to_circ(g_wire_line("h","off")),hsift,1,k)
end
end
return c
end
function add_left_connection(size)
-- adding connections
local left = new_circ()
for i=1,size do
blinsert(left,gnode_to_circ(g_wire_line("h","off")),1,1,i)
blinsert(left,gnode_to_circ(g_wire_line("h","off")),2,1,i)
end
return left
end
function bl_bundle_horisontal(count,length)
local c = new_circ()
for x=1,length do
for y=1,count do
blinsert(c,gnode_to_circ(g_wire_line("h","off")),x,1,y)
end
end
return c
end
function add_down_connection(size)
local down = new_circ()
for i=1,size do
local hshift = (i-1)*2+1
blinsert(down,gnode_to_circ(g_wire_line("v","off")),hshift,1,1)
blinsert(down,gnode_to_circ(g_wire_line("v","off")),hshift,1,2)
end
return down
end
function bl_t_bundle_to_down_step2_and_down_conn(size)
-- crossing
local c = bl_t_bundle_to_down_step2(size)
-- adding connections
local down = add_down_connection(size)
local all = new_circ()
blinsert(all,down,1,1,1)
blinsert(all,c,1,1,3)
return all
end
function bl_t_bundle_to_down_step2_and_connections(size)
-- crossing
local c = bl_t_bundle_to_down_step2(size)
-- connections
local left = add_left_connection(size)
local down = add_down_connection(size)
local all = new_circ()
blinsert(all,left,1,1,3)
blinsert(all,down,3,1,1)
blinsert(all,c,3,1,3)
return all
end
------------------------------------------------------------------------
-- generating circuits
------------------------------------------------------------------------
function bl_frame(gate, gstate, vstate, hstate,rot)
-- rot = "up" | "down"
local c = {}
c.nodes = {}
c.metas = {}
if gate == "diode" or gate == "not" then
-- vertical wires
set(c,1,1, g_wire_t("r",vstate))
set(c,2,1, g_wire_bend("lu",vstate))
set(c,1,2, g_wire_line("v",vstate))
if gate == "diode" then
set(c,2,2, g_gate_diode(gstate,rot))
else
set(c,2,2, g_gate_not(gstate,rot))
end
--horizontal wires
set(c,2,3, g_wire_t("d",hstate))
else -- wire
set(c,1,1, g_wire_line("v",vstate))
set(c,1,2, g_wire_line("v",vstate))
set(c,2,3, g_wire_line("h",hstate))
end
-- crossing
if hstate == "off" and vstate == "off" then
set(c,1,3, g_wire_cross("off"))
elseif hstate == "on" and vstate == "off" then
set(c,1,3, g_wire_cross("h"))
elseif hstate == "off" and vstate == "on" then
set(c,1,3, g_wire_cross("v"))
else
set(c,1,3, g_wire_cross("on"))
end
c.nodes.sx = 2
c.nodes.sy = 1
c.nodes.sz = 3
c.metas.sx = 2
c.metas.sy = 1
c.metas.sz = 3
return c
end
function bl_ff(data)
local c = new_circ()
set(c,1,1,g_ff("off","up",data))
set(c,1,2,g_wire_cross("off"))
set(c,2,2,g_wire_t("d","off"))
set(c,2,1,g_wire_bend("lu","off"))
c.nodes.sx = 2
c.nodes.sy = 1
c.nodes.sz = 2
c.metas.sx = 2
c.metas.sy = 1
c.metas.sz = 2
return c
end
function bl_register(vvalue)
local c = new_circ()
for i=1,#vvalue do
local char = string.sub(vvalue,i,i)
if char == "0" then
blinsert(c,bl_ff("off"),(i-1)*2+1,1,1)
else
blinsert(c,bl_ff("on"),(i-1)*2+1,1,1)
end
end
return c
end
function bl_state_value_in(v,hstate)
-- eg. v = "01011"
local len = string.len(v)
local vstate = "off"
local c = new_circ()
local rot = "up"
local curri = 1
for i=1,len do
local char = string.sub(v,i,i)
if char == "0" then
blinsert(c,bl_frame("diode",vstate, vstate,hstate,rot), (i-1)*2+1,1,1 )
else
blinsert(c,bl_frame("not",inverse_state(vstate), vstate,hstate,rot), (i-1)*2+1,1,1 )
end
end
return c
end
function bl_state_value_out(v,hv,vvalue)
-- eg. v = "01011"
-- eg. hv = "on" | "off"
-- eg. vvalue "010110"
local len = string.len(v)
local c = new_circ()
local rot = "down"
local curri = 1
for i=1,len do
local char = string.sub(v,i,i)
local out_wire_state = switch_01_to_onoff(string.sub(vvalue,i,i))
if char == "1" then
blinsert(c,bl_frame("diode",hv, out_wire_state, hv, rot), (i-1)*2+1,1,1 )
else
blinsert(c,bl_frame("wire",hv, out_wire_state, hv, rot), (i-1)*2+1,1,1 )
end
end
return c
end
function bl_pin_value(pins, defined_pins,hstate)
local hpins = list_to_hash(pins)
local c = new_circ()
local rot = "up"
local vstate = "off"
for i=1,#defined_pins do
local v = defined_pins[i]
local p = (i-1)*2+1
if hpins[v] ~= nil then
blinsert(c,bl_frame("not",inverse_state(vstate), vstate, hstate,rot),p,1,1)
elseif hpins[add_negation(v)] ~= nil then
blinsert(c,bl_frame("diode",vstate, vstate, hstate,rot), p,1,1)
else
blinsert(c,bl_frame("wire",vstate, vstate, hstate,rot), p,1,1)
end
end
return c
end
function generate_ifthens(db)
local c = new_circ()
-- computing output state
local cumulative_state = string.rep("0", db.state_bitsize)
for state,ifs in pairs(db.trans) do
for _,v in pairs(ifs) do
local pins = v.pins
local valuein = db.state_values[state]
local valueout = db.state_values[v.state]
local if_pin_state
if has_positive_pin(pins) then
if_pin_state = "on"
else
if_pin_state = "off"
end
local if_state_state
if is_all_zeros(valuein) then
if_state_state = "off"
else
if_state_state = "on"
end
local if_state = or_state(if_pin_state, if_state_state)
if if_state == "off" then -- because of negation in front of it
-- so this one is active
cumulative_state = or_value(cumulative_state,valueout)
end
end
end
-- generating
local thens = new_circ()
local ifs = new_circ()
local i = 1
for state,ife in pairs(db.trans) do
for _,v in pairs(ife) do
local pins = v.pins
local valuein = db.state_values[state]
local valueout = db.state_values[v.state]
local defined_pins = db.inputs_used
-- if state == 0000 then skip generating
if not is_all_zeros(valueout) then
local if_pin_state
if has_positive_pin(pins) then
if_pin_state = "on"
else
if_pin_state = "off"
end
local if_state_state
if is_all_zeros(valuein) then
if_state_state = "off"
else
if_state_state = "on"
end
local if_state = or_state(if_pin_state, if_state_state)
local bl_statein = bl_state_value_in(valuein,if_state)
local bl_pinsin = bl_pin_value(pins, defined_pins, if_state)
local bl_stateout = bl_state_value_out(valueout,inverse_state(if_state), cumulative_state)
-- putting all together
local curr_pos = (i-1)*3+1
blinsert(thens,bl_stateout,1,1,curr_pos)
blinsert(ifs, gnode_to_circ(g_gate_not(inverse_state(if_state) ,"left")),1,1,curr_pos+2)
blinsert(ifs, bl_statein, 2,1,curr_pos)
blinsert(ifs, bl_pinsin, 1+1+ db.state_bitsize*2,1,curr_pos)
i=i+1
end
end
end
blinsert(c,thens,1,1,1)
blinsert(c,ifs, 1+db.state_bitsize*2,1,1)
blinsert(c,bl_register(cumulative_state), 1, 1, (i-1)*3+1 )
blinsert(c,bl_bend_bundle_from_down_to_right_step2(db.state_bitsize),1,1,(i-1)*3+1 +2)
blinsert(c,bl_t_bundle_to_down_step2_and_connections(db.state_bitsize),
db.state_bitsize*2,1,(i-1)*3+1 )
local posy = (i-1)*3+1 +2
local posx = 2*2*db.state_bitsize + 2
blinsert(c,bl_bundle_horisontal(db.state_bitsize,2*#db.inputs_used),posx,1,posy)
return c
end
function pins_to_value(lpins,defined_output_pins)
local hpins = list_to_hash(lpins)
local v = ""
for _,p in ipairs(defined_output_pins) do
if hpins[p] ~= nil then
if not is_negated(p) then
v = v .. "1"
else
v = v .. "0"
end
else
v=v.."0"
end
end
return v
end
function generate_output_ifstate_thenpins(db)
local c = new_circ()
local defined_pins = db.outputs_used
-- computing output state
local cumulative_state = string.rep("0", #defined_pins)
for state,out_pins in pairs(db.outputs) do
local value = db.state_values[state]
if value ~= nil then
if is_all_zeros(value) then
cumulative_state = pins_to_value(out_pins,defined_pins )
end
end
end
-- generating
local ifs = new_circ()
local thens = new_circ()
local nots = new_circ()
local i = 1
for state,out_pins in pairs(db.outputs) do
local value = db.state_values[state]
if value == nil then return new_circ() end --aborting all, no states
local pinso = pins_to_value(out_pins,defined_pins)
local if_state_state
if is_all_zeros(value) then
if_state = "off"
else
if_state = "on"
end
local bl_state = bl_state_value_in(value,if_state)
local bl_stateout = bl_state_value_out(pinso,inverse_state(if_state), cumulative_state)
-- putting all together
local curr_pos = (i-1)*3+1
blinsert(ifs,bl_state,1,1,curr_pos)
blinsert(nots, gnode_to_circ(g_gate_not(inverse_state(if_state) ,"right")),
1,1,curr_pos+2)
blinsert(thens, bl_stateout, 1,1,curr_pos)
i=i+1
end
blinsert(c,ifs,1,1,1)
blinsert(c,nots, db.state_bitsize*2+1,1,1)
blinsert(c,thens,db.state_bitsize*2+2,1,1)
if count_hash(db.outputs) ~= 0 then
blinsert(c,bl_t_bundle_to_down_step2_and_down_conn(db.state_bitsize),
1,1,(i-1)*3+1 )
end
return c
end
-----------------------------------------------------------------
function fill_with_air(c)
if c == nil then return nil end
local sx = c.nodes.sx
local sy = c.nodes.sy
local sz = c.nodes.sz
for ix=1,sx do
for iy=1,sy do
for iz=1,sz do
if m3_get(c.nodes,ix,iy,iz) == nil then
m3_set(c.nodes,ix,iy,iz, {name="air",param2=0})
end
end
end
end
-- do I have to fill meta too?
end
function fsmgenerate_raw(db)
local ifth = generate_ifthens(db)
local ifout = generate_output_ifstate_thenpins(db)
local c = new_circ()
local hsize = ifth.nodes.sx
local vsize = ifth.nodes.sz
local hsize2 = ifout.nodes.sx
local vsize2 = ifout.nodes.sz
local max = math.max(vsize,vsize2)
blinsert(c,ifth,1,1,max+1-vsize)
blinsert(c,ifout,hsize+2,1,max-vsize2+1 )
blinsert(c,bl_bundle_horisontal(db.state_bitsize,1),
1+ifth.nodes.sx,1, max+1-db.state_bitsize)
fill_with_air(c)
return c
end
function generate_circuit(bin,options)
return fsmgenerate_raw(bin)
end

@ -0,0 +1,11 @@
function DeepCopy(Src)
local Dest = {}
if type(Src) ~= "table" then return Src end
for Key, Val in pairs(Src) do
Key = type(Key) == "table" and DeepCopy(Key) or Key
Val = type(Val) == "table" and DeepCopy(Val) or Val
Dest[Key] = Val
end
return Dest
end

@ -0,0 +1,113 @@
function pins_to_area(pins,defined_pins)
-- pins : list
local part = {}
for _,pin in pairs(pins) do
if is_negated(pin) then
part[drop_negation(pin)] = false
else
part[pin] = true
end
end
local parts = {}
table.insert(parts,part)
local area = {}
area.vars = defined_pins
area.parts = parts
return area
end
function part_to_pins(part)
local list = {}
for k,v in pairs(part) do
if v == true then
table.insert(list, k)
else
table.insert(list, add_negation(k))
end
end
return list
end
function apply_priority_state(list_ifs,defined_pins)
if list_ifs == nil then return nil end
if #list_ifs == 0 then return {} end
local new_ifs = {}
local aggreg_area = pins_to_area(list_ifs[1].pins,defined_pins)
table.insert(new_ifs,list_ifs[1])
for i=2,#list_ifs do
local curr_area = pins_to_area(list_ifs[i].pins,defined_pins)
local new_area = area_sub(curr_area,aggreg_area)
aggreg_area = area_add(aggreg_area,curr_area)
for _,part in pairs(new_area.parts) do
table.insert(new_ifs, { pins = part_to_pins(part), state = list_ifs[i].state })
end
end
return new_ifs
end
function apply_priority_special_case_no_input_pins(db)
-- no inputs, adding one phantom input
table.insert(db.inputs_used, "x")
apply_priority(db)
db.inputs_used = {}
end
-- changes db.trans
function apply_priority(db)
if db.inputs_used == nil then return end
if #db.inputs_used == 0 then
if count_hash(db.trans) == 0 then
return
else
apply_priority_special_case_no_input_pins(db)
return
end
end
local new_trans = {}
for state, v in pairs(db.trans) do
new_trans[state] = apply_priority_state(v, db.inputs_used)
end
db.trans = new_trans
end
------------------------------------------------------------------------
-- other algorithms
------------------------------------------------------------------------
function clear_empty_transitions(db)
for state,ifs in pairs(db.trans) do
if #ifs == 0 then
db.trans[state] = nil
end
end
end
------------------------------------------------------------------------
-- default behavior
------------------------------------------------------------------------
function add_default_loops(db)
-- if no transition, stay in current state
local hreachables = db.reachables
for s,_ in pairs(hreachables) do
if db.trans[s] == nil then
db.trans[s] = {}
end
table.insert(db.trans[s], { pins = {} , state = s } )
end
end

@ -310,6 +310,13 @@ dofile(minetest.get_modpath("mesecons_autotools").."/book/circuit.lua");
--dofile(minetest.get_modpath("mesecons_autotools").."/book/library.lua");
dofile(minetest.get_modpath("mesecons_autotools").."/book/file.lua");
-- Register FSM generator
dofile(minetest.get_modpath("mesecons_autotools").."/fsm/fsm.lua");
-- Register Formula generator
dofile(minetest.get_modpath("mesecons_autotools").."/formula/formula.lua");
-- Register chatcommands
dofile(minetest.get_modpath("mesecons_autotools").."/commands/all_commands.lua");

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

@ -39,7 +39,7 @@ dofile(minetest.get_modpath("mesecons_autotools").."/tools/bundle.lua");
local tool_list = {
{"black", "Auto Wire Tool"},
{"grey", "Auto crossing Tool"},
{"grey", "Auto crossing Tool"},
{"blue", "Selection Tool"},
{"white_up", "Selection Top Edge Tool"},
{"white_down", "Selection Bottom Edge Tool"},
@ -50,6 +50,7 @@ local tool_list = {
{"yellow", "Move Horizontal Tool"},
{"yellow_updown", "Move Vertical Tool"},
{"refresh", "Refresh Tool"},
}
for _,t in pairs(tool_list) do
local tool = t[1]

@ -51,7 +51,7 @@ mesecons_autotools.register_action("black","right","block", function(user,pos,ra
mesecons_autotools.zero_stack_direction(user)
elseif create_bundle_bended_wire(user,sel,pos,user_direction) then
-- nothing to do here so far
-- update of new selection is inside the function
-- update of new selection if inside the function
end

@ -1,23 +1,24 @@
mesecons_autotools.register_action("test","left","air", function(user,pos,rad)
end)
end)
mesecons_autotools.register_action("test","left","block", function(user,pos,rad)
local node = minetest.get_node(pos)
print ("rules[" .. dump(mesecon.get_any_rules(node)) .. "]" )
end)
local node = minetest.get_node(pos)
-- print ("rules[" .. dump(mesecon.get_any_rules(node)) .. "]" )
print("node=" .. dump(minetest.get_node(pos) ))
print("meta=" ..dump(minetest.get_meta(pos):to_table() ) )
end)
mesecons_autotools.register_action("test","right","block", function(user,pos,rad)
end)
mesecons_autotools.register_action("test","right","air", function(user,pos,rad)
local pos1 = mesecons_autotools.get_pos(user,1)
local pos2 = mesecons_autotools.get_pos(user,2)
print ("D=pos1="..dump(pos1) .. ", pos2="..dump(pos2))
end)
mesecons_autotools.register_action("test","right","block", function(user,pos,rad)
end)
mesecons_autotools.register_action("test","right","air", function(user,pos,rad)
local pos1 = mesecons_autotools.get_pos(user,1)
local pos2 = mesecons_autotools.get_pos(user,2)
print ("D=pos1="..dump(pos1) .. ", pos2="..dump(pos2))
end)

@ -2,3 +2,4 @@ name = mesecons_x
title = Mesecons Extended
author = marek
description = Adds Latch, Flipflop, tools for automatic wireing, 3-input gates etc.
release = 10474