158 lines
5.0 KiB
Lua
158 lines
5.0 KiB
Lua
-- Natural language compiler, outputs lua code
|
|
-- (C) rnd 2021
|
|
-- TODO: add IF: if condition arg1 ACTION1 else ACTION2 ?
|
|
-- ADD SUBROUTINE: sub NAME = enter subroutine definition mode, sub end = ends definition mode
|
|
|
|
if not init then init = true
|
|
dtext = {};
|
|
dout = function(text) dtext[#dtext+1] = text end
|
|
text =
|
|
[[
|
|
if cond1 value action1 and action2 and action3 else actiona1 and actiona2
|
|
]]
|
|
|
|
translate = { -- dictionary of used words
|
|
["forward"] = "forward",
|
|
["backward"] = "backward",
|
|
["left"] = "left",
|
|
["right"] = "right",
|
|
["random"] = "random",
|
|
["dirt"] = "default:dirt",
|
|
["wood"]= "default:wood",
|
|
["cobble"] = "default:cobble",
|
|
}
|
|
|
|
cmds = {
|
|
["if"] = function(code,ibeg,iend) -- if COND value ACTION1 and ACTION2 and ... ACTIONn else(optional) ACTION1 and ... and ACTIONm
|
|
-- COND: 'see' nodename (block right in front), 'var = value' value of variable?,...
|
|
local ELSE = " else"
|
|
local AND = " and"
|
|
local i,j,condtype,value;
|
|
condtype, j = get_next_word(code,ibeg,iend)
|
|
local out = {};
|
|
dout(condtype)
|
|
|
|
if condtype== "see" then
|
|
value, j = get_next_word(code,j,iend)
|
|
value = translate(value or "");
|
|
if not minetest.registered_nodename(value) then say("error: unknown block name " .. value .. "used in 'if'") return "" end
|
|
out[#out+1] = "if read_node.forward() == " .. value .. " then ";
|
|
else
|
|
say("error: unknown condition " .. condtype .. " used in 'if'") return "" end
|
|
end
|
|
-- now after j left: ACTION1 else(optional) ACTION2, ACTION can be multiple, separated by ' and '
|
|
|
|
|
|
-- parse and before else
|
|
local k = j;
|
|
lcoal cmds = {}
|
|
while k do
|
|
k = string.find(code,AND,j)
|
|
if k and k<ielse then
|
|
cmds[#cmds+1] = string.sub(code,j,k-1)
|
|
j=k+1;
|
|
else
|
|
break
|
|
end
|
|
end
|
|
|
|
dout(table.concat(cmds,"\n"))
|
|
|
|
|
|
--j=...
|
|
out[#out+1] = "end"
|
|
|
|
|
|
return table.concat(out," "),j
|
|
|
|
end,
|
|
|
|
["move"] = function(code,ibeg,iend)
|
|
-- move forward count
|
|
local i,direction,count
|
|
direction,i = get_next_word(code,ibeg,iend)
|
|
direction = translate[direction];
|
|
if not direction then say("error: unknown direction used in 'turn'") return "" end
|
|
return "move."..direction .."(); pause();",i;
|
|
end,
|
|
|
|
["turn"] = function(code,ibeg,iend)
|
|
-- move forward count
|
|
local i,direction,count
|
|
direction,i = get_next_word(code,ibeg,iend)
|
|
|
|
direction = translate[direction];
|
|
if not direction then say("error: unknown direction used in 'turn'") return "" end
|
|
local c;
|
|
if direction == "random" then
|
|
c = "if math.random(2) == 1 then turn.left() else turn.right() end;";
|
|
else
|
|
c = "turn."..direction .."();"
|
|
end
|
|
return c.." pause();",i
|
|
end,
|
|
|
|
["dig"] = function() return "dig.forward(); pause();" end,
|
|
|
|
["place"] = function(code,ibeg,iend)
|
|
-- place nodename
|
|
local nodename,i
|
|
nodename,i = get_next_word(code,ibeg,iend)
|
|
nodename = translate[nodename]
|
|
if not nodename then say("error: unknown nodename used in 'place'") return "" end
|
|
|
|
return "place.forward('" .. nodename .. "'); pause();",i
|
|
end,
|
|
|
|
}
|
|
|
|
-- given position ibeg in string find next word, return it and then return position immediately after word.
|
|
-- word is defined as a sequence of alphanumeric characters (%w)
|
|
-- example 'hello world', ibeg = 1. -> 'hello', 6
|
|
|
|
get_next_word = function(code, ibeg,iend) -- attempt to return next word, starting from position ibeg. returns word, index after word
|
|
if not ibeg or not iend then return end
|
|
local j = string.find(code,"%w",ibeg); -- where is start of word?
|
|
if not j or j>iend then return "", iend+1 end -- no words present
|
|
ibeg = j;
|
|
j = string.find(code,"%W",j);--where is end of word?
|
|
if not j or j>iend then return string.sub(code,ibeg,iend-1),iend+1 end
|
|
return string.sub(code,ibeg,j-1), j
|
|
end
|
|
|
|
|
|
parse_code = function(code)
|
|
local out = {};
|
|
local ibeg,iend,word;
|
|
local clen = string.len(code)
|
|
local step =0
|
|
|
|
iend = 1; ibeg = 1;
|
|
while step < 10 do
|
|
if ibeg>clen then break end
|
|
step = step+1
|
|
iend = string.find(code, "\n", ibeg)
|
|
if not iend then iend = clen end -- get out of loop, no more lines to process
|
|
|
|
word, ibeg = get_next_word(code,ibeg,iend)
|
|
--dout("rem " .. string.sub(code,ibeg,iend))
|
|
--dout("Dword '" .. word .. "' " .. ibeg .. " " .. iend)
|
|
local cmd = cmds[word];
|
|
|
|
if cmd then out[#out+1],ibeg = cmd(code,ibeg,iend) end
|
|
if not ibeg then ibeg = iend+1 end
|
|
if ibeg<=iend then -- still some space remaining in line, last parameter is repetition
|
|
local count,i
|
|
count,i = get_next_word(code,ibeg,iend);
|
|
count = tonumber(count) or 1
|
|
if count>9 then count = 9 elseif count<1 then count=1 end
|
|
if count > 1 then out[#out] = "for i=1,"..count.. " do " .. out[#out] .. " end" end
|
|
end
|
|
ibeg = iend +1 -- go new line
|
|
end
|
|
return table.concat(out,"\n")
|
|
end
|
|
|
|
|
|
self.label(parse_code(text) .. "\n\n" .. table.concat(dtext,"\n"))
|
|
end |