Skip to content

Instantly share code, notes, and snippets.

@alakra
Created July 26, 2012 19:22
Show Gist options
  • Save alakra/3183982 to your computer and use it in GitHub Desktop.
Save alakra/3183982 to your computer and use it in GitHub Desktop.
S-Expression compiler for Ladder Logic (version 1)
# 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 !@currentrung.nil? && @currentrung != oldrung
@basic.push(@currentbasic) if !@currentbasic.nil? && @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