Created
July 26, 2012 19:22
-
-
Save alakra/3183982 to your computer and use it in GitHub Desktop.
S-Expression compiler for Ladder Logic (version 1)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# luby.rb -- | |
# | |
# This file translates S-Expressions as a mark-up language into ladder | |
# logic which can be used by the Ladder Logic Editor by Sylva Control Systems. | |
# | |
# Copyright (c) 2006 RODI Systems, Inc. | |
# | |
# See the file "LICENSE" for more information on usage and redistrution of | |
# this file and for a DISCLAIMER OF ALL WARRANTIES. | |
# Get Standard Libs | |
require 'yaml' | |
require 'pp' | |
# Init Vars | |
unless ARGV.empty? || ARGV[3].nil? | |
$template = YAML.load(File.open(ARGV[0])) | |
$title = ARGV[1] | |
$inputfile = ARGV[2] | |
$outputfile = ARGV[3] | |
end | |
$rungcount = 1 | |
$mathcount = 0 | |
module Ladder | |
attr_accessor :rungs, :basic, :datastructure | |
attr_reader :linenumber, :type_tracker, :mode_tracker, :par_tracker | |
def lexical_analysis(sourcefile) #returns tokenized array | |
@linenumber = 0 | |
@par_tracker = 0 | |
lexicality = Array.new | |
f = File.open(sourcefile) | |
#Tokenize every item in source code | |
f.each { |line| | |
@linenumber = @linenumber.succ | |
line.gsub(/\(|\)|rung|input|output|comment|xih|xil|ost|ftt|wequal!|wequal|wgtequal|wltequal|wlt|wgt|branch|inline|math\b|timerdone|ltdone|ctdone|wcopy|filter|ote|flipflop|otl|otu|dly|dlx|rsttdone|rsttimer|settdone|write_lcd|count|rst_count|rstcdone|setcdone|incword|decword|ros|eol|sub|ret|(\$|@)(\w+(\[\d+\]\[\w+\]|\[\d+\]|\[\w+\])|\w+)|&(\w+)|\^(\w+)|~(\w+)|'.+'|%(\w+)|\-\d+|\d+|basic|line|mathtracker/) { |result| | |
paratracker(result) | |
if (result == "rung" and @par_tracker != 1) or ((result == "basic" and @par_tracker != 1)) | |
#Throw error | |
print "Error: Parantheses stack does not equal zero! Check line number #{@linenumber} of #{sourcefile} and make sure that you have used the correct number of parantheses." | |
exit | |
end | |
lexicality.push(result) | |
} | |
} | |
#Return array of tokenize code | |
return lexicality | |
end | |
def syntax_analysis(tokenarray) #returns organized data structure in array/hash | |
@currentrung = nil | |
@currentbasic = nil | |
@par_tracker_store = 0 | |
@currentbranch = Array.new | |
@branchedrung = Array.new | |
oldrung = nil | |
oldbasic = nil | |
@rungs = Array.new | |
@basic = Array.new | |
tokenarray.each { |token| | |
#change paratracker later to check for actual parenthetical closings vs. dimensions of method calls | |
paratracker(token) | |
if @par_tracker == 0 | |
@rungs.push(@currentrung) if [email protected]? && @currentrung != oldrung | |
@basic.push(@currentbasic) if [email protected]? && @currentbasic != oldbasic | |
oldrung = @currentrung | |
oldbasic = @currentbasic | |
elsif @par_tracker == 1 | |
# Determine if processing rungs or basic and create new array or hash | |
if token == 'rung' | |
@currentrung = Hash.new | |
@type_tracker = :rung | |
elsif token == 'basic' | |
@currentbasic = Array.new | |
@type_tracker = :basic | |
end | |
elsif @par_tracker == 2 | |
# Create rung structures | |
if @type_tracker == :rung | |
if token == 'input' | |
@mode_tracker = :input | |
@currentrung.update(:input => Array.new) | |
elsif token == 'output' | |
@mode_tracker = :output | |
@currentrung.update(:output => Array.new) | |
elsif token == 'comment' | |
@mode_tracker = :comment | |
@currentrung.update(:comment => String.new) | |
elsif token =~ /'.+'/ | |
@currentrung[:comment] = token | |
end | |
# Create BASIC structures | |
elsif @type_tracker == :basic | |
if token == 'line' | |
@mode_tracker = :line | |
@currentbasic.push(:line => String.new) | |
elsif token == 'comment' | |
@mode_tracker = :comment | |
@currentbasic.push(:comment => String.new) | |
elsif token == 'mathtracker' | |
@mode_tracker = :mathtracker | |
@currentbasic.push(:mathtracker => String.new) | |
elsif token =~ /'.+'|\$\w+|&\w+|\^\w+|~\w+/ | |
@currentbasic[-1][@mode_tracker] = token | |
end | |
end | |
elsif @par_tracker == 3 | |
if @type_tracker == :rung | |
if @mode_tracker == :input | |
if token =~ /xih|xil|ost|ftt|wequal!|wequal|wgtequal|wltequal|wlt|wgt|branch|inline|timerdone|ltdone|ctdone/ | |
@currentrung[:input].push({token => Array.new}) | |
@lastfunc = token | |
@currentbranch = @currentrung[:input] | |
elsif token == '(' or token == ')' | |
#skip paranthesis | |
else | |
@currentrung[:input][-1][@lastfunc].push(token) | |
end | |
elsif @mode_tracker == :output | |
#need to fix regular expression, it catches variables with the same keywords | |
if token =~ /math|wcopy|filter|ote|flipflop|otl|otu|dlx|dly|rsttdone|rsttimer|settdone|write_lcd|count|rst_count|rstcdone|setcdone|incword|decword|ros|eol|sub|new|ret/ | |
@currentrung[:output].push({token => Array.new}) | |
@lastfunc = token | |
elsif token == '(' or token == ')' | |
#skip paranthesis | |
else | |
@currentrung[:output][-1][@lastfunc].push(token) | |
end | |
end | |
end | |
elsif @par_tracker > 3 | |
if @type_tracker == :rung && @mode_tracker == :input | |
whichbranch = String.new | |
#figure out the locations of inlines and branchs in datastructure | |
if token =~ /inline/ | |
@inlinebranch = @par_tracker | |
(@par_tracker-3).times { whichbranch << "[-1][\"branch\"]" } | |
elsif token =~ /branch/ | |
(@par_tracker-3).times { whichbranch << "[-1][\"branch\"]" } | |
else | |
if @inlinebranch == nil | |
(@par_tracker-3).times { whichbranch << "[-1][\"branch\"]" } | |
else | |
if @inlinebranch-1 == @par_tracker | |
@inlinebranch = nil | |
else | |
if @par_tracker == 4 | |
(@par_tracker-3).times { whichbranch << "[-1][\"branch\"]" } | |
else | |
(@par_tracker-4).times {whichbranch << "[-1][\"branch\"]"} | |
whichbranch << "[-1][\"inline\"]" | |
end | |
end | |
end | |
end | |
# add branches, inlines, and elements into datastructure | |
if token =~ /xih|xil|ost|ftt|wequal!|wequal|wgtequal|wltequal|wlt|wgt|timerdone|ltdone|ctdone/ | |
eval("@currentbranch" + whichbranch + ".push({token => Array.new})") | |
@lasttoken = token | |
elsif token == '(' or token == ')' | |
#skip paranthesis | |
elsif token =~ /branch|inline/ | |
eval("@currentbranch" + whichbranch + ".push({token => Array.new})") | |
else | |
eval("@currentbranch" + whichbranch + "[-1][@lasttoken].push(token)") | |
end | |
@par_tracker_store = @par_tracker | |
else | |
print "It looks like your parantheses have gotten out of control in your math, rung output, or rung comment routines. Please check them to verify that you have numbered them correctly." | |
exit | |
end | |
end | |
} | |
Hash[ :rungs => @rungs, :basic => @basic] | |
end | |
def generate_ladder(thename,datastructure) #returns basic and rungs | |
@math_database = Hash.new | |
@ost_ftt_database = Hash.new | |
@subroutine_database = Hash.new | |
@onetimer_database = Hash.new | |
@timerenable_database = Hash.new | |
@counter_database = Hash.new | |
@currenttimerenablebit = @template['bits']['onesectimer_enable_bits'].first | |
@lasttimerenablebit = @template['bits']['onesectimer_enable_bits'].last | |
@currentonetimer = @template['words']['onetimers'].first | |
@lastonetimer = @template['words']['onetimers'].last | |
@current_counter = @template['counters'].first | |
@last_counter = @template['counters'].last | |
@currentsub = 1 | |
@currentmath = @template['basic'].first | |
@lastmath = @template['basic'].last | |
@currentost_ftt = @template['bits']['ftt_bits_real'].first | |
@lastost_ftt = @template['bits']['ftt_bits_real'].last | |
@cell = 0 | |
@level = 0 | |
@currentwidth=0 | |
@column = Array.new | |
mathtable = Array.new | |
functypes = Array.new | |
generation = String.new | |
#Put CPU Name at beginning of file | |
generation << template['name'] + "\n" | |
# Go through each type | |
datastructure[:rungs].each { |indi_rung| | |
#Place rung number | |
generation << thename + "\n" | |
generation << $rungcount.to_s + "\n" | |
#do some regex to place #{} placeholders in right place | |
indi_rung[:comment].gsub!(/'/,'') | |
#Grab constants from template, user-defined table, settings table, and previously assigned variables table | |
indi_rung[:comment].gsub!(/(\$)\w+|@(\w+(\[\d+\]\[\w+\]|\[\w+\])|\w+)/) {|stringVar| stringVar = pickwordvalue([stringVar])[0] } | |
indi_rung[:comment].gsub!(/(\$)\w+|@(\w+(\[\d+\]\[\w+\]|\[\w+\])|\w+)/) {|stringVar| stringVar = pickbitvalue(stringVar) } | |
indi_rung[:comment].gsub!(/%(\w+)/) {|stringVar| stringVar = vartracker(:subroutine_database, :currentsub, stringVar) } | |
generation << indi_rung[:comment].gsub(/\{\d+\}/) { |stringVar| CONSTANTS[stringVar.gsub!(/\{|\}/,'').to_i].to_s } | |
generation << "\n" | |
#grab input rung elements and call methods based on their callsign | |
rungArray = Array.new | |
if indi_rung[:input].nil? | |
@numofcells = 0 | |
else | |
indi_rung[:input].each { |instruction| | |
if instruction.has_key?("branch") | |
@currentcolumn = 0 | |
@finishitoff = 0 | |
@level = 0 | |
@maxbranchwidth = 0 | |
@branchstruct = Array.new | |
branch_analysis(instruction) | |
rungArray.concat(@branchstruct) | |
else | |
#when there are no branches | |
instruction.each { |key, value| | |
if key =~ /xih|xil|ftt|ost|timerdone|ltdone|ctdone/ | |
rungArray.push(eval(key+"(value)")) | |
else | |
eval(key+"(value)").each {|eacharr| rungArray.push(eacharr)} | |
end | |
} | |
end | |
} | |
#spit out everything into generation | |
rungArray.each {|eachitem| | |
eachitem.each {|eachelement| | |
generation << eachelement + "\n" #switch to using Array.join to save on memory | |
} | |
(24-eachitem.length).times { generation << "\n"} | |
} | |
@numofcells = rungArray.length | |
end | |
if @numofcells < 12 | |
blankcells = 10 - @numofcells | |
generation << "\n" | |
blankcells.times { generation << "-------"; 24.times { generation << "\n"}} | |
else | |
print "Error: Your luby code has too many input elements on a single rung, make sure you have 10 elements or less on your rungs." | |
exit | |
end | |
#grab output rung elements and call methods based on their callsign | |
@numofcells = 0 | |
@outputsource = String.new | |
indi_rung[:output].each { |instruction| | |
instruction.each { |key, value| | |
cellsource = eval(key + '(value)') | |
@numofcells = @numofcells + cellsource[1] | |
@outputsource << cellsource[0] | |
} | |
} | |
if @numofcells == 1 | |
generation << "-------\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n" | |
generation << @outputsource | |
generation << "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n" | |
elsif @numofcells > 1 && @numofcells <= 6 | |
generation << "---+---\n" | |
(@numofcells-1).times { generation << " |\n |\n |\n x---\n" } | |
slashnfix = 22 - ((@numofcells-1)*4) | |
slashnfix.times { generation << "\n" } | |
generation << @outputsource | |
(slashnfix-2).times { generation << "\n" } | |
else | |
print "Error: Your luby code has too many output elements on a single rung, make sure you have 6 elements or less on your rungs. You have #{@numofcells}.\n" | |
exit | |
end | |
$rungcount = $rungcount.succ | |
} | |
#Go and see if there is basic and put it out! | |
if !datastructure[:basic].nil? | |
datastructure[:basic].each { |indi_basic| | |
premathgeneration = String.new | |
indi_basic.each { |indexer| | |
indexer.each { |key, value| | |
if key.to_s =~ /line|comment/ | |
premathgeneration << eval(key.to_s + '(value)') | |
elsif key.to_s =~ /mathtracker/ | |
@mathgenerationindex = eval(key.to_s + '(value)') | |
else | |
print "Error: Basic command '#{key}' not recognized. Check your code and verify that it is using correct syntax.\n" | |
exit | |
end | |
} | |
} | |
mathtable[@mathgenerationindex] = premathgeneration unless @mathgenerationindex.nil? | |
} | |
end | |
#Add EOL Tag | |
generation << "*****END OF LADDER*****\n" | |
#Add Basic | |
mathCounter = 1 | |
mathtable.each_with_index {|mathprogs,index| generation << mathprogs + "\n*****END OF BASIC" + (1+index).to_s + "*****\n" unless mathprogs.nil?} | |
#Add remaining maths that are not used | |
if @currentmath < @lastmath | |
(@lastmath - @currentmath + 1).times {|i| i = i + @currentmath ; generation << "\n*****END OF BASIC" + (i+1).to_s + "*****\n" } | |
end | |
#Add PID Config Data | |
8.times { | |
11.times { generation << "0\n"} | |
generation << "4095\n" | |
4.times { generation << "0\n"} | |
generation << "1\n" | |
generation << "0\n" | |
} | |
generation << "*****END OF PID*****\n" | |
#Add Messages | |
generation << " USER COMPANY NAME\n" | |
generation << "--------------------\n" | |
generation << " USER PROGRAM INFO\n" | |
generation << " USER PROGRAM INFO\n" | |
320.times { generation << "\n"} | |
generation << "*****END OF MESSAGES*****\n" | |
#Bit Table | |
2048.times { generation << "\n" } | |
generation << "*****END OF BIT TABLE*****\n" | |
#Word Table | |
4096.times { generation << "\n" } | |
generation << "*****END OF WORD TABLE*****\n" | |
#Text Screens | |
128.times { generation << "\n" } | |
generation << "*****END OF TEXT SCREENS*****\n" | |
#Setpoints | |
1152.times { generation << "\n" } | |
generation << "*****END OF SETPOINTS*****\n" | |
#Modbus | |
generation << "SLAVE\n" | |
224.times { generation << "\n" } | |
generation << "*****END OF MODBUS*****\n" | |
#Calibration | |
576.times { generation << "\n" } | |
generation << "*****END OF CALIBRATION*****\n" | |
#I2C | |
112.times { generation << "\n" } | |
generation << "*****END OF I2C*****\n\n\n\n\n" | |
return generation | |
end | |
private | |
def branch_analysis(branch) | |
if @level == 0 | |
@branchstruct[@currentcolumn] = ["","---+---"] | |
end | |
hasbud = false | |
hasbranch = false | |
branch["branch"].each{|keysinbranch| keysinbranch.each_key {|eachkey| | |
if eachkey =~ /xih|xil|ftt|ost|wequal!|wequal|wgtequal|wltequal|wlt|wgt|timerdone|ltdone|ctdone/ | |
hasbud = true | |
end | |
if eachkey =~ /branch/ | |
hasbranch = true | |
end | |
} | |
} | |
branch["branch"].each {|twig| | |
if twig.has_key?("inline") | |
x = 0 | |
twig["inline"].each {|bud| | |
bud.each {|leafkey,leafvalue| | |
if leafkey =~ /xih|xil|ftt|ost|timerdone|ltdone|ctdone/ | |
x = x.succ | |
@branchstruct[@currentcolumn+x] = Array.new if @branchstruct[@currentcolumn+x].nil? | |
@branchstruct[@currentcolumn+x].concat(eval(leafkey+"(leafvalue)")) | |
else | |
x = x + 2 | |
elementarr = eval(leafkey + "(leafvalue)") | |
@branchstruct[@currentcolumn+x-1] = Array.new if @branchstruct[@currentcolumn+x-1].nil? | |
@branchstruct[@currentcolumn+x] = Array.new if @branchstruct[@currentcolumn+x].nil? | |
@branchstruct[@currentcolumn+x-1].concat(elementarr[0]) | |
@branchstruct[@currentcolumn+x].concat(elementarr[1]) | |
end | |
@maxbranchwidth = x if x > @maxbranchwidth | |
@currentbranchwidth = x | |
} | |
} | |
end | |
["xih","xil","ftt","ost","wequal","wequal!","wgtequal","wltequal","wlt","wgt"].each {|bud| | |
if twig.has_key?(bud) | |
if hasbranch == true | |
if @level != 0 | |
@branchstruct[@currentcolumn] = Array.new if @branchstruct[@currentcolumn].nil? | |
@branchstruct[@currentcolumn].concat(["---+---"," |"," |"," |"," +---"]) | |
else | |
@branchstruct[@currentcolumn] = Array.new if @branchstruct[@currentcolumn].nil? | |
@branchstruct[@currentcolumn].concat([" |"," |"," |"," +---"]) | |
end | |
end | |
twig.each {|leafkey,leafvalue| | |
if leafkey =~ /xih|xil|ftt|ost|timerdone|ltdone|ctdone/ | |
@currentcolumn = @currentcolumn.succ | |
@branchstruct[@currentcolumn] = Array.new if @branchstruct[@currentcolumn].nil? | |
@branchstruct[@currentcolumn].concat(eval(leafkey+"(leafvalue)")) | |
elsif leafkey =~ /wequal!|wequal|wgtequal|wltequal|wlt|wgt/ | |
elementarr = eval(leafkey + "(leafvalue)") | |
@currentcolumn = @currentcolumn + 2 | |
@branchstruct[@currentcolumn-1] = Array.new if @branchstruct[@currentcolumn+x-1].nil? | |
@branchstruct[@currentcolumn] = Array.new if @branchstruct[@currentcolumn+x].nil? | |
@branchstruct[@currentcolumn-1].concat(elementarr[0]) | |
@branchstruct[@currentcolumn].concat(elementarr[1]) | |
end | |
@maxbranchwidth = @currentcolumn if @currentcolumn > @maxbranchwidth | |
@currentbranchwidth = @currentcolumn | |
} | |
end | |
} | |
} | |
branch["branch"].each { |eachbranch| | |
if eachbranch.has_key?("branch") | |
if hasbud == true && hasbranch == true | |
@currentcolumn = @currentcolumn.succ | |
@finishitoff = @finishitoff.succ | |
@branchstruct[@currentcolumn] = Array.new if @branchstruct[@currentcolumn+1].nil? | |
@branchstruct[@currentcolumn].concat(["","---+---"," |"," |"," |"," +---"]) | |
elsif hasbud == true && hasbranch == false | |
@branchstruct[@currentcolumn] = Array.new if @branchstruct[@currentcolumn].nil? | |
@branchstruct[@currentcolumn].concat([" |"," |"," |"," +---"]) | |
elsif hasbud == false && hasbranch == true | |
@branchstruct[@currentcolumn] = Array.new if @branchstruct[@currentcolumn].nil? | |
@branchstruct[@currentcolumn].concat([" |"," |"," |"," +---"]) | |
end | |
@level = @level.succ | |
branch_analysis(eachbranch) | |
end | |
} | |
nomorebranch = false | |
branch["branch"].each { |nobranch| | |
if nobranch.include?("branch") | |
nomorebranch = true | |
end | |
} | |
if nomorebranch == false | |
@branchstruct[@currentcolumn+1] = Array.new if @branchstruct[@currentcolumn+1].nil? | |
@branchstruct[@currentcolumn+1].concat(["","---+---"]) | |
(@level).times {@branchstruct[@currentcolumn+1].concat([" |"," |"," |","---+"])} | |
if @finishitoff != 0 | |
@branchstruct[@maxbranchwidth+1] = Array.new if @branchstruct[@maxbranchwidth+1].nil? | |
@branchstruct[@maxbranchwidth+1].concat(["","---+---"]) | |
(@finishitoff).times { @branchstruct[@maxbranchwidth+1].concat([" |"," |"," |","---+"])} | |
end | |
end | |
end | |
# Figures out branch divisions | |
# --------------------------------- | |
def elementCaller(key,value) | |
if key == "branch" | |
value.each { |arrayElement| arrayElement.each_key { |anotherkey, anothervalue| | |
if anotherkey == "branch" | |
@numofbranches = @numofbranches.succ | |
elementCaller(anotherkey, anothervalue) | |
elsif anotherkey == "inline" | |
#do nothing right now | |
else | |
@numofelements = @numofelements.succ | |
end | |
} | |
} | |
elsif key == "inline" | |
else | |
cellsource = eval(key + '(value)') | |
end | |
return cellsource | |
end | |
# Memory Tracking | |
# ----------------------------------- | |
def paratracker(item) | |
if item == '(' | |
@par_tracker = @par_tracker.succ | |
elsif item == ')' | |
@par_tracker = @par_tracker - 1 | |
end | |
end | |
# Handles all variable tracking table | |
# --------------------------------------- | |
def vartracker(database, pointer, value) | |
eval("@"+pointer.to_s + "=" + "updatepointer(database,pointer)") | |
if eval("@" + database.to_s).has_key?(value) | |
result = eval("@" + database.to_s)[value] | |
else | |
eval("@" + database.to_s).update({value => eval("@"+pointer.to_s)}) | |
result = eval("@" + pointer.to_s) | |
end | |
return result | |
end | |
# Update pointer correctly for given table | |
# ------------------------------------------- | |
def updatepointer(db,pointertracker) | |
unless eval("@"+db.to_s).has_value?(eval("@"+pointertracker.to_s)) | |
newop = eval("@" + pointertracker.to_s) | |
else | |
eval("@" + pointertracker.to_s + " = @"+pointertracker.to_s + "+1") | |
updatepointer(db,pointertracker) | |
newop = eval("@"+pointertracker.to_s) | |
end | |
return newop | |
end | |
# Added for compliance with luby syntax for basic references | |
# ---------------------------------------------------------- | |
def mathtracker(amath) | |
vartracker(:math_database, :currentmath, amath) | |
end | |
# Look for available words and assign them | |
# ---------------------------------------- | |
def pickwordvalue(pickarr) | |
constant = false | |
newwordarr= pickarr.collect {|varname| | |
if varname =~ /\$\w+/ | |
varname = vartracker(:freewords_database, :currentfreewords, varname) | |
elsif varname =~ /@(\w+(\[\d+\]\[\w+\]|\[\w+\])|\w+)/ | |
if varname =~ /@\w+(\[\d+\]\[\w+\]|\[\w+\])/ | |
if varname =~ /@\w+(\[\d+\]\[\w+\])/ | |
newname = varname.match(/\[\D+\]/).to_s | |
correctindex = varname.match(/\[\d+\]/).to_s | |
containername = varname.match(/\w+\[/).to_s | |
varname = vartracker("#{containername[0..-2]}_database[#{correctindex[1..-2]}]", eval("\'current" + containername[0..-2] + "[#{correctindex[1..-2]}]\'"), newname[1..-2]) | |
else | |
newname = varname.match(/\[\D+\]/).to_s | |
containername = varname.match(/\w+\[/).to_s | |
varname = vartracker(eval(":" + containername[0..-2] +"_database"), eval(":current"+containername[0..-2]), newname[1..-2]) | |
end | |
else | |
varname = @template['words'][varname[1..-1]] | |
end | |
else | |
constant = true | |
end | |
} | |
return [newwordarr,constant].flatten | |
end | |
# Look for available words and assign them | |
# ---------------------------------------------- | |
def pickbitvalue(pickbit) | |
pickbit = pickbit[0] #convert from array to string | |
#check for a constant or a variable | |
if pickbit =~ /\$\w+/ | |
bit = vartracker(:freebits_database, :currentfreebits, pickbit[1..-1]) | |
elsif pickbit =~ /@(\w+(\[\w+\])|\w+)/ | |
bit = @template['bits'][pickbit[1..-1]] | |
else | |
bit = pickbit | |
end | |
return [bit] | |
end | |
def comment(ref) | |
#Check for a word reference from template | |
ref.gsub!(/(\$)\w+|@(\w+(\[\d+\]\[\w+\]|\[\w+\])|\w+)/) {|stringVar| stringVar = pickwordvalue([stringVar]) unless pickwordvalue([stringVar]).nil?} | |
#Check for a bit reference from template | |
ref.gsub!(/(\$)\w+|@(\w+(\[\d+\]\[\w+\]|\[\w+\])|\w+)/) {|stringVar| stringVar = pickbitvalue(stringVar) unless pickbitvalue(stringVar).nil?} | |
#Replace custom references variables with user references | |
return "REM " + ref[1..-2].gsub(/\{\d+\}/) { |stringVar| CONSTANTS[stringVar.gsub!(/\{|\}/,'').to_i].to_s } + "\n" | |
end | |
def line(lineref) | |
#Replace all word variable references with real word locations from template | |
lineref.gsub!(/\((\$|@)\w+(\[\d+\]\[\w+\]|\[\w+\]|)/) {|found| | |
if found =~ /\((\$)\w+\[pre\]/ | |
found = "(" + ((vartracker(:onetimer_database, :currentonetimer, found[1..-6]) - 65)*2 + @template['words']['onesecondtimers'].first + 1).to_s | |
elsif found =~ /\((\$)\w+\[acc\]/ | |
found = "(" + ((vartracker(:onetimer_database, :currentonetimer, found[1..-6]) - 65)*2 + @template['words']['onesecondtimers'].first).to_s | |
else | |
found = "(" + pickwordvalue([found[1..-1]])[0].to_s | |
end | |
} | |
#Replace all bit variable references with real bit locations | |
lineref.gsub!(/(([x|X][i|I]([h|H]|[l|L]))|([o|O][t|T]([u|U]|[l|L])))\s(@|\$|&|\^|~)(\d+|(\w+(\[\w+\])|\w+))/){|found| | |
#For words and bits | |
found.gsub!(/(@|\$|&|\^|~)(\w+|\d+)(\w+|\d+)/) { |justthenumber| | |
if justthenumber =~ /@|\$/ | |
templatebit = pickbitvalue([justthenumber])[0] | |
basicbitformat = String.new | |
(4-templatebit.to_s.length).times {basicbitformat << "0"} | |
justthenumber = basicbitformat + templatebit.to_s | |
elsif justthenumber =~ /&/ | |
justthenumber = '$' + justthenumber[1..-1] | |
associatedtimer = vartracker(:onetimer_database, :currentonetimer, justthenumber) | |
templatebit = @template['bits']['timer_done_bits'].first + associatedtimer.to_i | |
basicbitformat = String.new | |
(4-templatebit.to_s.length).times {basicbitformat << "0"} | |
justthenumber = basicbitformat + templatebit.to_s | |
elsif justthenumber =~ /~/ | |
justthenumber = '$' + justthenumber[1..-1] | |
associatedcounter = vartracker(:counter_database, :current_counter, justthenumber) | |
templatebit = @template['bits']['counter_done_bits'].first + associatedcounter.to_i | |
basicbitformat = String.new | |
(4-templatebit.to_s.length).times {basicbitformat << "0"} | |
justthenumber = basicbitformat + templatebit.to_s | |
elsif justthenumber =~ /\^/ | |
justthenumber = '$' + justthenumber[1..-1] | |
assignedost = vartracker(:ost_ftt_database, :currentost_ftt, [justthenumber]) | |
templatebit = @template['bits']['ftt_bits_raw'].first + assignedost.to_i | |
basicbitformat = String.new | |
(4-templatebit.to_s.length).times {basicbitformat << "0"} | |
justthenumber = basicbitformat + templatebit.to_s | |
else | |
justthenumber = "0000" | |
end | |
} | |
} | |
#Return line with surrounding quotes removed and newline char | |
return lineref[1..-2] + "\n" | |
end | |
def xih(thebitvar) | |
if thebitvar[0].to_i.zero? | |
assignedbit = pickbitvalue(thebitvar) | |
else | |
assignedbit = thebitvar[0] | |
end | |
return ["B:#{assignedbit}","--] [--",thebitvar.to_s[1..-1],""] | |
end | |
def xil(thebitvar) | |
if thebitvar[0].to_i.zero? | |
assignedbit = pickbitvalue(thebitvar) | |
else | |
assignedbit = thebitvar[0] | |
end | |
return ["B:#{assignedbit}","--]/[--",thebitvar.to_s[1..-1],""] | |
end | |
def ost(ostval) | |
assignedost = vartracker(:ost_ftt_database, :currentost_ftt, ostval) | |
return ["O:#{assignedost}","-[OST]-","",""] | |
end | |
def ftt(fttval) | |
assignedftt = vartracker(:ost_ftt_database, :currentost_ftt, fttval) | |
return ["O:#{assignedfttt}","-[FTT]-","",""] | |
end | |
def wequal(equalarr) | |
word,compared,constant =* pickwordvalue(equalarr) | |
arr1 = ["W:#{word}","--[W]--",equalarr[0].to_s[1..-1],""] | |
if constant == true | |
if equalarr[1].length > 5 | |
arr2 = ["K#{equalarr[1]}","--[=]--","",""] | |
else | |
arr2 = ["K:#{equalarr[1]}","--[=]--","",""] | |
end | |
else | |
arr2 = ["W:#{compared}","--[=]--","#{equalarr[1][1..-1]}",""] | |
end | |
return [arr1,arr2] | |
end | |
def wgt(equalarr) | |
word,compared,constant =* pickwordvalue(equalarr) | |
arr1 = ["W:#{word}","--[W]--",equalarr[0].to_s[1..-1],""] | |
if constant == true | |
if equalarr[1].length > 5 | |
arr2 = ["K#{equalarr[1]}","--[>]--","",""] | |
else | |
arr2 = ["K:#{equalarr[1]}","--[>]--","",""] | |
end | |
else | |
arr2 = ["W:#{compared}","--[>]--","#{equalarr[1][1..-1]}",""] | |
end | |
return [arr1,arr2] | |
end | |
def wgtequal(equalarr) | |
word,compared,constant =* pickwordvalue(equalarr) | |
arr1 = ["W:#{word}","--[W]--",equalarr[0].to_s[1..-1],""] | |
if constant == true | |
if equalarr[1].length > 5 | |
arr2 = ["K#{equalarr[1]}","-[> =]-","",""] | |
else | |
arr2 = ["K:#{equalarr[1]}","-[> =]-","",""] | |
end | |
else | |
arr2 = ["W:#{compared}","-[> =]-","#{equalarr[1][1..-1]}",""] | |
end | |
return [arr1,arr2] | |
end | |
def wlt(equalarr) | |
word,compared,constant =* pickwordvalue(equalarr) | |
arr1 = ["W:#{word}","--[W]--",equalarr[0].to_s[1..-1],""] | |
if constant == true | |
if equalarr[1].length > 5 | |
arr2 = ["K#{equalarr[1]}","--[<]--","",""] | |
else | |
arr2 = ["K:#{equalarr[1]}","--[<]--","",""] | |
end | |
else | |
arr2 = ["W:#{compared}","--[<]--","#{equalarr[1][1..-1]}",""] | |
end | |
return [arr1,arr2] | |
end | |
def wltequal(equalarr) | |
word,compared,constant =* pickwordvalue(equalarr) | |
arr1 = ["W:#{word}","--[W]--",equalarr[0].to_s[1..-1],""] | |
if constant == true | |
if equalarr[1].length > 5 | |
arr2 = ["K#{equalarr[1]}","-[< =]-","",""] | |
else | |
arr2 = ["K:#{equalarr[1]}","-[< =]-","",""] | |
end | |
else | |
arr2 = ["W:#{compared}","-[< =]-","#{equalarr[1][1..-1]}",""] | |
end | |
return [arr1,arr2] | |
end | |
def wequal!(equalarr) | |
word,compared,constant =* pickwordvalue(equalarr) | |
arr1 = ["W:#{word}","--[W]--",equalarr[0].to_s[1..-1],""] | |
if constant == true | |
if equalarr[1].length > 5 | |
arr2 = ["K#{equalarr[1]}","-[< >]-","",""] | |
else | |
arr2 = ["K:#{equalarr[1]}","-[< >]-","",""] | |
end | |
else | |
arr2 = ["W:#{compared}","-[< >]-","#{equalarr[1][1..-1]}",""] | |
end | |
return [arr1,arr2] | |
end | |
def timerdone(donebit) | |
associatedtimer = vartracker(:onetimer_database, :currentonetimer, donebit[0]) | |
timerdonebit = @template['bits']['timer_done_bits'].first + associatedtimer.to_i | |
return ["B:#{timerdonebit}","--] [--",donebit[0].to_s[1..-1],""] | |
end | |
def ltdone(donebit) | |
associatedtimer = vartracker(:onetimer_database, :currentonetimer, donebit[0]) | |
timerdonebit = @template['bits']['timer_done_bits'].first + associatedtimer.to_i | |
return ["B:#{timerdonebit}","--]/[--",donebit[0].to_s[1..-1],""] | |
end | |
def ctdone(donebit) | |
associatedcounter = vartracker(:counter_database, :current_counter, donebit[0]) | |
counterdonebit = @template['bits']['counter_done_bits'].first + associatedcounter.to_i | |
return ["B:#{counterdonebit}","--] [--",donebit[0].to_s[1..-1],""] | |
end | |
def math(mref) | |
mathref = vartracker(:math_database, :currentmath, mref.to_s) | |
resultant = "P:#{mathref}\n" | |
resultant << "-(BAS)-\n" | |
resultant << "MATH PROG ##{mathref}\n\n" | |
return [resultant,1] | |
end | |
def wcopy(arr) | |
if arr[0] =~ /\A\d/ #check this to make sure it's correct | |
arr.reverse! | |
end | |
originator,to_word,constant =* pickwordvalue(arr) | |
if constant == true | |
resultant = "K:#{arr[1]}\n" | |
resultant << "-(CPY)-\n" | |
resultant << arr[1].to_s + "\n\n" | |
resultant << "W:#{originator}\n" | |
resultant << "-(DST)-\n" | |
resultant << arr[0].to_s[1..-1] + "\n\n" | |
else | |
resultant = "W:#{originator}\n" | |
resultant << "-(CPY)-\n" | |
resultant << arr[0].to_s[1..-1] + "\n\n" | |
resultant << "W:#{to_word}\n" | |
resultant << "-(DST)-\n" | |
resultant << arr[1].to_s[1..-1] + "\n\n" | |
end | |
return [resultant,2] | |
end | |
def filter(filtersource) | |
chosenfilter = filtersource[0] | |
wordsource = pickwordvalue(filtersource[1]) | |
resultant = "F:#{chosenfilter}\n" | |
resultant << "-(DSP)-\n" | |
resultant << "\n\n" | |
resultant << "W:#{wordsource[0]}\n" | |
resultant << "-(SRV)-\n" | |
resultant << filtersource.to_s[1..-1] + "\n\n" | |
return [resultant,2] | |
end | |
def ote(otebit) | |
if otebit[0].to_i.zero? | |
assignedote = pickbitvalue(otebit) | |
else | |
assignedote = otebit[0].to_i | |
end | |
resultant = "B:#{assignedote}\n" | |
resultant << "-(OTE)-\n" | |
resultant << "\n\n" | |
return [resultant,1] | |
end | |
def flipflop(ffbit) | |
if ffbit[0].to_i.zero? | |
assignedff = pickbitvalue(ffbit) | |
else | |
assignedff = ffbit[0].to_i | |
end | |
resultant = "B:#{assignedff}\n" | |
resultant << "-(FFP)-\n" | |
resultant << "\n\n" | |
return [resultant,1] | |
end | |
def otl(latchedbit) | |
if latchedbit[0].to_i.zero? | |
assignedotl = pickbitvalue(latchedbit) | |
else | |
assignedotl = latchedbit[0].to_i | |
end | |
resultant = "B:#{assignedotl}\n" | |
resultant << "-(OTL)-\n" | |
resultant << "\n\n" | |
return [resultant,1] | |
end | |
def otu(unlatchedbit) | |
if unlatchedbit[0].to_i.zero? | |
assignedotu = pickbitvalue(unlatchedbit) | |
else | |
assignedotu = unlatchedbit[0].to_i | |
end | |
resultant = "B:#{assignedotu}\n" | |
resultant << "-(OTU)-\n" | |
resultant << "\n\n" | |
return [resultant,1] | |
end | |
def dly(arr) | |
if arr[0].to_i.zero? | |
chosentimer = vartracker(:onetimer_database, :currentonetimer, arr[0]) | |
resultant = "T:#{chosentimer}\n" | |
resultant << "-(DLY)-\n" | |
resultant << "\n\n" | |
else | |
resultant = "T:#{arr[0]}\n" | |
resultant << "-(DLY)-\n" | |
resultant << "\n\n" | |
end | |
if arr[1].to_i.zero? | |
wordsource = pickwordvalue(arr[1]) | |
resultant << "V:#{wordsource[0]}\n" | |
resultant << "-(SRV)-\n" | |
resultant << arr[1].to_s[1..-1] + "\n\n" | |
else | |
resultant << "K:#{arr[1]}\n" | |
resultant << "-(SRK)-\n" | |
resultant << "\n\n" | |
end | |
return [resultant,2] | |
end | |
def dlx(arr) | |
if arr[0] =~ /^\$/ | |
chosentimer = vartracker(:onetimer_database, :currentonetimer, arr[0]) | |
resultant = "T:#{chosentimer}\n" | |
resultant << "-(DLX)-\n" | |
resultant << "\n\n" | |
else | |
resultant = "T:#{arr[0]}\n" | |
resultant << "-(DLX)-\n" | |
resultant << "\n\n" | |
end | |
if arr[1] =~ /^\$|^@/ | |
wordsource = pickwordvalue(arr[1]) | |
resultant << "V:#{wordsource[0]}\n" | |
resultant << "-(SRV)-\n" | |
resultant << arr[1].to_s[1..-1] + "\n\n" | |
else | |
resultant << "K:#{arr[1]}\n" | |
resultant << "-(SRK)-\n" | |
resultant << "\n\n" | |
end | |
return [resultant,2] | |
end | |
def rsttimer(arr) | |
if arr[0].to_i.zero? | |
chosentimer = vartracker(:onetimer_database, :currentonetimer, arr[0]) | |
else | |
chosentimer = arr[0] | |
end | |
resultant = "T:#{chosentimer}\n" | |
resultant << "-(RST)-\n" | |
resultant << "\n\n" | |
return [resultant,1] | |
end | |
def rsttdone(arr) | |
if arr[0].to_i.zero? | |
chosentimer = vartracker(:onetimer_database, :currentonetimer, arr[0]) | |
else | |
chosentimer = arr[0] | |
end | |
timerdonebit = @template['bits']['timer_done_bits'].first + chosentimer.to_i | |
resultant = "B:#{timerdonebit}\n" | |
resultant << "-(OTU)-\n" | |
resultant << "\n\n" | |
return [resultant,1] | |
end | |
def settdone(arr) | |
if arr[0].to_i.zero? | |
chosentimer = vartracker(:onetimer_database, :currentonetimer, arr[0]) | |
else | |
chosentimer = arr[0] | |
end | |
timerdonebit = @template['bits']['timer_done_bits'].first + chosentimer.to_i | |
resultant = "B:#{timerdonebit}\n" | |
resultant << "-(OTL)-\n" | |
resultant << "\n\n" | |
return [resultant,1] | |
end | |
def write_lcd | |
end | |
def count(arr) | |
chosencounter = vartracker(:counter_database, :current_counter, arr[0]) | |
resultant = "C:#{chosencounter}\n" | |
resultant << "-(CTU)-\n" | |
resultant << "\n\n" | |
if arr[1].to_i.zero? | |
wordsource = pickwordvalue(arr[1]) | |
resultant << "V:#{wordsource[0]}\n" | |
resultant << "-(SRV)-\n" | |
resultant << arr[1].to_s[1..-1] + "\n\n" | |
else | |
resultant << "K:#{arr[1]}\n" | |
resultant << "-(SRK)-\n" | |
resultant << "\n\n" | |
end | |
return [resultant,2] | |
end | |
def rst_count(arr) | |
chosencounter = vartracker(:counter_database, :current_counter, arr[0]) | |
resultant = "C:#{chosencounter}\n" | |
resultant << "-(CTR)-\n" | |
resultant << "\n\n" | |
return [resultant,1] | |
end | |
def rstcdone(arr) | |
chosencounter = vartracker(:counter_database, :current_counter, arr[0]) | |
counterdonebit = @template['bits']['counter_done_bits'].first + chosencounter.to_i | |
resultant = "B:#{counterdonebit}\n" | |
resultant << "-(OTU)-\n" | |
resultant << "\n\n" | |
return [resultant,1] | |
end | |
def setcdone(arr) | |
chosencounter = vartracker(:counter_database, :current_counter, arr[0]) | |
counterdonebit = @template['bits']['counter_done_bits'].first + chosencounter.to_i | |
resultant = "B:#{counterdonebit}\n" | |
resultant << "-(OTL)-\n" | |
resultant << "\n\n" | |
return [resultant,1] | |
end | |
def incword(arr) | |
wordsource = pickwordvalue(arr[0]) | |
resultant = "W:#{wordsource[0]}\n" | |
resultant << "-(INC)-\n" | |
resultant << arr[0].to_s[1..-1] + "\n\n" | |
return [resultant,1] | |
end | |
def decword(arr) | |
wordsource = pickwordvalue(arr[0]) | |
resultant = "W:#{wordsource[0]}\n" | |
resultant << "-(DEC)-\n" | |
resultant << arr[0].to_s[1..-1] + "\n\n" | |
return [resultant,1] | |
end | |
def ros(ostval) | |
assignedost = vartracker(:ost_ftt_database, :currentost_ftt, ostval) | |
resultant = "O:#{assignedost}\n" | |
resultant << "-(ROS)-\n" | |
resultant << "\n\n" | |
return [resultant,1] | |
end | |
def eol(nada) | |
resultant = "\n" | |
resultant << "-(EOL)-\n" | |
resultant << "\n\n" | |
return [resultant,1] | |
end | |
def sub(thecall) | |
realsub = vartracker(:subroutine_database, :currentsub, thecall[0]) | |
resultant = "S:#{realsub}\n" | |
resultant << "-(->>)-\n" | |
resultant << "\n\n" | |
return [resultant,1] | |
end | |
def ret(nada) | |
resultant = "\n" | |
resultant << "-(<--)-\n" | |
resultant << "\n\n" | |
return [resultant,1] | |
end | |
end | |
#Place Ladder Module into a class for Real Use | |
class Luby | |
attr_accessor :template | |
include Ladder | |
def initialize(tp,controllername,input,output,thereserves) | |
@template = YAML.load(File.open(tp)) | |
generate_containers | |
callinthereserves(thereserves) unless thereserves.nil? | |
ldr = File.new(output,'w') | |
ldr.write(generate_ladder(controllername,syntax_analysis(lexical_analysis(input)))) | |
ldr.close | |
datatable = File.new("jointbytes.yaml","w") | |
datatable.write @jointbytes_database.sort.to_yaml | |
datatable.close | |
datatable = File.new("amuletbytes.yaml","w") | |
datatable.write @amuletbytes_database[0].sort.to_yaml | |
datatable.close | |
datatable = File.new("amuletwords.yaml","w") | |
datatable.write @amuletwords_database[0].sort.to_yaml | |
datatable.close | |
datatable = File.new("freewords.yaml","w") | |
datatable.write @freewords_database.sort.to_yaml | |
datatable.close | |
end | |
def generate_containers | |
$containers.each_with_index {|group,index| | |
if @template[group.to_s].class == Array.new.class | |
eval("@"+group.to_s+"_database = Array.new") | |
eval("@current" + group.to_s + " = Array.new") | |
eval("@last" + group.to_s + " = Array.new") | |
@template[group.to_s].each_with_index {|range,anotherindex| | |
eval("@" + group.to_s + "_database.push(Hash.new)") | |
eval("@current" + group.to_s + "[" + anotherindex.to_s + "] = @template[\'" + group.to_s + "\'][" + anotherindex.to_s + "].first") | |
eval("@last" + group.to_s + "[" + anotherindex.to_s + "] = @template[\'" + group.to_s + "\'][" + anotherindex.to_s + "].last") | |
} | |
else | |
eval("@"+group.to_s+"_database = Hash.new") | |
eval("@current" + group.to_s + " = @template[\'" + group.to_s + "\'].first") | |
eval("@last" + group.to_s + " = @template[\'" + group.to_s + "\'].last") | |
end | |
} | |
end | |
def callinthereserves(reserves) | |
putReservedIntoTables(YAML.load(File.open(reserves))) | |
end | |
def putReservedIntoTables(reserved) | |
reserved.each_pair {|key,value| | |
unless value.nil? | |
if value.class == Hash.new.class | |
@savekey = key | |
putReservedIntoTables(value) | |
else | |
eval("@"+@savekey+".update(\""+ key + "\"=>" + value.to_s + ")") | |
end | |
end | |
} | |
end | |
def bytes | |
@bytes = @amuletbytes_database | |
end | |
def words | |
@words = @amuletwords_database | |
end | |
def jointbytes | |
@jointbytes = @jointbytes_database | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment