From 7e77480fc7ebea3e1a09b01b653d2bb843092b4f Mon Sep 17 00:00:00 2001 From: Deet Mit Date: Fri, 28 Jan 2022 14:53:26 +0100 Subject: [PATCH] add FSM generator (orange book) --- mesecons_autotools/book/circuit.lua | 60 +- mesecons_autotools/book/file.lua | 9 + mesecons_autotools/book/library.lua | 1 - mesecons_autotools/book/m3.lua | 19 + mesecons_autotools/book/misc.lua | 122 +-- mesecons_autotools/book/stats.lua | 3 + mesecons_autotools/formula/README | 1 + mesecons_autotools/formula/formula.lua | 33 + mesecons_autotools/fsm/alg.lua | 163 ++++ mesecons_autotools/fsm/compiler.lua | 470 +++++++++++ mesecons_autotools/fsm/compiler_checking.lua | 383 +++++++++ mesecons_autotools/fsm/compiler_general.lua | 191 +++++ .../fsm/compiler_generating.lua | 225 ++++++ mesecons_autotools/fsm/compiler_parsing.lua | 216 +++++ mesecons_autotools/fsm/fsm.lua | 275 +++++++ mesecons_autotools/fsm/generate.lua | 742 ++++++++++++++++++ mesecons_autotools/fsm/lib.lua | 11 + mesecons_autotools/fsm/priority.lua | 113 +++ mesecons_autotools/init.lua | 7 + mesecons_autotools/textures/formula_book.png | Bin 0 -> 1918 bytes mesecons_autotools/textures/fsm_book.png | Bin 0 -> 2219 bytes .../textures/fsm_book_empty.png | Bin 0 -> 1916 bytes mesecons_autotools/tools.lua | 3 +- mesecons_autotools/tools/black/black.lua | 2 +- mesecons_autotools/tools/test/test.lua | 35 +- modpack.conf | 1 + 26 files changed, 3002 insertions(+), 83 deletions(-) create mode 100644 mesecons_autotools/formula/README create mode 100644 mesecons_autotools/formula/formula.lua create mode 100644 mesecons_autotools/fsm/alg.lua create mode 100644 mesecons_autotools/fsm/compiler.lua create mode 100644 mesecons_autotools/fsm/compiler_checking.lua create mode 100644 mesecons_autotools/fsm/compiler_general.lua create mode 100644 mesecons_autotools/fsm/compiler_generating.lua create mode 100644 mesecons_autotools/fsm/compiler_parsing.lua create mode 100644 mesecons_autotools/fsm/fsm.lua create mode 100644 mesecons_autotools/fsm/generate.lua create mode 100644 mesecons_autotools/fsm/lib.lua create mode 100644 mesecons_autotools/fsm/priority.lua create mode 100644 mesecons_autotools/textures/formula_book.png create mode 100644 mesecons_autotools/textures/fsm_book.png create mode 100644 mesecons_autotools/textures/fsm_book_empty.png diff --git a/mesecons_autotools/book/circuit.lua b/mesecons_autotools/book/circuit.lua index 61af353..652186a 100644 --- a/mesecons_autotools/book/circuit.lua +++ b/mesecons_autotools/book/circuit.lua @@ -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) diff --git a/mesecons_autotools/book/file.lua b/mesecons_autotools/book/file.lua index 22c0978..d19d084 100644 --- a/mesecons_autotools/book/file.lua +++ b/mesecons_autotools/book/file.lua @@ -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() diff --git a/mesecons_autotools/book/library.lua b/mesecons_autotools/book/library.lua index d2d89d8..268571d 100644 --- a/mesecons_autotools/book/library.lua +++ b/mesecons_autotools/book/library.lua @@ -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 diff --git a/mesecons_autotools/book/m3.lua b/mesecons_autotools/book/m3.lua index 5770052..28c52a2 100644 --- a/mesecons_autotools/book/m3.lua +++ b/mesecons_autotools/book/m3.lua @@ -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 + diff --git a/mesecons_autotools/book/misc.lua b/mesecons_autotools/book/misc.lua index c1b900b..31df79a 100644 --- a/mesecons_autotools/book/misc.lua +++ b/mesecons_autotools/book/misc.lua @@ -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 diff --git a/mesecons_autotools/book/stats.lua b/mesecons_autotools/book/stats.lua index 5196548..51f95d8 100644 --- a/mesecons_autotools/book/stats.lua +++ b/mesecons_autotools/book/stats.lua @@ -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 diff --git a/mesecons_autotools/formula/README b/mesecons_autotools/formula/README new file mode 100644 index 0000000..00aac66 --- /dev/null +++ b/mesecons_autotools/formula/README @@ -0,0 +1 @@ +Formula tool (still in progress, not published in mod) diff --git a/mesecons_autotools/formula/formula.lua b/mesecons_autotools/formula/formula.lua new file mode 100644 index 0000000..06b4b0e --- /dev/null +++ b/mesecons_autotools/formula/formula.lua @@ -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, + --]] + }) diff --git a/mesecons_autotools/fsm/alg.lua b/mesecons_autotools/fsm/alg.lua new file mode 100644 index 0000000..654b7e5 --- /dev/null +++ b/mesecons_autotools/fsm/alg.lua @@ -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)) +--]] \ No newline at end of file diff --git a/mesecons_autotools/fsm/compiler.lua b/mesecons_autotools/fsm/compiler.lua new file mode 100644 index 0000000..7a179d8 --- /dev/null +++ b/mesecons_autotools/fsm/compiler.lua @@ -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 + + + + + diff --git a/mesecons_autotools/fsm/compiler_checking.lua b/mesecons_autotools/fsm/compiler_checking.lua new file mode 100644 index 0000000..f1aaac9 --- /dev/null +++ b/mesecons_autotools/fsm/compiler_checking.lua @@ -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 + + + diff --git a/mesecons_autotools/fsm/compiler_general.lua b/mesecons_autotools/fsm/compiler_general.lua new file mode 100644 index 0000000..39183cb --- /dev/null +++ b/mesecons_autotools/fsm/compiler_general.lua @@ -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 \ No newline at end of file diff --git a/mesecons_autotools/fsm/compiler_generating.lua b/mesecons_autotools/fsm/compiler_generating.lua new file mode 100644 index 0000000..74bda63 --- /dev/null +++ b/mesecons_autotools/fsm/compiler_generating.lua @@ -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 diff --git a/mesecons_autotools/fsm/compiler_parsing.lua b/mesecons_autotools/fsm/compiler_parsing.lua new file mode 100644 index 0000000..b39ebf5 --- /dev/null +++ b/mesecons_autotools/fsm/compiler_parsing.lua @@ -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 diff --git a/mesecons_autotools/fsm/fsm.lua b/mesecons_autotools/fsm/fsm.lua new file mode 100644 index 0000000..d95e00b --- /dev/null +++ b/mesecons_autotools/fsm/fsm.lua @@ -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 "" end + if data.circ == nil then return "" end + if data.circ.nodes == nil then return "" end + + local nodes = data.circ.nodes + local stats = "" + 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, + + }) diff --git a/mesecons_autotools/fsm/generate.lua b/mesecons_autotools/fsm/generate.lua new file mode 100644 index 0000000..5c31130 --- /dev/null +++ b/mesecons_autotools/fsm/generate.lua @@ -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 diff --git a/mesecons_autotools/fsm/lib.lua b/mesecons_autotools/fsm/lib.lua new file mode 100644 index 0000000..0959dbc --- /dev/null +++ b/mesecons_autotools/fsm/lib.lua @@ -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 diff --git a/mesecons_autotools/fsm/priority.lua b/mesecons_autotools/fsm/priority.lua new file mode 100644 index 0000000..7c6f37e --- /dev/null +++ b/mesecons_autotools/fsm/priority.lua @@ -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 + + diff --git a/mesecons_autotools/init.lua b/mesecons_autotools/init.lua index a44b55d..b1f808a 100644 --- a/mesecons_autotools/init.lua +++ b/mesecons_autotools/init.lua @@ -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"); diff --git a/mesecons_autotools/textures/formula_book.png b/mesecons_autotools/textures/formula_book.png new file mode 100644 index 0000000000000000000000000000000000000000..0108e99685e02174c39a5084cc148840aa66d0c6 GIT binary patch literal 1918 zcmV-^2Z8vBP) zaB^>EX>4U6ba`-PAZ2)IW&i+q+SONEawI7X{O1&V1SBB@$Dw#c><#w#n^5%2?Y2Gk z#7=DEDnO`&$P@y*{?FgT{e>Ts1sBv(%rScWLJpY=8p)r(yx!!)dhd?EG#=#cI=~PK zYB{@Bwtj(JZy#LdpyN^Q(jMWo4P7j6gED7kG}^tBkn<_0unpyYHeyb8@9M#4UAN+L zWj|ue^wr4hyL&+}pwtvv!rX<#uXj2(YraYeGdF@N6O7TK|3z^h4gN}TW7G)| z`EPCVzvH%3dBl_O6v zaubM9iDKX;O95FSDB{OLLJSoZ4XPT|G&#*$qQnq2#%OUSxng3$)S{Us%T|)4kTk_) zDW#lB<}Ap8nq#({a?Yi&E10OjR)KzjaJJOMlQV~8iRIRaEO*PljxJg5r zXt7yKEw|Fib!3Vjy7t(ur=EKmh_xXj3>|UUNF$GOWo@(iRDOXq+N|+nYUsX!6uOw{-OzF#B=Sv zQ*UW2;p*uwjc?V|%cdx~h}*?28dflrc1}8!twFY7^XVhlT>TBl^{m}%oIl*8PbB_d zZzF%+(7;s>d(8a{(ElFLOOll#0004nX+uL$Nkc;*aB^>EX>4Tx0C=2zkv&MmP!xqv zTcsjZ5eteqWT;LSM2k3T6^c+H)C#RSn7s54nlvOSE{=k0!NH%!s)LKOt`4q(Aov5~ z=;Wm6A|>9J6k5c1;qgAsyXWxUeSp7SW~$jS0jQc~q!Muqpci*4YqvT8m_ypovrW+RV2J!T!rE}gV4zr@95T6r|8+1Y9N3P2* zzi}=&?B|(bBb}Nj4igK7HkR9%6%Ca*MjTO8jq?2&mle)ioYiubHSft^7|LlY%Uq{5 zf&><^1Q7ycR8c}17NWFkq?kz2dECQ4?D$1;$>b`5kz)ZBsE`~#_#gb9t(l*ibdy4H zp!3DHKSqJzF3_mi_V=-EH%@@SGjOG~{FOQ|^GSNGrA3Z_zHQ**x~0i`z~v4w^rTCM z^3A0%HZrUiWx+SNq)l?P<>M2YPyP zsxo>J82|tP24YJ`L;(K){{a7>y{D4^000SaNLh0L01FcU01FcV0GgZ_00007bV*G` z2j&404mKD`;%{640081iL_t(I%VS`m5HRAC)O;rOAI82e^^B1u1CTW%19XEJNzshW zASQA&GaN%OnaI&x{H+SkWg%5_#h(TSK4Cso7m%cxmye<9dl8x!jxjJW^rHF=r{;Y{ z4A(;tF#$2aih-fQrh{Rz%34f=pqe?@IT)(G7cpG#MU6%lWR9SKFvH^5wG0dl3=FZy zf*BZ&GcXkES21w#axgR;sld>Tk`6SBA>ptk_W%O}0|UeR4{sUn-oMSj#>NJ5cJDJr zqSCfzvDAOfHB$f2TsZ!J<@!bcVFr^7G}lOxubHj@0J(Hog%!Qg9{>OV07*qoM6N<$ Ef{LDuQUCw| literal 0 HcmV?d00001 diff --git a/mesecons_autotools/textures/fsm_book.png b/mesecons_autotools/textures/fsm_book.png new file mode 100644 index 0000000000000000000000000000000000000000..ff366102959e2703f5c49092986ddbf997bdcb47 GIT binary patch literal 2219 zcmV;c2vqlpP) zaB^>EX>4U6ba`-PAZ2)IW&i+q+SONEb|fbZ{bvuRg p|Mm*%5T=E{spdo~hs-g0tBQR=%AQq*Tt3-7X?fy7+3(gtZ4y{;`ONOdJ>*!v#lVf*=h@(N{JY(3`33@Wg661S*gNA$DI`=b7fhw zD9VZJV$98l2CF1#62V*LZbT+|w=J~RO>4bM5i2*sP$np&MgJ#{`Bmpn9y3Or5Rr{H zRJ6<L!jCJkw# zg{CdG)N&`&ktw?CzK5BYlqdB=Lf9OVT~74bDZ9+L26Vl z6*SX{pPYd)b_B+CG609>$(fDh98cyZXErf{!ZVEYlN-}$G6o7=C-#GG?jFp2i#Oxr zw|L{Pm@|{Q|AILK>Rx&KfVDQyxw;tJQ8+QRg6JFEGe?uTR7FCuXyDKOZfX8h!H*S5 zv#XN3$ng24+tkr_f0e=h_w6!2vO_|@TKaCN0s4HI^qBv1JI#M%4}$hH9Y3|xyy{@L zDsbnK`>T>DmDiz^B&|Pj#*Go?_zm4CNK{fJm)isFhYJzx zZAdySHu)&#BOM`Z&|Fom>H&JuYbUlQr@HMB5;fH;tj&(Aw&$A#_?oT$br{XPn}=ZO*#lw5TbS zv|F*FlbTs`J35Gto`{nxuGMg?e^OG9do3j#R*hOWdp0(t)ly5_T-?Dabg0HT zCmkVh{2zxK?$s+pUd}q`K%6R&e2(`wo^wLHI~=R-+xe*He1|fye}o!cBp}YL+c_H@ zb$exYw(A~Yoog53aYN&4)g1iELAaj2B!8|4X8={~7dXG*-j*L2P!?zc|L_CNJj=W9&+1om76W`D@hme;n|OmbwrLxj_lZNSD67Qh#N#Gikob}7 zipy`D3r!YyX2?vZ=7~eZVxfbT4rWDDBc3J>tC~*vLdIp4^A=~VTxG3$@)rhk`pPob zX^kR*MJz#r2nAJ?P=<{t?K&wIQgj~o@Q*b861fy|mB7fcfC@CoO+WY_{GP3qpPF=& zf^nex#c@6&KwuYW)Ewvg*l`*sfd3h|(%b$@9hmtfz1G&kN5H@~aB4yW|h1F4i15_0%fmzyt}7!ZvXbQ=Jx}g zEONMx^}6f;000JJOGiWi{{a60|De66lK=n!32;bRa{vGf6951U69E94oEQKA00(qQ zO+^Rh0TK%;6tZ0tF8}}maY;l$R5;6}ld($zQ5?s=PY#DbQtrfa6zd!uil(SPph;-c zp~*3BaLOSxx{8gf6CA0G2tdn`H~_?yBo;y0 zr;)H`SW)O9SBQm$n3BY}SOY89(e6{xX#mhet_L_T_BAmQmLcD@!OC?Q_Qcn)Wg+HZ zW@D@I0st6|KVVuE!C(-zvjT=+Oo+NWE?GH9El1)pB?(rp!?Y;EmAoiBLsc=_&25LQ toW$9)4qVgcSwJ|@)Eq+A{fz;v89$4@losCiNoW87002ovPDHLkV1k1S8SnrA literal 0 HcmV?d00001 diff --git a/mesecons_autotools/textures/fsm_book_empty.png b/mesecons_autotools/textures/fsm_book_empty.png new file mode 100644 index 0000000000000000000000000000000000000000..3cfc8cb544771d1c12a676afca80890edce36ff9 GIT binary patch literal 1916 zcmV-?2ZQ*DP) zaB^>EX>4U6ba`-PAZ2)IW&i+q+T~VTmg^`C{AU%j1SBCI%i(#>-N7t>3XGkk)7|O5 zeb3CtgxJCYi=s+^P5=AXFn{64JD|*w8I^Vq5`I4XAG6EOgHgXT z%9@j%`3^c3gp&1|M+vwT^{`4pi8k8`ahk2Rpj1l4P-Pf$)F{)SzHFrmh>kl4Smw&I zd{F`?u1hgD3k?pEq)CKsmAipv5bwf5YuvQPs}wPEBXng#Fk13|P|RnI-zjE{Iw>L> zFRp-#XJ!G$RBqnU3o6jqy6GbLEL(nimp@2V$fk?thzVv}ju1uZmRquP7W7ltV^xp| zZ3h5F7+V2`1PWMBqU6a2=0F4U00aE1#knBV}o4{By7#r^dT76mB^K@fx zf)FZR0<=kzA(nHB{0S~1hq8(~RrMM)8cmwLc<0r7AN<_pa**JH1|LF*A%z?zx~S2| z5MxX+Cr*MrP?Jv~#gtOcY%&JQh?UVl!&A6OaYc(Sp~R9(u1KHis#ae^jWyNWxJkn{ z(R>Rnw$ySb)3H-@*R}f|dhDs^fm|Ch+|c1i7;&VLFV(iHPvsk`(N>L@QnO#URD;)q zaE73{o#@mIim@Xo9;O0FXr7wcNY3$8Zfa%|V^Jsrq)u&2qp27qbe&iSUE1B0`;u-( zfSIU_w-Jej-K)Pq$-cha1bFMDMj#W50wSv`$_8zn0nCGFEe`^2bKm$CkDkh3a zJDXRFJ*{-8w(6xkyC5JyGOyYO1Z1a&vcVfr9 zX_Y##dH#5bRDZ;C*kgk(j5~89kN0#S@F+uSI&l{1Hon>)%qca9xXWJb;qi!LeTcdS z(;+srLAUAL&>Oc0Ez!=)2MT$gX*h-sdggO;((64|$RlJ~p--aWb|AAE)~Qj<4-R zdq#|7Dn&2tz|{%0Gmr;=wiKskr$jk1N9m*9h|hK4L+ratM4CS{#vz`&DMQbO-b%WN`1G4rfJ|ib9Z}d%|{I%*^q17)Wy*HmD>i{FtnYFAcbn} z;e;8I|0+839W^)00D(* zLqkwWLqi~Na&Km7Y-Iodc$|HaJxIe)6opSyrJ@x6gO1!Trw21M-<9(cW&*8oM0HI!H zn$>rh#)32%b1g-Bs}Zup1P^-qCCsH@6YO2a~1=9BJnIU zOq+OvIJRjUocD=CtSGC*=fvYCU6A;Z>x#>7oC{4BcxK2)|5Tqat9cGGGtS3N`6{GJ`cQ~(Kls*pxhUd%U}+b8i3kwC48%oh)*=j`h0i00006VoOIv0RI600RN!9r;`8x z010qNS#tmY3ljhU3ljkVnw%H_000McNliru<^d85Dk0?V*9-sv0QpHoK~y-)V_={V zFyfQ^ma6$5#^#!RoslF1kToL%bb}a4(TvR?CUP`?c=!>{W+F%PwwNk7pM_M-yOSCi zgn9W;T|kOvK3+Zs^NB^MF8J{9Bg5rSuNZXs#25tR`SEJzYK~!$I`M}Q#Q*_$eufWT z9SqMDRT(a4*rFH&)6Bun!C*eI2t_kW7%(s}FbE0=Gce3J%WCShnsNe4SESpG9GFfgdBxW>T1z`*eS!&`>C_ir<>v9U35HODZxW6_Ki z?6hF{|IAJ5|CtNN|F2xX=s(P0lEE`KDe^Vb6#xKA`e_-6Uo8g!0000