#!/usr/bin/ruby # code generated by "translate.ruby.pss" a pep script # http://bumble.sf.net/books/pars/tr/ # require 'something' class Machine # make a new machine attr_accessor :work, :charsRead, :linesRead, :escape, :delimiter, :counter, :stack, :tape, :cell, :marks, :eof, :peep def initialize() @size = 300 # how many elements in stack/tape/marks @eof = false # end of stream reached? @charsRead = 0 # how many chars already read @linesRead = 1 # how many lines already read @escape = "\\" @delimiter = "*" # push/pop delimiter (default "*") @counter = 0 # a counter for anything @work = "" # the workspace @stack = [] # stack for parse tokens @cell = 0 # current tape cell @tape = Array.new(@size) {String.new} # a list of attribute for tokens @marks = Array.new(@size) {String.new} # marked tape cells # or dont initialse peep until "parse()" calls "setInput()" @peep = ARGF.readchar end # multiline strings are ok in ruby def printSizeError() puts " Tape max size exceeded! tape maximum size = #{@size} tape cell (current) = #{@cell} You can increase the @size value in the ruby script but normally this error indicates an error in your parsing script. The only exception would be massively nested structures in the source data." end def setInput(newInput) puts "to be implemented" end # read one character from the input stream and # update the machine. def read if @eof then exit end @charsRead += 1; # increment lines if @peep == "\n" then @linesRead += 1 end @work += @peep @peep = ARGF.readchar if @peep.nil? then @eof = true; end end # test if all chars in workspace are in unicode category def isInCategory(cat) #for ch in @work # if not category(ch).start_with?(cat) then return false end #return True end # this needs to actually walk the string # eg "abcab\cabc" # not trivial def unescapeChar(c) @work.gsub!("#{@escape}#{c}", c) end # add escape character : trivial? def escapeChar(c) @work.gsub!(c, @escape+c) end # a helper for the multiescape until bug def countEscaped(suffix) count = 0 #s = @work.sub(/#{suffix}$/, "") s = @work.delete_suffix(suffix) while s.end_with?(@escape) count += 1 s.delete_suffix!(@escape) end # puts "count=#{count}" return count end # reads the input stream until the workspace end with text def until(suffix) # read at least one character if @eof then return end self.read() while true do if @eof then return end # need to count the @escape chars preceding suffix # if odd, keep reading, if even, stop if @work.end_with?(suffix) then if (self.countEscaped(suffix).even?) then return end end self.read() end end # pop the first token from the stack into the workspace */ def pop() if @stack.length == 0 then return false end @work = @stack.pop() + @work if @cell > 0 then @cell -= 1 end return true end # push the first token from the workspace to the stack def push() # dont increment the tape pointer on an empty push if @work == "" then return false end # need to get this from the delimiter. iFirst = @work.index(@delimiter) if iFirst.nil? @stack.push(@work); @work = ""; return true # also @stack << @work end # s[i..j] means all chars from i to j # s[i,n] means n chars from i @stack.push(@work[0..iFirst]) @work = @work[iFirst+1..-1] if @cell < @size then @cell += 1 else self.printSizeError(); exit end return true end def printState() puts "Stack[#{@stack.join(', ')}] Work[#{@work}] Peep[#{@peep}]" puts "Acc:#{@counter} Esc:#{@escape} Delim:#{@delimiter} Chars:#{@charsRead}" + " Lines:#{@linesRead} Cell:#{@cell}" end # this is where the actual parsing/compiling code should go # so that it can be used by other ruby classes/objects. Also # should have a stream argument. def parse(s) # a reset or "setinput()" method would be useful to parse a # different string/file/stream, without creating a new # machine object. # could use code like this to check if input is string or file #if isinstance(s, file) print("") # @reset(s) # @reader = s #elseif isinstance(s, string) #f = StringIO.StringIO("test") #for line in f print(line) #else # f = STDIN #end #puts "not implemented" end end # end of Machine class definition # will become: # mm.parse(sys.stdin) or # mm.parse("abcdef") or # open f; mm.parse(f) # the restart flag, which allows .restart to work before the # parse label, in languages (like ruby) that dont have # labelled loops restart = false mm = Machine.new while !mm.eof do # lex block while true mm.read() # read #-------------- if (mm.work.match?(/^[[:space:]]+$/)) then mm.work = '' # clear break end #--------------- # We can ellide all these single character tests, because # the stack token is just the character itself with a * # Braces {} are used for blocks of commands, ',' and '.' for concatenating # tests with OR or AND logic. 'B' and 'E' for begin and end # tests, '!' is used for negation, ';' is used to terminate a # command. if (mm.work == "{" || mm.work == "}" || mm.work == ";" || mm.work == "," || mm.work == "." || mm.work == "!" || mm.work == "B" || mm.work == "E") then mm.tape[mm.cell] = mm.work # put mm.work += "*" mm.push(); break end #--------------- # format: "text" if (mm.work == "\"") then # save the start line number (for error messages) in case # there is no terminating quote character. mm.work = '' # clear mm.work += "line " mm.work += mm.linesRead.to_s # lines mm.work += " (character " mm.work += mm.charsRead.to_s # chars mm.work += ") " mm.tape[mm.cell] = mm.work # put mm.work = '' # clear mm.work += "\"" mm.until("\""); if (!mm.work.end_with?("\"")) then mm.work = '' # clear mm.work += "Unterminated quote character (\") starting at " mm.work += mm.tape[mm.cell] # get mm.work += " !\n" print mm.work # print exit end # check for empty quotes as arguments for escape etc # replace if mm.work.length > 0 then mm.work.gsub!("\#{", "\\\#{") end mm.tape[mm.cell] = mm.work # put mm.work = '' # clear mm.work += "quote*" mm.push(); break end #--------------- # format: 'text', single quotes are converted to double quotes # but we must escape embedded double quotes. if (mm.work == "'") then # save the start line number (for error messages) in case # there is no terminating quote character. mm.work = '' # clear mm.work += "line " mm.work += mm.linesRead.to_s # lines mm.work += " (character " mm.work += mm.charsRead.to_s # chars mm.work += ") " mm.tape[mm.cell] = mm.work # put mm.work = '' # clear mm.until("'"); if (!mm.work.end_with?("'")) then mm.work = '' # clear mm.work += "Unterminated quote (') starting at " mm.work += mm.tape[mm.cell] # get mm.work += "!\n" print mm.work # print exit end if !mm.work.empty? then # clip mm.work = mm.work[0..-2] # clip end mm.work.gsub!("\"", "#{mm.escape}\"") # escape # #{ does string interpolation in ruby which is not # what we want. Also unescape \' # replace if mm.work.length > 0 then mm.work.gsub!("\#{", "\\\#{") end mm.work.gsub!(mm.escape+"'", "'") # escape mm.tape[mm.cell] = mm.work # put mm.work = '' # clear mm.work += "\"" mm.work += mm.tape[mm.cell] # get mm.work += "\"" mm.tape[mm.cell] = mm.work # put mm.work = '' # clear mm.work += "quote*" mm.push(); break end #--------------- # formats: [:space:] [a-z] [abcd] [:alpha:] etc # should class tests really be multiline??! if (mm.work == "[") then # save the start line number (for error messages) in case # there is no terminating bracket character. mm.work = '' # clear mm.work += "line " mm.work += mm.linesRead.to_s # lines mm.work += " (character " mm.work += mm.charsRead.to_s # chars mm.work += ") " mm.tape[mm.cell] = mm.work # put mm.work = '' # clear mm.work += "[" mm.until("]"); if (mm.work == "[]") then mm.work = '' # clear mm.work += "pep script error at line " mm.work += mm.linesRead.to_s # lines mm.work += " (character " mm.work += mm.charsRead.to_s # chars mm.work += "): \n" mm.work += " empty character class [] \n" print mm.work # print exit end if (!mm.work.end_with?("]")) then mm.work = '' # clear mm.work += "Unterminated class text ([...]) starting at " mm.work += mm.tape[mm.cell] # get mm.work += "" mm.work += "\n class text can be used in tests or with the 'while' and " mm.work += "\n 'whilenot' commands. For example: " mm.work += "\n [:alpha:] { while [:alpha:]; print; clear; }" mm.work += "\n " print mm.work # print exit end # need to escape quotes? ruby uses /.../ to match mm.work.gsub!("\"", "#{mm.escape}\"") # escape # the caret is not a negation operator in pep scripts # replace if mm.work.length > 0 then mm.work.gsub!("^", "\\^") end # save the class on the tape mm.tape[mm.cell] = mm.work # put if !mm.work.empty? then # clop mm.work = mm.work[1..-1]; # clop end if !mm.work.empty? then # clop mm.work = mm.work[1..-1]; # clop end if (!mm.work.start_with?("-")) then # not a range class, eg [a-z] so need to escape '-' chars mm.work = '' # clear mm.work += mm.tape[mm.cell] # get # replace if mm.work.length > 0 then mm.work.gsub!("-", "\\-") end mm.tape[mm.cell] = mm.work # put end if (mm.work.start_with?("-")) then # a range class, eg [a-z], check if it is correct if !mm.work.empty? then # clip mm.work = mm.work[0..-2] # clip end if !mm.work.empty? then # clip mm.work = mm.work[0..-2] # clip end if (mm.work != "-") then mm.work = '' # clear mm.work += "Error in pep script at line " mm.work += mm.linesRead.to_s # lines mm.work += " (character " mm.work += mm.charsRead.to_s # chars mm.work += "): \n" mm.work += " Incorrect character range class " mm.work += mm.tape[mm.cell] # get mm.work += "" mm.work += "\n For example:" mm.work += "\n [a-g] # correct" mm.work += "\n [f-gh] # error! \n" print mm.work # print mm.work = '' # clear exit end end mm.work = '' # clear mm.work += mm.tape[mm.cell] # get # restore class text if (mm.work.start_with?("[:") && !mm.work.end_with?(":]")) then mm.work = '' # clear mm.work += "malformed character class starting at " mm.work += mm.tape[mm.cell] # get mm.work += "!\n" print mm.work # print exit end # class in the form [:digit:] if (mm.work.start_with?("[:") && mm.work != "[:]") then if !mm.work.empty? then # clip mm.work = mm.work[0..-2] # clip end if !mm.work.empty? then # clip mm.work = mm.work[0..-2] # clip end if !mm.work.empty? then # clop mm.work = mm.work[1..-1]; # clop end if !mm.work.empty? then # clop mm.work = mm.work[1..-1]; # clop end # unicode posix character classes # Also, abbreviations (not implemented in gh.c yet.) # this should not be tricky in ruby because posix is supported if (mm.work == "alnum" || mm.work == "N") then mm.work = '' # clear mm.work += "[[:alnum:]]" end if (mm.work == "alpha" || mm.work == "A") then mm.work = '' # clear mm.work += "[[:alpha:]]" end # ? can use s.ascii_only?() if (mm.work == "ascii" || mm.work == "I") then mm.work = '' # clear mm.work += "[[:ascii:]]" end # non-standard ruby posix class 'word' if (mm.work == "word" || mm.work == "W") then mm.work = '' # clear mm.work += "[[:word:]]" end if (mm.work == "blank" || mm.work == "B") then mm.work = '' # clear mm.work += "[[:blank:]]" end if (mm.work == "cntrl" || mm.work == "C") then mm.work = '' # clear mm.work += "[[:cntrl:]]" end if (mm.work == "digit" || mm.work == "D") then mm.work = '' # clear mm.work += "[[:digit:]]" end if (mm.work == "graph" || mm.work == "G") then mm.work = '' # clear mm.work += "[[:graph:]]" end if (mm.work == "lower" || mm.work == "L") then mm.work = '' # clear mm.work += "[[:lower:]]" end if (mm.work == "print" || mm.work == "P") then mm.work = '' # clear mm.work += "[[:print:]]" end if (mm.work == "punct" || mm.work == "T") then mm.work = '' # clear mm.work += "[[:punct:]]" end if (mm.work == "space" || mm.work == "S") then mm.work = '' # clear mm.work += "[[:space:]]" end if (mm.work == "upper" || mm.work == "U") then mm.work = '' # clear mm.work += "[[:upper:]]" end if (mm.work == "xdigit" || mm.work == "X") then mm.work = '' # clear mm.work += "[[:xdigit:]]" end if (!mm.work.start_with?("[[")) then mm.tape[mm.cell] = mm.work # put mm.work = '' # clear mm.work += "pep script error at line " mm.work += mm.linesRead.to_s # lines mm.work += " (character " mm.work += mm.charsRead.to_s # chars mm.work += "): \n" mm.work += "Unknown character class '" mm.work += mm.tape[mm.cell] # get mm.work += "'\n" print mm.work # print mm.work = '' # clear exit end end mm.tape[mm.cell] = mm.work # put mm.work = '' # clear mm.work += "/^" mm.work += mm.tape[mm.cell] # get mm.work += "+$/" mm.tape[mm.cell] = mm.work # put mm.work = '' # clear # add quotes around the class and limits around the # class so it can be used with the string.matches() method # (must match the whole string, not just one character) mm.work += "class*" mm.push(); break end #--------------- # formats: (eof) (EOF) (==) etc. if (mm.work == "(") then mm.work = '' # clear mm.until(")"); if !mm.work.empty? then # clip mm.work = mm.work[0..-2] # clip end mm.tape[mm.cell] = mm.work # put if (mm.work == "eof" || mm.work == "EOF") then mm.work = '' # clear mm.work += "eof*" mm.push(); break end if (mm.work == "==") then mm.work = '' # clear mm.work += "tapetest*" mm.push(); break end mm.work += " << unknown test near line " mm.work += mm.linesRead.to_s # lines mm.work += " of script.\n" mm.work += " bracket () tests are \n" mm.work += " (eof) test if end of stream reached. \n" mm.work += " (==) test if workspace is same as current tape cell \n" print mm.work # print mm.work = '' # clear exit end #--------------- # multiline and single line comments, eg #... and #* ... *# if (mm.work == "#") then mm.work = '' # clear mm.read() # read if (mm.work == "\n") then mm.work = '' # clear break end # checking for multiline comments of the form "#* \n\n\n *#" # these are just ignored at the moment (deleted) if (mm.work == "*") then # save the line number for possible error message later mm.work = '' # clear mm.work += mm.linesRead.to_s # lines mm.tape[mm.cell] = mm.work # put mm.work = '' # clear mm.until("*#"); if (mm.work.end_with?("*#")) then # convert to python comments (#), python doesnt have multiline # comments, as far as I know if !mm.work.empty? then # clip mm.work = mm.work[0..-2] # clip end if !mm.work.empty? then # clip mm.work = mm.work[0..-2] # clip end # replace if mm.work.length > 0 then mm.work.gsub!("\n", "\n#") end mm.tape[mm.cell] = mm.work # put mm.work = '' # clear # create a "comment" parse token # comment-out this line to remove multiline comments from the # compiled python # add "comment*"; push; break end # make an unterminated multiline comment an error # to ease debugging of scripts. mm.work = '' # clear mm.work += "unterminated multiline comment #* ... *# \n" mm.work += "stating at line number " mm.work += mm.tape[mm.cell] # get mm.work += "\n" print mm.work # print mm.work = '' # clear exit end # single line comments. some will get lost. mm.tape[mm.cell] = mm.work # put mm.work = '' # clear mm.work += "#" mm.work += mm.tape[mm.cell] # get mm.until("\n"); if !mm.work.empty? then # clip mm.work = mm.work[0..-2] # clip end mm.tape[mm.cell] = mm.work # put mm.work = '' # clear # comment out this below to remove single line comments # from the output mm.work += "comment*" mm.push(); break end #---------------------------------- # parse command words (and abbreviations) # legal characters for keywords (commands) if (!mm.work.match?(/^[abcdefghijklmnopqrstuvwxyzBEKGPRUWS+\-<>0\^]+$/)) then # error message about a misplaced character mm.tape[mm.cell] = mm.work # put mm.work = '' # clear mm.work += "!! Misplaced character '" mm.work += mm.tape[mm.cell] # get mm.work += "' in script near line " mm.work += mm.linesRead.to_s # lines mm.work += " (character " mm.work += mm.charsRead.to_s # chars mm.work += ") \n" print mm.work # print mm.work = '' # clear exit end # my testclass implementation cannot handle complex lists # eg [a-z+-] this is why I have to write out the whole alphabet # while while /^[abcdefghijklmnopqrstuvwxyzBEOFKGPRUWS+\-<>0\^]+$/.match?(mm.peep) if mm.eof then break end mm.read() end #---------------------------------- # KEYWORDS # here we can test for all the keywords (command words) and their # abbreviated one letter versions (eg: clip k, clop K etc). Then # we can print an error message and abort if the word is not a # legal keyword for the parse-edit language # make ll an alias for "lines" and cc an alias for chars if (mm.work == "ll") then mm.work = '' # clear mm.work += "lines" end if (mm.work == "cc") then mm.work = '' # clear mm.work += "chars" end # one letter command abbreviations if (mm.work == "a") then mm.work = '' # clear mm.work += "add" end if (mm.work == "k") then mm.work = '' # clear mm.work += "clip" end if (mm.work == "K") then mm.work = '' # clear mm.work += "clop" end if (mm.work == "D") then mm.work = '' # clear mm.work += "replace" end if (mm.work == "d") then mm.work = '' # clear mm.work += "clear" end if (mm.work == "t") then mm.work = '' # clear mm.work += "print" end if (mm.work == "p") then mm.work = '' # clear mm.work += "pop" end if (mm.work == "P") then mm.work = '' # clear mm.work += "push" end if (mm.work == "u") then mm.work = '' # clear mm.work += "unstack" end if (mm.work == "U") then mm.work = '' # clear mm.work += "stack" end if (mm.work == "G") then mm.work = '' # clear mm.work += "put" end if (mm.work == "g") then mm.work = '' # clear mm.work += "get" end if (mm.work == "x") then mm.work = '' # clear mm.work += "swap" end if (mm.work == ">") then mm.work = '' # clear mm.work += "++" end if (mm.work == "<") then mm.work = '' # clear mm.work += "--" end if (mm.work == "m") then mm.work = '' # clear mm.work += "mark" end if (mm.work == "M") then mm.work = '' # clear mm.work += "go" end if (mm.work == "r") then mm.work = '' # clear mm.work += "read" end if (mm.work == "R") then mm.work = '' # clear mm.work += "until" end if (mm.work == "w") then mm.work = '' # clear mm.work += "while" end if (mm.work == "W") then mm.work = '' # clear mm.work += "whilenot" end if (mm.work == "n") then mm.work = '' # clear mm.work += "count" end if (mm.work == "+") then mm.work = '' # clear mm.work += "a+" end if (mm.work == "-") then mm.work = '' # clear mm.work += "a-" end if (mm.work == "0") then mm.work = '' # clear mm.work += "zero" end if (mm.work == "c") then mm.work = '' # clear mm.work += "chars" end if (mm.work == "l") then mm.work = '' # clear mm.work += "lines" end if (mm.work == "^") then mm.work = '' # clear mm.work += "escape" end if (mm.work == "v") then mm.work = '' # clear mm.work += "unescape" end if (mm.work == "z") then mm.work = '' # clear mm.work += "delim" end if (mm.work == "S") then mm.work = '' # clear mm.work += "state" end if (mm.work == "q") then mm.work = '' # clear mm.work += "quit" end if (mm.work == "s") then mm.work = '' # clear mm.work += "write" end if (mm.work == "o") then mm.work = '' # clear mm.work += "nop" end if (mm.work == "rs") then mm.work = '' # clear mm.work += "restart" end if (mm.work == "rp") then mm.work = '' # clear mm.work += "reparse" end # some extra syntax for testeof and testtape if (mm.work == "" || mm.work == "") then mm.tape[mm.cell] = mm.work # put mm.work = '' # clear mm.work += "eof*" mm.push(); break end if (mm.work == "<==>") then mm.tape[mm.cell] = mm.work # put mm.work = '' # clear mm.work += "tapetest*" mm.push(); break end if (mm.work == "jump" || mm.work == "jumptrue" || mm.work == "jumpfalse" || mm.work == "testis" || mm.work == "testclass" || mm.work == "testbegins" || mm.work == "testends" || mm.work == "testeof" || mm.work == "testtape") then mm.tape[mm.cell] = mm.work # put mm.work = '' # clear mm.work += "The instruction '" mm.work += mm.tape[mm.cell] # get mm.work += "' near line " mm.work += mm.linesRead.to_s # lines mm.work += " (character " mm.work += mm.charsRead.to_s # chars mm.work += ")\n" mm.work += "can be used in pep assembly code but not scripts. \n" print mm.work # print mm.work = '' # clear exit end # show information if these "deprecated" commands are used if (mm.work == "Q" || mm.work == "bail") then mm.tape[mm.cell] = mm.work # put mm.work = '' # clear mm.work += "The instruction '" mm.work += mm.tape[mm.cell] # get mm.work += "' near line " mm.work += mm.linesRead.to_s # lines mm.work += " (character " mm.work += mm.charsRead.to_s # chars mm.work += ")\n" mm.work += "is no longer part of the pep language (july 2020). \n" mm.work += "use 'quit' instead of 'bail', and use 'unstack; print;' \n" mm.work += "instead of 'state'. \n" print mm.work # print mm.work = '' # clear exit end if (mm.work == "add" || mm.work == "clip" || mm.work == "clop" || mm.work == "replace" || mm.work == "upper" || mm.work == "lower" || mm.work == "cap" || mm.work == "clear" || mm.work == "print" || mm.work == "state" || mm.work == "pop" || mm.work == "push" || mm.work == "unstack" || mm.work == "stack" || mm.work == "put" || mm.work == "get" || mm.work == "swap" || mm.work == "++" || mm.work == "--" || mm.work == "mark" || mm.work == "go" || mm.work == "read" || mm.work == "until" || mm.work == "while" || mm.work == "whilenot" || mm.work == "count" || mm.work == "a+" || mm.work == "a-" || mm.work == "zero" || mm.work == "chars" || mm.work == "lines" || mm.work == "nochars" || mm.work == "nolines" || mm.work == "escape" || mm.work == "unescape" || mm.work == "delim" || mm.work == "quit" || mm.work == "write" || mm.work == "nop" || mm.work == "reparse" || mm.work == "restart") then mm.tape[mm.cell] = mm.work # put mm.work = '' # clear mm.work += "word*" mm.push(); break end #------------ # the .reparse command and "parse label" is a simple way to # make sure that all shift-reductions occur. It should be used inside # a block test, so as not to create an infinite loop. There is # no "goto" in java so we need to use labelled loops to # implement .reparse/parse> if (mm.work == "parse>") then mm.work = '' # clear mm.work += mm.counter.to_s # count if (mm.work != "0") then mm.work = '' # clear mm.work += "script error:\n" mm.work += " extra parse> label at line " mm.work += mm.linesRead.to_s # lines mm.work += ".\n" print mm.work # print exit end mm.work = '' # clear mm.work += "# parse> parse label" mm.tape[mm.cell] = mm.work # put mm.work = '' # clear mm.work += "parse>*" mm.push(); # use accumulator to indicate after parse> label mm.counter += 1 # a+ break end # -------------------- # implement "begin-blocks", which are only executed # once, at the beginning of the script (similar to awk's BEGIN {} rules) if (mm.work == "begin") then mm.tape[mm.cell] = mm.work # put mm.work += "*" mm.push(); break end mm.work += " << unknown command on line " mm.work += mm.linesRead.to_s # lines mm.work += " (char " mm.work += mm.charsRead.to_s # chars mm.work += ")" mm.work += " of source file. \n" print mm.work # print mm.work = '' # clear exit # ---------------------------------- # PARSING PHASE: # Below is the parse/compile phase of the script. Here we pop tokens off the # stack and check for sequences of tokens eg "word*semicolon*". If we find a # valid series of tokens, we "shift-reduce" or "resolve" the token series eg # word*semicolon* --> command* # At the same time, we manipulate (transform) the attributes on the tape, as # required. break end if restart then restart = false; next; end # parse block while true #------------------------------------- # 2 tokens #------------------------------------- mm.pop(); mm.pop(); # All of the patterns below are currently errors, but may not # be in the future if we expand the syntax of the parse # language. Also consider: # begintext* endtext* quoteset* notclass*, !* ,* ;* B* E* # It is nice to trap the errors here because we can emit some # (hopefully not very cryptic) error messages with a line number. # Otherwise the script writer has to debug with # pep -a asm.pp -I scriptfile if (mm.work == "word*word*" || mm.work == "word*}*" || mm.work == "word*begintext*" || mm.work == "word*endtext*" || mm.work == "word*!*" || mm.work == "word*,*" || mm.work == "quote*word*" || mm.work == "quote*class*" || mm.work == "quote*state*" || mm.work == "quote*}*" || mm.work == "quote*begintext*" || mm.work == "quote*endtext*" || mm.work == "class*word*" || mm.work == "class*quote*" || mm.work == "class*class*" || mm.work == "class*state*" || mm.work == "class*}*" || mm.work == "class*begintext*" || mm.work == "class*endtext*" || mm.work == "class*!*" || mm.work == "notclass*word*" || mm.work == "notclass*quote*" || mm.work == "notclass*class*" || mm.work == "notclass*state*" || mm.work == "notclass*}*") then mm.work += " (Token stack) \nValue: \n" mm.work += mm.tape[mm.cell] # get mm.work += "\nValue: \n" mm.cell += 1 # ++ mm.work += mm.tape[mm.cell] # get if mm.cell > 0 then mm.cell -= 1; end # -- mm.work += "\n" mm.work += "Error near line " mm.work += mm.linesRead.to_s # lines mm.work += " (char " mm.work += mm.charsRead.to_s # chars mm.work += ")" mm.work += " of pep script (missing semicolon?) \n" print mm.work # print mm.work = '' # clear exit end if (mm.work == "{*;*" || mm.work == ";*;*" || mm.work == "}*;*") then mm.push(); mm.push(); mm.work += "Error near line " mm.work += mm.linesRead.to_s # lines mm.work += " (char " mm.work += mm.charsRead.to_s # chars mm.work += ")" mm.work += " of pep script: misplaced semi-colon? ; \n" print mm.work # print mm.work = '' # clear exit end if (mm.work == ",*{*") then mm.push(); mm.push(); mm.work += "Error near line " mm.work += mm.linesRead.to_s # lines mm.work += " (char " mm.work += mm.charsRead.to_s # chars mm.work += ")" mm.work += " of script: extra comma in list? \n" print mm.work # print mm.work = '' # clear exit end if (mm.work == "command*;*" || mm.work == "commandset*;*") then mm.push(); mm.push(); mm.work += "Error near line " mm.work += mm.linesRead.to_s # lines mm.work += " (char " mm.work += mm.charsRead.to_s # chars mm.work += ")" mm.work += " of script: extra semi-colon? \n" print mm.work # print mm.work = '' # clear exit end if (mm.work == "!*!*") then mm.push(); mm.push(); mm.work += "error near line " mm.work += mm.linesRead.to_s # lines mm.work += " (char " mm.work += mm.charsRead.to_s # chars mm.work += ")" mm.work += " of script: \n double negation '!!' is not implemented \n" mm.work += " and probably won't be, because what would be the point? \n" print mm.work # print mm.work = '' # clear exit end if (mm.work == "!*{*" || mm.work == "!*;*") then mm.push(); mm.push(); mm.work += "error near line " mm.work += mm.linesRead.to_s # lines mm.work += " (char " mm.work += mm.charsRead.to_s # chars mm.work += ")" mm.work += " of script: misplaced negation operator (!)? \n" mm.work += " The negation operator precedes tests, for example: \n" mm.work += " !B'abc'{ ... } or !(eof),!'abc'{ ... } \n" print mm.work # print mm.work = '' # clear exit end if (mm.work == ",*command*") then mm.push(); mm.push(); mm.work += "error near line " mm.work += mm.linesRead.to_s # lines mm.work += " (char " mm.work += mm.charsRead.to_s # chars mm.work += ")" mm.work += " of script: misplaced comma? \n" print mm.work # print mm.work = '' # clear exit end if (mm.work == "!*command*") then mm.push(); mm.push(); mm.work += "error near line " mm.work += mm.linesRead.to_s # lines mm.work += " (at char " mm.work += mm.charsRead.to_s # chars mm.work += ") \n" mm.work += " The negation operator (!) cannot precede a command \n" print mm.work # print mm.work = '' # clear exit end if (mm.work == ";*{*" || mm.work == "command*{*" || mm.work == "commandset*{*") then mm.push(); mm.push(); mm.work += "error near line " mm.work += mm.linesRead.to_s # lines mm.work += " (char " mm.work += mm.charsRead.to_s # chars mm.work += ")" mm.work += " of script: no test for brace block? \n" print mm.work # print mm.work = '' # clear exit end if (mm.work == "{*}*") then mm.push(); mm.push(); mm.work += "error near line " mm.work += mm.linesRead.to_s # lines mm.work += " of script: empty braces {}. \n" print mm.work # print mm.work = '' # clear exit end if (mm.work == "B*class*" || mm.work == "E*class*") then mm.push(); mm.push(); mm.work += "error near line " mm.work += mm.linesRead.to_s # lines mm.work += " of script:\n classes ([a-z], [:space:] etc). \n" mm.work += " cannot use the 'begin' or 'end' modifiers (B/E) \n" print mm.work # print mm.work = '' # clear exit end if (mm.work == "comment*{*") then mm.push(); mm.push(); mm.work += "error near line " mm.work += mm.linesRead.to_s # lines mm.work += " of script: comments cannot occur between \n" mm.work += " a test and a brace ({). \n" print mm.work # print mm.work = '' # clear exit end if (mm.work == "}*command*") then mm.push(); mm.push(); mm.work += "error near line " mm.work += mm.linesRead.to_s # lines mm.work += " of script: extra closing brace '}' ?. \n" print mm.work # print mm.work = '' # clear exit end #------------ # The .restart command jumps to the first instruction after the # begin block (if there is a begin block), or the first instruction # of the script. if (mm.work == ".*word*") then mm.work = '' # clear mm.cell += 1 # ++ mm.work += mm.tape[mm.cell] # get if mm.cell > 0 then mm.cell -= 1; end # -- if (mm.work == "restart") then mm.work = '' # clear mm.work += mm.counter.to_s # count # this is the opposite of .reparse, using run-once loops # cant do next before label, infinite loop # need to set flag variable. There is a subtlety: .restart can # exist without parse> (although it would be unusual). # before (or without) the parse> label if (mm.work == "0") then mm.work = '' # clear # use the comment '# restart' so we can replace # this with 'break' if the parse> label appears later mm.work += "restart = true; next # restart" end if (mm.work == "1") then mm.work = '' # clear mm.work += "break" end # after the parse> label mm.tape[mm.cell] = mm.work # put mm.work = '' # clear mm.work += "command*" mm.push(); next end if (mm.work == "reparse") then mm.work = '' # clear mm.work += mm.counter.to_s # count # check accumulator to see if we are in the "lex" block # or the "parse" block and adjust the .reparse compilation # accordingly. if (mm.work == "0") then mm.work = '' # clear mm.work += "break" end if (mm.work == "1") then mm.work = '' # clear mm.work += "next" end mm.tape[mm.cell] = mm.work # put mm.work = '' # clear mm.work += "command*" mm.push(); next end mm.push(); mm.push(); mm.work += "error near line " mm.work += mm.linesRead.to_s # lines mm.work += " (char " mm.work += mm.charsRead.to_s # chars mm.work += ")" mm.work += " of script: \n" mm.work += " misplaced dot '.' (use for AND logic or in .reparse/.restart \n" print mm.work # print mm.work = '' # clear exit end #--------------------------------- # Compiling comments so as to transfer them to the java if (mm.work == "comment*command*" || mm.work == "command*comment*" || mm.work == "commandset*comment*") then mm.work = '' # clear mm.work += mm.tape[mm.cell] # get mm.work += "\n" mm.cell += 1 # ++ mm.work += mm.tape[mm.cell] # get if mm.cell > 0 then mm.cell -= 1; end # -- mm.tape[mm.cell] = mm.work # put mm.work = '' # clear mm.work += "command*" mm.push(); next end if (mm.work == "comment*comment*") then mm.work = '' # clear mm.work += mm.tape[mm.cell] # get mm.work += "\n" mm.cell += 1 # ++ mm.work += mm.tape[mm.cell] # get if mm.cell > 0 then mm.cell -= 1; end # -- mm.tape[mm.cell] = mm.work # put mm.work = '' # clear mm.work += "comment*" mm.push(); next end # ----------------------- # negated tokens. # This is a new more elegant way to negate a whole set of # tests (tokens) where the negation logic is stored on the # stack, not in the current tape cell. We just add "not" to # the stack token. # eg: ![:alpha:] ![a-z] ![abcd] !"abc" !B"abc" !E"xyz" # This format is used to indicate a negative test for # a brace block. eg: ![aeiou] { add "< not a vowel"; print; clear; } if (mm.work == "!*quote*" || mm.work == "!*class*" || mm.work == "!*begintext*" || mm.work == "!*endtext*" || mm.work == "!*eof*" || mm.work == "!*tapetest*") then # a simplification: store the token name "quote*/class*/..." # in the tape cell corresponding to the "!*" token. # replace if mm.work.length > 0 then mm.work.gsub!("!*", "not") end mm.push(); # this was a bug?? a missing ++; ?? # now get the token-value mm.work += mm.tape[mm.cell] # get if mm.cell > 0 then mm.cell -= 1; end # -- mm.tape[mm.cell] = mm.work # put mm.cell += 1 # ++ mm.work = '' # clear next end #----------------------------------------- # format: E"text" or E'text' # This format is used to indicate a "workspace-ends-with" text before # a brace block. if (mm.work == "E*quote*") then mm.work = '' # clear mm.work += "endtext*" mm.push(); mm.work += mm.tape[mm.cell] # get if (mm.work == "\"\"") then # empty argument is an error mm.work = '' # clear mm.work += "pep script error near line " mm.work += mm.linesRead.to_s # lines mm.work += " (character " mm.work += mm.charsRead.to_s # chars mm.work += "): \n" mm.work += " empty argument for end-test (E\"\") \n" print mm.work # print exit end if mm.cell > 0 then mm.cell -= 1; end # -- mm.tape[mm.cell] = mm.work # put mm.cell += 1 # ++ mm.work = '' # clear next end #----------------------------------------- # format: B"sometext" or B'sometext' # A 'B' preceding some quoted text is used to indicate a # 'workspace-begins-with' test, before a brace block. if (mm.work == "B*quote*") then mm.work = '' # clear mm.work += "begintext*" mm.push(); mm.work += mm.tape[mm.cell] # get if (mm.work == "\"\"") then # empty argument is an error mm.work = '' # clear mm.work += "pep script error near line " mm.work += mm.linesRead.to_s # lines mm.work += " (character " mm.work += mm.charsRead.to_s # chars mm.work += "): \n" mm.work += " empty argument for begin-test (B\"\") \n" print mm.work # print exit end if mm.cell > 0 then mm.cell -= 1; end # -- mm.tape[mm.cell] = mm.work # put mm.cell += 1 # ++ mm.work = '' # clear next end #-------------------------------------------- # ebnf: command := word, ';' ; # formats: "pop; push; clear; print; " etc # all commands need to end with a semi-colon except for # .reparse and .restart if (mm.work == "word*;*") then mm.work = '' # clear # check if command requires parameter mm.work += mm.tape[mm.cell] # get if (mm.work == "add" || mm.work == "until" || mm.work == "while" || mm.work == "whilenot" || mm.work == "mark" || mm.work == "go" || mm.work == "escape" || mm.work == "unescape" || mm.work == "delim" || mm.work == "replace") then mm.tape[mm.cell] = mm.work # put mm.work = '' # clear mm.work += "'" mm.work += mm.tape[mm.cell] # get mm.work += "'" mm.work += " << command needs an argument, on line " mm.work += mm.linesRead.to_s # lines mm.work += " of script.\n" print mm.work # print mm.work = '' # clear exit end if (mm.work == "clip") then mm.work = '' # clear mm.work += "if !mm.work.empty? then # clip \n" mm.work += " mm.work = mm.work[0..-2] # clip \n" mm.work += "end" mm.tape[mm.cell] = mm.work # put end if (mm.work == "clop") then mm.work = '' # clear mm.work += "if !mm.work.empty? then # clop \n" mm.work += " mm.work = mm.work[1..-1]; # clop \n" mm.work += "end" mm.tape[mm.cell] = mm.work # put end if (mm.work == "clear") then mm.work = '' # clear mm.work += "mm.work = '' # clear" mm.tape[mm.cell] = mm.work # put end if (mm.work == "upper") then mm.work = '' # clear mm.work += "mm.work.upcase! # upper" mm.tape[mm.cell] = mm.work # put end if (mm.work == "lower") then mm.work = '' # clear mm.work += "mm.work.downcase! # lower" mm.tape[mm.cell] = mm.work # put end if (mm.work == "cap") then mm.work = '' # clear mm.work += "mm.work.capitalize! # capital" mm.tape[mm.cell] = mm.work # put end if (mm.work == "print") then mm.work = '' # clear mm.work += "print mm.work # print" mm.tape[mm.cell] = mm.work # put end if (mm.work == "state") then mm.work = '' # clear mm.work += "mm.printState() # state" mm.tape[mm.cell] = mm.work # put end if (mm.work == "pop") then mm.work = '' # clear mm.work += "mm.pop();" mm.tape[mm.cell] = mm.work # put end if (mm.work == "push") then mm.work = '' # clear mm.work += "mm.push();" mm.tape[mm.cell] = mm.work # put end if (mm.work == "unstack") then mm.work = '' # clear mm.work += "while mm.pop() do next end # unstack " mm.tape[mm.cell] = mm.work # put end if (mm.work == "stack") then mm.work = '' # clear mm.work += "while mm.push() do next end # stack " mm.tape[mm.cell] = mm.work # put end if (mm.work == "put") then mm.work = '' # clear mm.work += "mm.tape[mm.cell] = mm.work # put " mm.tape[mm.cell] = mm.work # put end if (mm.work == "get") then mm.work = '' # clear mm.work += "mm.work += mm.tape[mm.cell] # get" mm.tape[mm.cell] = mm.work # put end if (mm.work == "swap") then mm.work = '' # clear mm.work += "mm.work, mm.tape[mm.cell] = mm.tape[mm.cell], mm.work # swap " mm.tape[mm.cell] = mm.work # put end if (mm.work == "++") then mm.work = '' # clear mm.work += "mm.cell += 1 # ++" mm.tape[mm.cell] = mm.work # put end if (mm.work == "--") then mm.work = '' # clear mm.work += "if mm.cell > 0 then mm.cell -= 1; end # --" mm.tape[mm.cell] = mm.work # put end if (mm.work == "read") then mm.work = '' # clear mm.work += "mm.read() # read" mm.tape[mm.cell] = mm.work # put end if (mm.work == "count") then mm.work = '' # clear mm.work += "mm.work += mm.counter.to_s # count " mm.tape[mm.cell] = mm.work # put end if (mm.work == "a+") then mm.work = '' # clear mm.work += "mm.counter += 1 # a+ " mm.tape[mm.cell] = mm.work # put end if (mm.work == "a-") then mm.work = '' # clear mm.work += "mm.counter -= 1 # a- " mm.tape[mm.cell] = mm.work # put end if (mm.work == "zero") then mm.work = '' # clear mm.work += "mm.counter = 0 # zero " mm.tape[mm.cell] = mm.work # put end if (mm.work == "chars") then mm.work = '' # clear mm.work += "mm.work += mm.charsRead.to_s # chars " mm.tape[mm.cell] = mm.work # put end if (mm.work == "lines") then mm.work = '' # clear mm.work += "mm.work += mm.linesRead.to_s # lines " mm.tape[mm.cell] = mm.work # put end if (mm.work == "nochars") then mm.work = '' # clear mm.work += "mm.charsRead = 0 # nochars " mm.tape[mm.cell] = mm.work # put end if (mm.work == "nolines") then mm.work = '' # clear mm.work += "mm.linesRead = 0 # nolines " mm.tape[mm.cell] = mm.work # put end # use a labelled loop to quit script. if (mm.work == "quit") then mm.work = '' # clear mm.work += "exit" mm.tape[mm.cell] = mm.work # put end # inline this? if (mm.work == "write") then mm.work = '' # clear mm.work += "File.write('sav.pp', mm.work)" mm.tape[mm.cell] = mm.work # put end # File.open("sav.pp", 'w') { |file| file.write("your text") } # convert to "pass" which does nothing. if (mm.work == "nop") then mm.work = '' # clear mm.work += "# nop: no-operation" mm.tape[mm.cell] = mm.work # put end mm.work = '' # clear mm.work += "command*" mm.push(); next end #----------------------------------------- # ebnf: commandset := command , command ; if (mm.work == "command*command*" || mm.work == "commandset*command*") then mm.work = '' # clear mm.work += "commandset*" mm.push(); # format the tape attributes. Add the next command on a newline if mm.cell > 0 then mm.cell -= 1; end # -- mm.work += mm.tape[mm.cell] # get mm.work += "\n" mm.cell += 1 # ++ mm.work += mm.tape[mm.cell] # get if mm.cell > 0 then mm.cell -= 1; end # -- mm.tape[mm.cell] = mm.work # put mm.cell += 1 # ++ mm.work = '' # clear next end #------------------- # here we begin to parse "test*" and "ortestset*" and "andtestset*" # #------------------- # eg: B"abc" {} or E"xyz" {} # transform and markup the different test types if (mm.work == "begintext*,*" || mm.work == "endtext*,*" || mm.work == "quote*,*" || mm.work == "class*,*" || mm.work == "eof*,*" || mm.work == "tapetest*,*" || mm.work == "begintext*.*" || mm.work == "endtext*.*" || mm.work == "quote*.*" || mm.work == "class*.*" || mm.work == "eof*.*" || mm.work == "tapetest*.*" || mm.work == "begintext*{*" || mm.work == "endtext*{*" || mm.work == "quote*{*" || mm.work == "class*{*" || mm.work == "eof*{*" || mm.work == "tapetest*{*") then if (mm.work.start_with?("begin")) then mm.work = '' # clear mm.work += "mm.work.start_with?(" end if (mm.work.start_with?("end")) then mm.work = '' # clear mm.work += "mm.work.end_with?(" end if (mm.work.start_with?("quote")) then mm.work = '' # clear mm.work += "mm.work == " end if (mm.work.start_with?("class")) then mm.work = '' # clear mm.work += "mm.work.match?(" # unicode categories are also regexs end # clear the tapecell for testeof and testtape because # they take no arguments. if (mm.work.start_with?("eof")) then mm.work = '' # clear mm.tape[mm.cell] = mm.work # put mm.work += "mm.eof" end if (mm.work.start_with?("tapetest")) then mm.work = '' # clear mm.tape[mm.cell] = mm.work # put mm.work += "mm.work == mm.tape[mm.cell]" end mm.work += mm.tape[mm.cell] # get # a hack #B"mm.work.match?" { add ')'; } if (!mm.work.start_with?("mm.eof") && !mm.work.start_with?("mm.work ==")) then mm.work += ")" end mm.tape[mm.cell] = mm.work # put mm.work = '' # clear mm.work += "test*" mm.push(); # the trick below pushes the right token back on the stack. mm.work += mm.tape[mm.cell] # get mm.work += "*" mm.push(); next end #------------------- # negated tests # eg: !B"xyz {} !(eof) {} !(==) {} # !E"xyz" {} # !"abc" {} # ![a-z] {} if (mm.work == "notbegintext*,*" || mm.work == "notendtext*,*" || mm.work == "notquote*,*" || mm.work == "notclass*,*" || mm.work == "noteof*,*" || mm.work == "nottapetest*,*" || mm.work == "notbegintext*.*" || mm.work == "notendtext*.*" || mm.work == "notquote*.*" || mm.work == "notclass*.*" || mm.work == "noteof*.*" || mm.work == "nottapetest*.*" || mm.work == "notbegintext*{*" || mm.work == "notendtext*{*" || mm.work == "notquote*{*" || mm.work == "notclass*{*" || mm.work == "noteof*{*" || mm.work == "nottapetest*{*") then if (mm.work.start_with?("notbegin")) then mm.work = '' # clear mm.work += "!mm.work.start_with?(" end if (mm.work.start_with?("notend")) then mm.work = '' # clear mm.work += "!mm.work.end_with?(" end if (mm.work.start_with?("notquote")) then mm.work = '' # clear mm.work += "mm.work != " end if (mm.work.start_with?("notclass")) then mm.work = '' # clear mm.work += "!mm.work.match?(" # ruby unicode categories are regexs end # clear the tapecell for testeof and testtape because # they take no arguments. if (mm.work.start_with?("noteof")) then mm.work = '' # clear mm.tape[mm.cell] = mm.work # put mm.work += "!mm.eof" end if (mm.work.start_with?("nottapetest")) then mm.work = '' # clear mm.tape[mm.cell] = mm.work # put mm.work += "mm.work != mm.tape[mm.cell]" end mm.work += mm.tape[mm.cell] # get if (!mm.work.start_with?("!mm.eof") && !mm.work.start_with?("mm.work !=")) then mm.work += ")" end mm.tape[mm.cell] = mm.work # put mm.work = '' # clear mm.work += "test*" mm.push(); # the trick below pushes the right token back on the stack. mm.work += mm.tape[mm.cell] # get mm.work += "*" mm.push(); next end #------------------- # 3 tokens #------------------- mm.pop(); #----------------------------- # some 3 token errors!!! # not a comprehensive list of 3 token errors if (mm.work == "{*quote*;*" || mm.work == "{*begintext*;*" || mm.work == "{*endtext*;*" || mm.work == "{*class*;*" || mm.work == "commandset*quote*;*" || mm.work == "command*quote*;*") then mm.push(); mm.push(); mm.push(); mm.work += "[pep error]\n invalid syntax near line " mm.work += mm.linesRead.to_s # lines mm.work += " (char " mm.work += mm.charsRead.to_s # chars mm.work += ")" mm.work += " of script (misplaced semicolon?) \n" print mm.work # print mm.work = '' # clear exit end # to simplify subsequent tests, transmogrify a single command # to a commandset (multiple commands). if (mm.work == "{*command*}*") then mm.work = '' # clear mm.work += "{*commandset*}*" mm.push(); mm.push(); mm.push(); next end # errors! mixing AND and OR concatenation if (mm.work == ",*andtestset*{*" || mm.work == ".*ortestset*{*") then # push the tokens back to make debugging easier mm.push(); mm.push(); mm.push(); mm.work += " error: mixing AND (.) and OR (,) concatenation in \n" mm.work += " in pep script near line " mm.work += mm.linesRead.to_s # lines mm.work += " (character " mm.work += mm.charsRead.to_s # chars mm.work += ") \n" mm.work += " " mm.work += "\n For example:" mm.work += "\n B\".\".!E\"/\".[abcd./] { print; } # Correct!" mm.work += "\n B\".\".!E\"/\",[abcd./] { print; } # Error! \n" print mm.work # print mm.work = '' # clear exit end #-------------------------------------------- # ebnf: command := keyword , quoted-text , ";" ; # format: add "text"; if (mm.work == "word*quote*;*") then mm.work = '' # clear mm.work += mm.tape[mm.cell] # get if (mm.work == "replace") then # error mm.work += "< command requires 2 parameters, not 1 \n" mm.work += "near line " mm.work += mm.linesRead.to_s # lines mm.work += " of script. \n" print mm.work # print mm.work = '' # clear exit end # check whether argument is single character, otherwise # throw an error. Also, check that argument is not empty # eg "". Its probably silly to allow while/while not to # have a quote argument, since the same can be achieved with # >> while [a]; etc if (mm.work == "escape" || mm.work == "unescape" || mm.work == "while" || mm.work == "whilenot") then # This is trickier than I thought it would be. mm.work = '' # clear mm.cell += 1 # ++ mm.work += mm.tape[mm.cell] # get if mm.cell > 0 then mm.cell -= 1; end # -- # check that arg not empty, (but an empty quote is ok # for the second arg of 'replace' if (mm.work == "\"\"") then mm.work = '' # clear mm.work += "[pep error] near line " mm.work += mm.linesRead.to_s # lines mm.work += " (or char " mm.work += mm.charsRead.to_s # chars mm.work += "): \n" mm.work += " command '" mm.work += mm.tape[mm.cell] # get mm.work += "' cannot have an empty argument (\"\") \n" print mm.work # print exit end # quoted text has the quotes still around it. # also handle escape characters like \n \r etc # this needs to be better if !mm.work.empty? then # clip mm.work = mm.work[0..-2] # clip end if !mm.work.empty? then # clop mm.work = mm.work[1..-1]; # clop end if !mm.work.empty? then # clop mm.work = mm.work[1..-1]; # clop end if !mm.work.empty? then # clop mm.work = mm.work[1..-1]; # clop end # B "\\" { clip; } if !mm.work.empty? then # clip mm.work = mm.work[0..-2] # clip end if (mm.work != "") then mm.work = '' # clear mm.work += "Pep script error near line " mm.work += mm.linesRead.to_s # lines mm.work += " (character " mm.work += mm.charsRead.to_s # chars mm.work += "): \n" mm.work += " command '" mm.work += mm.tape[mm.cell] # get mm.work += "' takes only a single character argument. \n" print mm.work # print exit end mm.work = '' # clear mm.work += mm.tape[mm.cell] # get end if (mm.work == "mark") then mm.work = '' # clear mm.work += "mm.marks[mm.cell] = " mm.cell += 1 # ++ mm.work += mm.tape[mm.cell] # get if mm.cell > 0 then mm.cell -= 1; end # -- mm.work += " # mark" mm.tape[mm.cell] = mm.work # put mm.work = '' # clear mm.work += "command*" mm.push(); next end if (mm.work == "go") then mm.work = '' # clear # convert to ruby mm.work += "ii = mm.marks.find_index(" mm.cell += 1 # ++ mm.work += mm.tape[mm.cell] # get if mm.cell > 0 then mm.cell -= 1; end # -- mm.work += ")\n" mm.work += "if !ii.nil? then mm.cell = ii end " mm.tape[mm.cell] = mm.work # put mm.work = '' # clear mm.work += "command*" mm.push(); next end if (mm.work == "delim") then mm.work = '' # clear # the delimiter should be a single character, no? mm.work += "mm.delimiter = " mm.cell += 1 # ++ mm.work += mm.tape[mm.cell] # get if mm.cell > 0 then mm.cell -= 1; end # -- mm.work += " # delim " mm.tape[mm.cell] = mm.work # put mm.work = '' # clear mm.work += "command*" mm.push(); next end if (mm.work == "add") then mm.work = '' # clear mm.work += "mm.work += " mm.cell += 1 # ++ mm.work += mm.tape[mm.cell] # get if mm.cell > 0 then mm.cell -= 1; end # -- # handle multiline text # check this! \\n or \n # replace if mm.work.length > 0 then mm.work.gsub!("\n", "\"\nmm.work += \"\\n") end mm.tape[mm.cell] = mm.work # put mm.work = '' # clear mm.work += "command*" mm.push(); next end if (mm.work == "while") then mm.work = '' # clear mm.work += "while mm.peep == " mm.cell += 1 # ++ mm.work += mm.tape[mm.cell] # get if mm.cell > 0 then mm.cell -= 1; end # -- mm.work += "; # while \n" mm.work += " if mm.eof then break end\n mm.read()\n" mm.work += "end" mm.tape[mm.cell] = mm.work # put mm.work = '' # clear mm.work += "command*" mm.push(); next end if (mm.work == "whilenot") then mm.work = '' # clear mm.work += "while mm.peep != " mm.cell += 1 # ++ mm.work += mm.tape[mm.cell] # get if mm.cell > 0 then mm.cell -= 1; end # -- mm.work += "; # whilenot \n" mm.work += " if mm.eof then break end\n mm.read()\nend" mm.tape[mm.cell] = mm.work # put mm.work = '' # clear mm.work += "command*" mm.push(); next end if (mm.work == "until") then mm.work = '' # clear mm.work += "mm.until(" mm.cell += 1 # ++ mm.work += mm.tape[mm.cell] # get if mm.cell > 0 then mm.cell -= 1; end # -- # error until cannot have empty argument if (mm.work == "mm.until(\"\"") then mm.work = '' # clear mm.work += "Pep script error near line " mm.work += mm.linesRead.to_s # lines mm.work += " (character " mm.work += mm.charsRead.to_s # chars mm.work += "): \n" mm.work += " empty argument for 'until' \n" mm.work += " " mm.work += "\n For example:" mm.work += "\n until '.txt'; until \">\"; # correct " mm.work += "\n until ''; until \"\"; # errors! \n" print mm.work # print exit end # handle multiline argument # replace if mm.work.length > 0 then mm.work.gsub!("\n", "\\n") end mm.work += ");" mm.tape[mm.cell] = mm.work # put mm.work = '' # clear mm.work += "command*" mm.push(); next end if (mm.work == "escape") then mm.work = '' # clear mm.cell += 1 # ++ # argument still has quotes around it # it should be a single character since this has been previously # checked. mm.work += mm.tape[mm.cell] # get if !mm.work.empty? then # clip mm.work = mm.work[0..-2] # clip end if !mm.work.empty? then # clop mm.work = mm.work[1..-1]; # clop end mm.tape[mm.cell] = mm.work # put mm.work = '' # clear # A strange ruby fact: gsub("'", "\\'") doesnt work # but gsub("'", "\\\\'") does. Hence the hack below mm.work += "mm.work.gsub!(\"" mm.work += mm.tape[mm.cell] # get mm.work += "\", \"" # a hack, for the quirk above, but this hack will not work # if mm.escape != '\\' if (mm.work.end_with?("\"'\", \"")) then mm.work += "\#{mm.escape}" end mm.work += "\#{mm.escape}" mm.work += mm.tape[mm.cell] # get mm.work += "\") # escape" if mm.cell > 0 then mm.cell -= 1; end # -- mm.tape[mm.cell] = mm.work # put mm.work = '' # clear mm.work += "command*" mm.push(); next end # replace \n with n for example (only 1 character) if (mm.work == "unescape") then mm.work = '' # clear mm.cell += 1 # ++ # unescape is not trivial, need to walk the string # hence the method rather than one-liner #add 'mm.unescapeChar('; get; add ') # unescape'; mm.work += "mm.work.gsub!(mm.escape+" mm.work += mm.tape[mm.cell] # get mm.work += ", " mm.work += mm.tape[mm.cell] # get mm.work += ") # escape" if mm.cell > 0 then mm.cell -= 1; end # -- mm.tape[mm.cell] = mm.work # put mm.work = '' # clear mm.work += "command*" mm.push(); next end # error, superfluous argument mm.work += ": command does not take an argument \n" mm.work += "near line " mm.work += mm.linesRead.to_s # lines mm.work += " of script. \n" print mm.work # print mm.work = '' # clear #state exit end #---------------------------------- # format: "while [:alpha:] ;" or whilenot [a-z] ; if (mm.work == "word*class*;*") then mm.work = '' # clear mm.work += mm.tape[mm.cell] # get if (mm.work == "while") then mm.work = '' # clear mm.work += "# while \n" # the ruby pat.match? method should be faster than others mm.work += "while " mm.cell += 1 # ++ mm.work += mm.tape[mm.cell] # get if mm.cell > 0 then mm.cell -= 1; end # -- mm.work += ".match?(mm.peep)\n" mm.work += " if mm.eof then break end\n mm.read()\nend" mm.tape[mm.cell] = mm.work # put mm.work = '' # clear mm.work += "command*" mm.push(); next end if (mm.work == "whilenot") then mm.work = '' # clear mm.work += "# whilenot \n" mm.work += "while !" mm.cell += 1 # ++ mm.work += mm.tape[mm.cell] # get if mm.cell > 0 then mm.cell -= 1; end # -- mm.work += ".match?(mm.peep)\n" mm.work += " if mm.eof then break end\n" mm.work += " mm.read()\nend" mm.tape[mm.cell] = mm.work # put mm.work = '' # clear mm.work += "command*" mm.push(); next end # error mm.work += " < command cannot have a class argument \n" mm.work += "line " mm.work += mm.linesRead.to_s # lines mm.work += ": error in script \n" print mm.work # print mm.work = '' # clear exit end # arrange the parse> label loops # also, change .restart code before the parse label if (mm.eof) then if (mm.work == "commandset*parse>*commandset*" || mm.work == "command*parse>*commandset*" || mm.work == "commandset*parse>*command*" || mm.work == "command*parse>*command*") then mm.work = '' # clear # indent both code blocks mm.work += " " mm.work += mm.tape[mm.cell] # get # replace if mm.work.length > 0 then mm.work.gsub!("\n", "\n ") end # change .restart code before parse> label # replace if mm.work.length > 0 then mm.work.gsub!("next # restart", "break # restart") end mm.tape[mm.cell] = mm.work # put mm.work = '' # clear mm.cell += 1 # ++ mm.cell += 1 # ++ mm.work += " " mm.work += mm.tape[mm.cell] # get # replace if mm.work.length > 0 then mm.work.gsub!("\n", "\n ") end mm.tape[mm.cell] = mm.work # put mm.work = '' # clear if mm.cell > 0 then mm.cell -= 1; end # -- if mm.cell > 0 then mm.cell -= 1; end # -- # add a block so that .reparse works before the parse> label. mm.work += "\n# lex block \n" mm.work += "while true \n" mm.work += mm.tape[mm.cell] # get mm.work += "\n break \nend\n" mm.cell += 1 # ++ mm.cell += 1 # ++ mm.work += "if restart then restart = false; next; end\n" # indent code block # add " "; get; replace "\n" "\n "; put; clear; # ruby doesnt support labelled loops (but swift does, and go?) # add "parse: \n"; mm.work += "\n# parse block \n" mm.work += "while true \n" mm.work += mm.tape[mm.cell] # get mm.work += "\n break \nend # parse\n" if mm.cell > 0 then mm.cell -= 1; end # -- if mm.cell > 0 then mm.cell -= 1; end # -- mm.tape[mm.cell] = mm.work # put mm.work = '' # clear mm.work += "commandset*" mm.push(); next end end # ------------------------------- # 4 tokens # ------------------------------- mm.pop(); #------------------------------------- # bnf: command := replace , quote , quote , ";" ; # example: replace "and" "AND" ; if (mm.work == "word*quote*quote*;*") then mm.work = '' # clear mm.work += mm.tape[mm.cell] # get if (mm.work == "replace") then #--------------------------- # a command plus 2 arguments, eg replace "this" "that" mm.work = '' # clear mm.work += "# replace \n" mm.work += "if mm.work.length > 0 then \n" mm.work += " mm.work.gsub!(" mm.cell += 1 # ++ mm.work += mm.tape[mm.cell] # get mm.work += ", " mm.cell += 1 # ++ mm.work += mm.tape[mm.cell] # get mm.work += ")\n end\n" if mm.cell > 0 then mm.cell -= 1; end # -- if mm.cell > 0 then mm.cell -= 1; end # -- mm.tape[mm.cell] = mm.work # put mm.work = '' # clear mm.work += "command*" mm.push(); next end mm.work += "Pep script error on line " mm.work += mm.linesRead.to_s # lines mm.work += " (character " mm.work += mm.charsRead.to_s # chars mm.work += "): \n" mm.work += " command does not take 2 quoted arguments. \n" print mm.work # print exit end #------------------------------------- # format: begin { #* commands *# } # "begin" blocks which are only executed once (they # will are assembled before the "start:" label. They must come before # all other commands. # "begin*{*command*}*", if (mm.work == "begin*{*commandset*}*") then mm.work = '' # clear mm.cell += 1 # ++ mm.cell += 1 # ++ mm.work += mm.tape[mm.cell] # get if mm.cell > 0 then mm.cell -= 1; end # -- if mm.cell > 0 then mm.cell -= 1; end # -- mm.tape[mm.cell] = mm.work # put mm.work = '' # clear mm.work += "beginblock*" mm.push(); next end # ------------- # parses and compiles concatenated tests # eg: 'a',B'b',E'c',[def],[:space:],[g-k] { ... # these 2 tests should be all that is necessary if (mm.work == "test*,*ortestset*{*" || mm.work == "test*,*test*{*") then mm.work = '' # clear mm.work += mm.tape[mm.cell] # get mm.work += " || " mm.cell += 1 # ++ mm.cell += 1 # ++ mm.work += mm.tape[mm.cell] # get if mm.cell > 0 then mm.cell -= 1; end # -- if mm.cell > 0 then mm.cell -= 1; end # -- mm.tape[mm.cell] = mm.work # put mm.work = '' # clear mm.work += "ortestset*{*" mm.push(); mm.push(); next end # dont mix AND and OR concatenations # ------------- # AND logic # parses and compiles concatenated AND tests # eg: 'a',B'b',E'c',[def],[:space:],[g-k] { ... # it is possible to elide this block with the negated block # for compactness but maybe readability is not as good. # negated tests can be chained with non negated tests. # eg: B'http' . !E'.txt' { ... } if (mm.work == "test*.*andtestset*{*" || mm.work == "test*.*test*{*") then mm.work = '' # clear mm.work += mm.tape[mm.cell] # get mm.work += " && " mm.cell += 1 # ++ mm.cell += 1 # ++ mm.work += mm.tape[mm.cell] # get if mm.cell > 0 then mm.cell -= 1; end # -- if mm.cell > 0 then mm.cell -= 1; end # -- mm.tape[mm.cell] = mm.work # put mm.work = '' # clear mm.work += "andtestset*{*" mm.push(); mm.push(); next end #------------------------------------- # we should not have to check for the {*command*}* pattern # because that has already been transformed to {*commandset*}* if (mm.work == "test*{*commandset*}*" || mm.work == "andtestset*{*commandset*}*" || mm.work == "ortestset*{*commandset*}*") then mm.work = '' # clear # indent the code for readability mm.cell += 1 # ++ mm.cell += 1 # ++ mm.work += " " mm.work += mm.tape[mm.cell] # get # replace if mm.work.length > 0 then mm.work.gsub!("\n", "\n ") end mm.tape[mm.cell] = mm.work # put if mm.cell > 0 then mm.cell -= 1; end # -- if mm.cell > 0 then mm.cell -= 1; end # -- mm.work = '' # clear mm.work += "if (" mm.work += mm.tape[mm.cell] # get mm.work += ") then\n" mm.cell += 1 # ++ mm.cell += 1 # ++ mm.work += mm.tape[mm.cell] # get # block end required mm.work += "\nend" if mm.cell > 0 then mm.cell -= 1; end # -- if mm.cell > 0 then mm.cell -= 1; end # -- mm.tape[mm.cell] = mm.work # put mm.work = '' # clear mm.work += "command*" mm.push(); # always reparse/compile next end # ------------- # multi-token end-of-stream errors # not a comprehensive list of errors... if (mm.eof) then if (mm.work.end_with?("begintext*") || mm.work.end_with?("endtext*") || mm.work.end_with?("test*") || mm.work.end_with?("ortestset*") || mm.work.end_with?("andtestset*")) then mm.work += " Error near end of script at line " mm.work += mm.linesRead.to_s # lines mm.work += ". Test with no brace block? \n" print mm.work # print mm.work = '' # clear exit end if (mm.work.end_with?("quote*") || mm.work.end_with?("class*") || mm.work.end_with?("word*")) then mm.tape[mm.cell] = mm.work # put mm.work = '' # clear mm.work += "Error at end of pep script near line " mm.work += mm.linesRead.to_s # lines mm.work += ": missing semi-colon? \n" mm.work += "Parse stack: " mm.work += mm.tape[mm.cell] # get mm.work += "\n" print mm.work # print mm.work = '' # clear exit end if (mm.work.end_with?("{*") || mm.work.end_with?("}*") || mm.work.end_with?(";*") || mm.work.end_with?(",*") || mm.work.end_with?(".*") || mm.work.end_with?("!*") || mm.work.end_with?("B*") || mm.work.end_with?("E*")) then mm.tape[mm.cell] = mm.work # put mm.work = '' # clear mm.work += "Error: misplaced terminal character at end of script! (line " mm.work += mm.linesRead.to_s # lines mm.work += "). \n" mm.work += "Parse stack: " mm.work += mm.tape[mm.cell] # get mm.work += "\n" print mm.work # print mm.work = '' # clear exit end end # put the 4 (or less) tokens back on the stack mm.push(); mm.push(); mm.push(); mm.push(); if (mm.eof) then print mm.work # print mm.work = '' # clear # create the virtual machine object code and save it # somewhere on the tape. mm.work += "#!/usr/bin/ruby" mm.work += "\n" mm.work += "\n# code generated by \"translate.ruby.pss\" a pep script" mm.work += "\n# http://bumble.sf.net/books/pars/tr/" mm.work += "\n# require 'something'" mm.work += "\n" mm.work += "\nclass Machine " mm.work += "\n # make a new machine " mm.work += "\n attr_accessor :work, :charsRead, :linesRead, :escape, :delimiter, " mm.work += "\n :counter, :stack, :tape, :cell, :marks, :eof, :peep" mm.work += "\n " mm.work += "\n def initialize() " mm.work += "\n @size = 300 # how many elements in stack/tape/marks" mm.work += "\n @eof = false # end of stream reached?" mm.work += "\n @charsRead = 0 # how many chars already read" mm.work += "\n @linesRead = 1 # how many lines already read" mm.work += "\n @escape = \"\\\\\"" mm.work += "\n @delimiter = \"*\" # push/pop delimiter (default \"*\")" mm.work += "\n @counter = 0 # a counter for anything" mm.work += "\n @work = \"\" # the workspace" mm.work += "\n @stack = [] # stack for parse tokens " mm.work += "\n @cell = 0 # current tape cell" mm.work += "\n @tape = Array.new(@size) {String.new} # a list of attribute for tokens " mm.work += "\n @marks = Array.new(@size) {String.new} # marked tape cells " mm.work += "\n # or dont initialse peep until \"parse()\" calls \"setInput()\"" mm.work += "\n @peep = ARGF.readchar" mm.work += "\n end" mm.work += "\n" mm.work += "\n # multiline strings are ok in ruby" mm.work += "\n def printSizeError()" mm.work += "\n puts \"" mm.work += "\n Tape max size exceeded!" mm.work += "\n tape maximum size = \#{@size}" mm.work += "\n tape cell (current) = \#{@cell} " mm.work += "\n You can increase the @size value in the ruby script " mm.work += "\n but normally this error indicates an error in your parsing " mm.work += "\n script. The only exception would be massively nested structures" mm.work += "\n in the source data.\"" mm.work += "\n end" mm.work += "\n" mm.work += "\n def setInput(newInput) " mm.work += "\n puts \"to be implemented\"" mm.work += "\n end" mm.work += "\n" mm.work += "\n # read one character from the input stream and " mm.work += "\n # update the machine." mm.work += "\n def read " mm.work += "\n if @eof then exit end" mm.work += "\n @charsRead += 1;" mm.work += "\n # increment lines" mm.work += "\n if @peep == \"\\n\" then @linesRead += 1 end" mm.work += "\n @work += @peep" mm.work += "\n @peep = ARGF.readchar" mm.work += "\n if @peep.nil? then @eof = true; end" mm.work += "\n end" mm.work += "\n" mm.work += "\n # test if all chars in workspace are in unicode category" mm.work += "\n def isInCategory(cat) " mm.work += "\n #for ch in @work" mm.work += "\n # if not category(ch).start_with?(cat) then return false end" mm.work += "\n #return True" mm.work += "\n end" mm.work += "\n" mm.work += "\n # this needs to actually walk the string" mm.work += "\n # eg \"ab\cab\\cab\c\"" mm.work += "\n # not trivial" mm.work += "\n def unescapeChar(c)" mm.work += "\n @work.gsub!(\"\#{@escape}\#{c}\", c)" mm.work += "\n end" mm.work += "\n" mm.work += "\n # add escape character : trivial?" mm.work += "\n def escapeChar(c)" mm.work += "\n @work.gsub!(c, @escape+c)" mm.work += "\n end" mm.work += "\n" mm.work += "\n # a helper for the multiescape until bug" mm.work += "\n def countEscaped(suffix) " mm.work += "\n count = 0" mm.work += "\n #s = @work.sub(/\#{suffix}$/, \"\")" mm.work += "\n s = @work.delete_suffix(suffix)" mm.work += "\n while s.end_with?(@escape) " mm.work += "\n count += 1" mm.work += "\n s.delete_suffix!(@escape)" mm.work += "\n end" mm.work += "\n # puts \"count=\#{count}\"" mm.work += "\n return count" mm.work += "\n end" mm.work += "\n" mm.work += "\n # reads the input stream until the workspace end with text " mm.work += "\n def until(suffix) " mm.work += "\n # read at least one character" mm.work += "\n if @eof then return end" mm.work += "\n self.read()" mm.work += "\n while true do" mm.work += "\n if @eof then return end" mm.work += "\n # need to count the @escape chars preceding suffix" mm.work += "\n # if odd, keep reading, if even, stop" mm.work += "\n if @work.end_with?(suffix) then " mm.work += "\n if (self.countEscaped(suffix).even?) then return end" mm.work += "\n end" mm.work += "\n self.read()" mm.work += "\n end" mm.work += "\n end " mm.work += "\n" mm.work += "\n # pop the first token from the stack into the workspace */" mm.work += "\n def pop() " mm.work += "\n if @stack.length == 0 then return false end" mm.work += "\n @work = @stack.pop() + @work" mm.work += "\n if @cell > 0 then @cell -= 1 end" mm.work += "\n return true" mm.work += "\n end" mm.work += "\n" mm.work += "\n # push the first token from the workspace to the stack " mm.work += "\n def push() " mm.work += "\n # dont increment the tape pointer on an empty push" mm.work += "\n if @work == \"\" then return false end" mm.work += "\n # need to get this from the delimiter." mm.work += "\n iFirst = @work.index(@delimiter)" mm.work += "\n if iFirst.nil?" mm.work += "\n @stack.push(@work); @work = \"\"; return true" mm.work += "\n # also @stack << @work" mm.work += "\n end" mm.work += "\n # s[i..j] means all chars from i to j" mm.work += "\n # s[i,n] means n chars from i" mm.work += "\n @stack.push(@work[0..iFirst])" mm.work += "\n @work = @work[iFirst+1..-1]" mm.work += "\n if @cell < @size then " mm.work += "\n @cell += 1" mm.work += "\n else" mm.work += "\n self.printSizeError(); exit " mm.work += "\n end" mm.work += "\n return true" mm.work += "\n end" mm.work += "\n" mm.work += "\n def printState() " mm.work += "\n puts \"Stack[\#{@stack.join(', ')}] Work[\#{@work}] Peep[\#{@peep}]\"" mm.work += "\n puts \"Acc:\#{@counter} Esc:\#{@escape} Delim:\#{@delimiter} Chars:\#{@charsRead}\" +" mm.work += "\n \" Lines:\#{@linesRead} Cell:\#{@cell}\"" mm.work += "\n end" mm.work += "\n" mm.work += "\n # this is where the actual parsing/compiling code should go" mm.work += "\n # so that it can be used by other ruby classes/objects. Also" mm.work += "\n # should have a stream argument." mm.work += "\n def parse(s) " mm.work += "\n # a reset or \"setinput()\" method would be useful to parse a " mm.work += "\n # different string/file/stream, without creating a new" mm.work += "\n # machine object." mm.work += "\n # could use code like this to check if input is string or file" mm.work += "\n #if isinstance(s, file)" mm.work += "\n print(\"\")" mm.work += "\n # @reset(s)" mm.work += "\n # @reader = s" mm.work += "\n #elseif isinstance(s, string)" mm.work += "\n #f = StringIO.StringIO(\"test\")" mm.work += "\n #for line in f print(line)" mm.work += "\n #else" mm.work += "\n # f = STDIN " mm.work += "\n #end" mm.work += "\n #puts \"not implemented\"" mm.work += "\n end" mm.work += "\n" mm.work += "\nend" mm.work += "\n" mm.work += "\n# end of Machine class definition" mm.work += "\n" mm.work += "\n# will become:" mm.work += "\n# mm.parse(sys.stdin) or " mm.work += "\n# mm.parse(\"abcdef\") or" mm.work += "\n# open f; mm.parse(f)" mm.work += "\n" mm.work += "\n# the restart flag, which allows .restart to work before the " mm.work += "\n# parse label, in languages (like ruby) that dont have " mm.work += "\n# labelled loops" mm.work += "\nrestart = false" mm.work += "\nmm = Machine.new \n" # save the code in the current tape cell mm.tape[mm.cell] = mm.work # put mm.work = '' # clear #--------------------- # check if the script correctly parsed (there should only # be one token on the stack, namely "commandset*" or "command*"). mm.pop(); mm.pop(); if (mm.work == "commandset*" || mm.work == "command*") then mm.work = '' # clear # indent generated code (6 spaces) for readability. mm.work += " " mm.work += mm.tape[mm.cell] # get # replace if mm.work.length > 0 then mm.work.gsub!("\n", "\n ") end mm.tape[mm.cell] = mm.work # put mm.work = '' # clear # restore the java preamble from the tape mm.cell += 1 # ++ mm.work += mm.tape[mm.cell] # get if mm.cell > 0 then mm.cell -= 1; end # -- #add 'script: \n'; mm.work += "while !mm.eof do \n" mm.work += mm.tape[mm.cell] # get # end block marker required in ruby mm.work += "\nend\n" mm.work += "\n\n# end of generated code\n" # put a copy of the final compilation into the tapecell # so it can be inspected interactively. mm.tape[mm.cell] = mm.work # put print mm.work # print mm.work = '' # clear exit end if (mm.work == "beginblock*commandset*" || mm.work == "beginblock*command*") then mm.work = '' # clear # indentation not needed here #add ""; get; #replace "\n" "\n"; put; clear; # indent main code for readability. mm.cell += 1 # ++ mm.work += " " mm.work += mm.tape[mm.cell] # get # replace if mm.work.length > 0 then mm.work.gsub!("\n", "\n ") end mm.tape[mm.cell] = mm.work # put mm.work = '' # clear if mm.cell > 0 then mm.cell -= 1; end # -- # get java preamble from tape mm.cell += 1 # ++ mm.cell += 1 # ++ mm.work += mm.tape[mm.cell] # get if mm.cell > 0 then mm.cell -= 1; end # -- if mm.cell > 0 then mm.cell -= 1; end # -- mm.work += mm.tape[mm.cell] # get mm.work += "\n" mm.cell += 1 # ++ # a labelled loop for "quit" (but quit can just exit?) #add "script: \n"; mm.work += "while !mm.eof do \n" mm.work += mm.tape[mm.cell] # get # end block marker required in ruby mm.work += "\nend\n" mm.work += "\n\n# end of generated code\n" # put a copy of the final compilation into the tapecell # for interactive debugging. mm.tape[mm.cell] = mm.work # put print mm.work # print mm.work = '' # clear exit end mm.push(); mm.push(); # try to explain some more errors while mm.pop() do next end # unstack if (mm.work.start_with?("parse>")) then mm.tape[mm.cell] = mm.work # put mm.work = '' # clear mm.work += "[error] pep syntax error:\n" mm.work += " The parse> label cannot be the 1st item \n" mm.work += " of a script \n" print mm.work # print exit end mm.tape[mm.cell] = mm.work # put mm.work = '' # clear mm.work = '' # clear mm.work += "After compiling with 'translate.ruby.pss' (at EOF): \n " mm.work += " parse error in input script. \n " print mm.work # print mm.work = '' # clear while mm.pop() do next end # unstack mm.tape[mm.cell] = mm.work # put mm.work = '' # clear mm.work += "Parse stack: " mm.work += mm.tape[mm.cell] # get mm.work += "\n" mm.work += " * debug script " mm.work += " >> pep -If script -i 'some input' \n " mm.work += " * debug compilation. \n " mm.work += " >> pep -Ia asm.pp script' \n " print mm.work # print mm.work = '' # clear exit end # not eof # there is an implicit .restart command here (jump start) break end # parse end # end of generated code