--[[ String gates ! :P ]] local MAX_LEN = 1024*1024 -- max string length of 1MB GateActions("String") GateActions["string_ceq"] = { name = "Equal", inputs = { "A" , "B" }, inputtypes = { "STRING" , "STRING" }, output = function(gate, A, B) if A == B then return 1 else return 0 end end, label = function(Out, A, B) return string.format ("(%s == %s) = %d", A, B, Out) end } GateActions["string_cineq"] = { name = "Inequal", inputs = { "A" , "B" }, inputtypes = { "STRING" , "STRING" }, output = function(gate, A, B) if A ~= B then return 1 else return 0 end end, label = function(Out, A, B) return string.format ("(%s != %s) = %d", A, B, Out) end } GateActions["string_index"] = { name = "Index", inputs = { "A" , "Index" }, inputtypes = { "STRING" , "NORMAL" }, outputtypes = { "STRING" }, output = function(gate, A, B) if !A then A = "" end if !B then B = 0 end return string.sub(A,B,B) end, label = function(Out, A, B) return string.format ("index(%s , %s) = %q", A, B, Out) end } GateActions["string_length"] = { name = "Length", inputs = { "A" }, inputtypes = { "STRING" }, output = function(gate, A) if !A then A = "" end return #A end, label = function(Out, A) return string.format ("length(%s) = %d", A, Out) end } GateActions["string_upper"] = { name = "Uppercase", inputs = { "A" }, inputtypes = { "STRING" }, outputtypes = { "STRING" }, output = function(gate, A) if !A then A = "" end return string.upper(A) end, label = function(Out, A) return string.format ("upper(%s) = %q", A, Out) end } GateActions["string_lower"] = { name = "Lowercase", inputs = { "A" }, inputtypes = { "STRING" }, outputtypes = { "STRING" }, output = function(gate, A) if !A then A = "" end return string.lower(A) end, label = function(Out, A) return string.format ("lower(%s) = %q", A, Out) end } GateActions["string_sub"] = { name = "Substring", inputs = { "A" , "Start" , "End" }, inputtypes = { "STRING" , "NORMAL" , "NORMAL" }, outputtypes = { "STRING" }, output = function(gate, A, B, C) if !A then A = "" end if !B then B = 1 end -- defaults to start of string if !C then C = -1 end -- defaults to end of string return string.sub(A,B,C) end, label = function(Out, A, B, C) return string.format ("%s:sub(%s , %s) = %q", A, B, C, Out) end } GateActions["string_explode"] = { name = "Explode", inputs = { "A" , "Separator" }, inputtypes = { "STRING" , "STRING" }, outputtypes = { "ARRAY" }, output = function(gate, A, B) if !A then A = "" end if !B then B = "" end return string.Explode(B,A) end, label = function(Out, A, B) return string.format ("explode(%s , %s)", A, B) end } GateActions["string_find"] = { name = "Find", inputs = { "A", "B", "StartIndex" }, inputtypes = { "STRING", "STRING" }, outputtypes = { "NORMAL" }, outputs = { "Out" }, output = function(gate, A, B, StartIndex) local r,_ = string.find(A,B,StartIndex,true) return r or 0 end, label = function(Out, A, B) if istable(Out) then Out = Out.Out end return string.format ("find(%s , %s) = %d", A, B, Out) end } GateActions["string_concat"] = { name = "Concatenate", inputs = { "A" , "B" , "C" , "D" , "E" , "F" , "G" , "H" }, inputtypes = { "STRING" , "STRING" , "STRING" , "STRING" , "STRING" , "STRING" , "STRING" , "STRING" }, outputtypes = { "STRING" }, output = function(gate, A, B, C, D, E, F, G, H) if (A and #A or 0) + (B and #B or 0) + (C and #C or 0) + (D and #D or 0) + (E and #E or 0) + (F and #F or 0) + (G and #G or 0) + (H and #H or 0) > MAX_LEN then return false end local T = {A,B,C,D,E,F,G,H} return table.concat(T) end, label = function(Out) return string.format ("concat = %q", Out) end } GateActions["string_trim"] = { name = "Trim", inputs = { "A" }, inputtypes = { "STRING" }, outputtypes = { "STRING" }, output = function(gate, A) if !A then A = "" end return string.Trim(A) end, label = function(Out, A) return string.format ("trim(%s) = %q", A, Out) end } GateActions["string_replace"] = { name = "Replace", inputs = { "String" , "ToBeReplaced" , "Replacer" }, inputtypes = { "STRING" , "STRING" , "STRING" }, outputtypes = { "STRING" }, output = function(gate, A, B, C) if !A then A = "" end if !B then B = "" end if !C then C = "" end return string.gsub(A,B,C) end, label = function(Out, A, B, C) return string.format ("%s:replace(%s , %s) = %q", A, B, C, Out) end } GateActions["string_reverse"] = { name = "Reverse", inputs = { "A" }, inputtypes = { "STRING" }, outputtypes = { "STRING" }, output = function(gate, A) if !A then A = "" end return string.reverse(A) end, label = function(Out, A) return string.format ("reverse(%s) = %q", A, Out) end } GateActions["string_tonum"] = { name = "To Number", inputs = { "A" }, inputtypes = { "STRING" }, outputtypes = { "NORMAL" }, output = function(gate, A) if !A then A = "" end return tonumber(A) end, label = function(Out, A) return string.format ("tonumber(%s) = %d", A, Out) end } GateActions["string_tostr"] = { name = "Number to String", inputs = { "A" }, inputtypes = { "NORMAL" }, outputtypes = { "STRING" }, output = function(gate, A) if !A then A = 0 end return tostring(A) end, label = function(Out, A) return string.format ("tostring(%s) = %q", A, Out) end } GateActions["string_tobyte"] = { name = "To Byte", inputs = { "A" }, inputtypes = { "STRING" }, outputtypes = { "NORMAL" }, output = function(gate, A) if !A then A = "" end return string.byte(A) end, label = function(Out, A) return string.format ("tobyte(%s) = %d", A, Out) end } GateActions["string_tochar"] = { name = "To Character", inputs = { "A" }, inputtypes = { "NORMAL" }, outputtypes = { "STRING" }, output = function(gate, A) if !A then A = 0 end return string.char(A) end, label = function(Out, A) return string.format ("tochar(%s) = %q", A, Out) end } GateActions["string_repeat"] = { name = "Repeat", inputs = { "A" , "Num"}, inputtypes = { "STRING" , "NORMAL" }, outputtypes = { "STRING" }, output = function(gate, A, B) if !A then A = "" end if !B or B<1 then B = 1 end if B * #A > MAX_LEN then return false end return string.rep(A,B) end, label = function(Out, A) return string.format ("repeat(%s) = %q", A, Out) end } GateActions["string_ident"] = { name = "Identity", inputs = { "A" }, inputtypes = { "STRING" }, outputtypes = { "STRING" }, output = function(gate, A ) return A end, label = function(Out, A) return string.format ("%s = %s", A, Out) end } GateActions["string_select"] = { name = "Select", inputs = { "Choice", "A", "B", "C", "D", "E", "F", "G", "H" }, inputtypes = { "NORMAL", "STRING", "STRING", "STRING", "STRING", "STRING", "STRING", "STRING", "STRING" }, outputtypes = { "STRING" }, output = function(gate, Choice, ...) return ({...})[math.Clamp(Choice,1,8)] end, label = function(Out, Choice) return string.format ("select(%s) = %s", Choice, Out) end } GateActions["string_to_memory"] = { name = "String => Memory", inputs = { "A" }, inputtypes = { "STRING" }, outputs = { "Memory" }, reset = function(gate) gate.stringQueued = false gate.stringChanged = false gate.currentString = "" end, output = function(gate, A) if (A ~= gate.currentString) then if (not gate.stringChanged) then gate.stringChanged = true gate.currentString = A gate.stringQueued = false else gate.stringQueued = true end end return gate.Outputs["Memory"].Value --This will prevent Wire_TriggerOutput from changing anything end, ReadCell = function(self, gate, Address) if (Address == 0) then --Clk if (gate.stringChanged) then return 1 else return 0 end elseif (Address == 1) then --String length return #(gate.currentString) else --Return string bytes local index = Address - 1 if (index > #(gate.currentString)) then -- Check whether requested address is outside the string return 0 else return string.byte(gate.currentString, index) end end end, WriteCell = function(self, gate, Address, value) if (Address == 0) and (value == 0) then --String got accepted gate.stringChanged = false if gate.stringQueued then --Get queued string gate.stringQueued = false gate.currentString = gate.Inputs["A"].Value gate.stringChanged = true end return true else return false end end } GateActions["string_from_memory"] = { name = "Memory => String", inputs = {}, outputs = { "Out", "Memory" }, outputtypes = { "STRING", "NORMAL" }, reset = function(gate) --initialize the gate gate.memory = {} gate.stringLength = 0 gate.currentString = "" gate.ready = true end, output = function(gate) return gate.currentString, gate.Outputs["Memory"].Value end, ReadCell = function(self, gate, address) if (address == 0) then return 0 elseif (address == 1) then return gate.stringLength else return gate.memory[address-1] or 0 -- "or 0" to prevent it from returning nil if index is outside the array end end, WriteCell = function(self, gate, address, value) if (value >= 0) then if (address == 0) and (value == 1) then -- Clk has been set local maxIndex = gate.stringLength for i=1,gate.stringLength,1 do if not gate.memory[i] then maxIndex = i-1 break end end gate.currentString = string.char(unpack(gate.memory, 1, maxIndex)) gate:CalcOutput() return true elseif (address == 1) then -- Set string length gate.stringLength = math.floor(value) return true elseif (address > 1) then -- Set memory cell gate.memory[address-1] = math.floor(value) return true end end return false; -- if (value < 0) or ((address == 0) and (value != 1)) end } GateActions()