pyudage.py 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213
  1. #!/usr/bin/env python
  2. """\
  3. Udage Interpreter in Python, revision 1
  4. Copyright (c) 2005, Kang Seonghoon (Tokigun).
  5. This library is free software; you can redistribute it and/or
  6. modify it under the terms of the GNU Lesser General Public
  7. License as published by the Free Software Foundation; either
  8. version 2.1 of the License, or (at your option) any later version.
  9. This library is distributed in the hope that it will be useful,
  10. but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  12. Lesser General Public License for more details.
  13. You should have received a copy of the GNU Lesser General Public
  14. License along with this library; if not, write to the Free Software
  15. Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
  16. """
  17. import sys
  18. class UdageDict(dict):
  19. def __init__(self, default=None, object=None):
  20. if object is None: dict.__init__(self)
  21. else: dict.__init__(self, object)
  22. self.default = default
  23. def __getitem__(self, key):
  24. try: return dict.__getitem__(self, key)
  25. except KeyError: return self.default
  26. class UdageIO:
  27. def __init__(self, encoding='utf-8'):
  28. self.encoding = encoding
  29. def getunicode(self):
  30. buf = ''
  31. while 1:
  32. char = sys.stdin.read(1)
  33. if char == '': return -1
  34. buf += char
  35. try:
  36. return ord(buf.decode(self.encoding))
  37. except UnicodeError:
  38. try:
  39. reason = sys.exc_info()[1].reason
  40. if not ('end of data' in reason or 'incomplete' in reason):
  41. return None
  42. except: pass
  43. def getchar(self):
  44. char = self.getunicode()
  45. if char is None:
  46. return 0xfffd
  47. else:
  48. return char == 13 and 10 or char
  49. def putchar(self, value):
  50. try: sys.stdout.write(unichr(value).encode(self.encoding))
  51. except: sys.stdout.write('[U+%04X]' % value)
  52. class UdageInterpreter:
  53. def __init__(self, code, io=None):
  54. if not isinstance(code, unicode):
  55. code = unicode(code)
  56. self.code = code
  57. self.io = io
  58. def init(self):
  59. self.pointer = UdageDict(0)
  60. self.value = UdageDict(False)
  61. self.current = 0
  62. def getswitch(self, key):
  63. return self.value[self.pointer[key] or key]
  64. def setswitch(self, key, value):
  65. self.value[self.pointer[key] or key] = value
  66. def step(self):
  67. if self.current >= len(self.code): return False
  68. char = self.code[self.current]
  69. if self.getswitch(char):
  70. ptr = self.current
  71. while ptr < len(self.code) and char == self.code[ptr]:
  72. ptr += 1
  73. length = ptr - self.current
  74. if length == 1:
  75. self.setswitch(char, False)
  76. self.current += 1
  77. elif length == 2:
  78. ptr += 3
  79. if ptr > len(self.code): raise RuntimeError, "incomplete instruction"
  80. operand1 = self.getswitch(self.code[ptr-3])
  81. operand2 = self.getswitch(self.code[ptr-2])
  82. target = self.code[ptr-1]
  83. result = not (operand1 and operand2)
  84. self.setswitch(char, result)
  85. if not result:
  86. direction = self.getswitch(target)
  87. if direction:
  88. ptr = self.code.rfind(target, 0, self.current)
  89. else:
  90. ptr = self.code.find(target, ptr)
  91. if ptr < 0: raise RuntimeError, "cannot jump to undefined instruction"
  92. ptr += 1
  93. self.current = ptr
  94. elif length == 3 or length == 4:
  95. endptr = self.code.find(char, ptr)
  96. if endptr < 0: raise RuntimeError, "incomplete instruction"
  97. number = self.code[ptr:endptr]
  98. value = reduce(lambda x,y:x*2+int(self.getswitch(y)), number, 0)
  99. if length == 3:
  100. self.pointer[char] = value
  101. elif value > 0:
  102. self.io.putchar(value)
  103. else:
  104. value = self.io.getchar()
  105. for char in number[::-1]:
  106. self.setswitch(char, bool(value & 1))
  107. value >>= 1
  108. self.current = endptr + 1
  109. else:
  110. raise RuntimeError, "invalid instruction"
  111. else:
  112. self.setswitch(char, True)
  113. self.current += 1
  114. return True
  115. def execute(self):
  116. self.init()
  117. while self.step(): pass
  118. ################################################################################
  119. def version(apppath):
  120. print __doc__
  121. return 0
  122. def help(apppath):
  123. print __doc__[:__doc__.find('\n\n')]
  124. print
  125. print "Usage: %s [options] <filename> [<param>]" % apppath
  126. print
  127. print "--help, -h"
  128. print " prints help message."
  129. print "--version, -V"
  130. print " prints version information."
  131. print "--fileencoding, -e"
  132. print " set encoding of source code. (default 'utf-8')"
  133. print "--ioencoding"
  134. print " set encoding for input/output."
  135. return 0
  136. def main(argv):
  137. import getopt, locale
  138. try:
  139. opts, args = getopt.getopt(argv[1:], "hVe:",
  140. ['help', 'version', 'fileencoding=', 'ioencoding=',])
  141. except getopt.GetoptError:
  142. return help(argv[0])
  143. fencoding = 'utf-8'
  144. ioencoding = locale.getdefaultlocale()[1]
  145. for opt, arg in opts:
  146. if opt in ('-h', '--help'):
  147. return help(argv[0])
  148. if opt in ('-V', '--version'):
  149. return version(argv[0])
  150. if opt in ('-e', '--fileencoding'):
  151. fencoding = arg
  152. if opt == '--ioencoding':
  153. ioencoding = arg
  154. if len(args) == 0:
  155. print __doc__[:__doc__.find('\n\n')]
  156. return 1
  157. # encoding fix
  158. if fencoding == 'euc-kr': fencoding = 'cp949'
  159. if ioencoding == 'euc-kr': ioencoding = 'cp949'
  160. filename = args[0]
  161. param = args[1:]
  162. try:
  163. if filename == '-':
  164. data = sys.stdin.read()
  165. else:
  166. data = file(filename).read()
  167. except IOError:
  168. print "Cannot read file: %s" % filename
  169. return 1
  170. try:
  171. code = data.decode(fencoding)
  172. except LookupError:
  173. print "There is no encoding named '%s'." % fencoding
  174. return 1
  175. except UnicodeDecodeError:
  176. print "Unicode decode error!"
  177. return 1
  178. io = UdageIO(ioencoding)
  179. interpreter = UdageInterpreter(code.strip(), io)
  180. interpreter.execute()
  181. return 0
  182. if __name__ == '__main__':
  183. sys.exit(main(sys.argv))