Skip to content

Commit

Permalink
Merge pull request #40 from aheui/options
Browse files Browse the repository at this point in the history
옵션 에러 처리 방식 변경 및 테스트 추가, .aheui.aheuis 방지
  • Loading branch information
youknowone authored Apr 7, 2024
2 parents d11187e + 9cb3880 commit ceaae87
Show file tree
Hide file tree
Showing 6 changed files with 152 additions and 59 deletions.
25 changes: 16 additions & 9 deletions aheui/_argparse.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,14 @@ class ArgumentNotInChoicesError(ParserError):
__description__ = 'argument is not in choices: '


class InformationException(ParserError):
__description__ = ''
class InformationException(Exception):
def __init__(self, desc=''):
self.desc = desc



class HelpException(InformationException):
__description__ = ''
pass


class ArgumentParser(object):
Expand Down Expand Up @@ -88,7 +90,10 @@ def _parse_args(self, args):
done = True
elif name.startswith('-'):
if name == arg:
arg = args[idx + 1]
try:
arg = args[idx + 1]
except IndexError:
raise TooFewArgumentError(name)
parsed[dest] = arg
idx += 2
else:
Expand All @@ -113,7 +118,7 @@ def parse_args(self, args):
try:
return self._parse_args(args)
except HelpException:
os.write(2, 'usage: %s [option] ... file\n\n' % self.kwargs.get('prog', args[0]))
os.write(2, 'usage: %s [option] ... file\n\n' % get_prog(args[0]))
for names, opt in self.arguments:
name = names[0] if names[0] == names[1] else ('%s,%s' % names[0:2])
os.write(2, '%s%s: %s' % (name, ' ' * (12 - len(name)), opt['description']))
Expand All @@ -125,9 +130,11 @@ def parse_args(self, args):
os.write(2, '\n')
os.write(2, opt['full_description'])
os.write(2, '\n')
raise
except InformationException as e:
os.write(2, '%s\n' % e.desc)
except ParserError as e:
prog = self.kwargs.get('prog', args[0])
os.write(2, '%s: error: %s\n' % (prog, e.message()))
return {}, []
raise


def get_prog(arg0):
return arg0.rsplit('/', 1)[-1]
33 changes: 18 additions & 15 deletions aheui/aheui.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,11 @@
import os

from aheui import const as c
from aheui._argparse import InformationException, get_prog
from aheui._compat import jit, unichr, ord, _unicode, bigint, PYR
from aheui import compile
from aheui.option import process_options
from aheui.warning import WarningPool
from aheui.option import process_options, OptionError
from aheui.warning import NoRpythonWarning, WriteUtf8RangeWarning, warnings


def get_location(pc, stackok, is_queue, program):
Expand All @@ -34,7 +35,7 @@ def get_location(pc, stackok, is_queue, program):
MINUS1 = bigint.fromlong(-1)


class Link(object):
class Node(object):
"""Element unit for stack and queue."""

def __init__(self, next, value=MINUS1):
Expand Down Expand Up @@ -102,7 +103,7 @@ def __init__(self):

def push(self, value):
# assert(isinstance(value, bigint.Int))
node = Link(self.head, value)
node = Node(self.head, value)
self.head = node
self.size += 1

Expand All @@ -121,22 +122,22 @@ def _put_value(self, value):
class Queue(LinkedList):

def __init__(self):
self.tail = Link(None)
self.tail = Node(None)
self.head = self.tail
self.size = 0

def push(self, value):
# assert(isinstance(value, bigint.Int))
tail = self.tail
tail.value = value
new = Link(None)
new = Node(None)
tail.next = new
self.tail = new
self.size += 1

def dup(self):
head = self.head
node = Link(head, head.value)
node = Node(head, head.value)
self.head = node
self.size += 1

Expand All @@ -156,7 +157,7 @@ def __init__(self):

def push(self, value):
# assert(isinstance(value, bigint.Int))
node = Link(self.head, value)
node = Node(self.head, value)
self.head = node
self.size += 1
self.last_push = value
Expand Down Expand Up @@ -282,7 +283,7 @@ def write_number(value_str):
os.write(outfp, value_str)


def write_utf8(warnings, value):
def write_utf8(value):
REPLACE_CHAR = unichr(0xfffd).encode('utf-8')

if bigint.is_unicodepoint(value):
Expand All @@ -295,8 +296,8 @@ def write_utf8(warnings, value):
os.write(outfp, bytes)


def warn_utf8_range(warnings, value):
warnings.warn(b'write-utf8-range', value)
def warn_utf8_range(value):
warnings.warn(WriteUtf8RangeWarning, value)
os.write(outfp, unichr(0xfffd).encode('utf-8'))

class Program(object):
Expand Down Expand Up @@ -327,7 +328,6 @@ def get_label(self, pc):

outfp = 1
errfp = 2
warnings = WarningPool()


def mainloop(program, debug):
Expand Down Expand Up @@ -421,7 +421,7 @@ def mainloop(program, debug):
write_number(bigint.str(r))
elif op == c.OP_POPCHAR:
r = selected.pop()
write_utf8(warnings, r)
write_utf8(r)
elif op == c.OP_PUSHNUM:
num = read_number()
selected.push(num)
Expand Down Expand Up @@ -483,7 +483,10 @@ def prepare_compiler(contents, opt_level=2, source='code', aheuic_output=None, a
def entry_point(argv):
try:
cmd, source, contents, str_opt_level, target, aheuic_output, comment_aheuis, output, warning_limit, trace_limit = process_options(argv, os.environ)
except SystemExit:
except InformationException:
return 0
except OptionError as e:
os.write(errfp, b"%s: error: %s\n" % (get_prog(argv[0]), e.message()))
return 1

warnings.limit = warning_limit
Expand All @@ -495,7 +498,7 @@ def entry_point(argv):
outfp = 1 if output == '-' else open_w(output)
if target == 'run':
if not PYR:
warnings.warn(b'no-rpython')
warnings.warn(NoRpythonWarning)
program = Program(compiler.lines, compiler.label_map)
exitcode = mainloop(program, compiler.debug)
elif target in ['asm', 'asm+comment']:
Expand Down
77 changes: 57 additions & 20 deletions aheui/option.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@
from __future__ import absolute_import

import os
from aheui._argparse import ArgumentParser
from aheui._argparse import ArgumentParser, ParserError
from aheui._compat import bigint, PY3
from aheui.version import VERSION
from aheui.warning import CommandLineArgumentWarning, warnings
from aheui import compile


Expand Down Expand Up @@ -37,6 +38,39 @@
parser.add_argument('--help', '-h', narg='-1', default='no', description='Show this help text')


class OptionError(Exception):
pass


class ParsingError(OptionError):
def __init__(self, msg):
self.args = (msg,)

def message(self):
return self.args[0]


class IntOptionParsingError(OptionError):
def __init__(self, key, value):
self.args = (key, value)
def message(self):
return 'The value of %s="%s" is not a valid integer' % self.args


class SourceError(OptionError):
pass


class NoInputError(SourceError):
def message(self):
return "no input files"


class CommandConflictInputFileError(SourceError):
def message(self):
return "--cmd,-c and input file cannot be used together"


def kwarg_or_environ(kwargs, environ, arg_key, env_key):
if arg_key in kwargs and kwargs[arg_key] != '':
return (1, kwargs[arg_key])
Expand All @@ -54,41 +88,39 @@ def kwarg_or_environ_int(kwargs, environ, arg_key, env_key, default):
value = int(arg)
except ValueError:
if source == 1:
msg = b'The value of --%s="%s" is not a valid integer\n' % (arg_key, arg)
raise IntOptionParsingError('--' + arg_key, arg)
elif source == 2:
msg = b'The value %s="%s" is not a valid integer\n' % (env_key, arg)
raise IntOptionParsingError(env_key, arg)
else:
assert False
os.write(2, msg)
raise
return value


def process_options(argv, environ):
def open_r(filename):
return os.open(filename, os.O_RDONLY, 0o777)
def open_input(filename):
return os.open(filename, os.O_RDONLY, 0o777)

kwargs, args = parser.parse_args(argv)
if not args:
raise SystemExit()

def process_options(argv, environ):
try:
kwargs, args = parser.parse_args(argv)
except ParserError as e:
raise ParsingError(e.message())

cmd = kwargs['cmd']
if cmd == '':
if len(args) != 2:
os.write(2, b'aheui: error: no input files\n')
raise SystemExit()
raise NoInputError()
filename = args[1]
if filename == '-':
fp = 0
contents = compile.read(fp)
else:
fp = open_r(filename)
fp = open_input(filename)
contents = compile.read(fp)
os.close(fp)
else:
if len(args) != 1:
os.write(2, b'aheui: error: --cmd,-c but input file found\n')
raise SystemExit()
raise CommandConflictInputFileError
if PY3:
cmd = cmd.encode('utf-8')
contents = cmd
Expand All @@ -115,10 +147,12 @@ def open_r(filename):

if need_aheuic:
aheuic_output = filename
if aheuic_output.endswith('.aheui'):
aheuic_output += 'c'
if filename.endswith('.aheui'):
aheuic_output = filename + 'c'
elif filename.endswith('.aheuis'):
aheuic_output = filename[:-1] + 'c'
else:
aheuic_output += '.aheuic'
aheuic_output = filename + '.aheuic'
else:
aheuic_output = None

Expand All @@ -142,8 +176,11 @@ def open_r(filename):
elif target == 'run':
output = '-'
else:
os.write(2, b'aheui: error: --target,-t must be one of "bytecode", "asm", "asm+comment", "run"\n') # noqa: E501
assert False # must be handled by argparse
raise SystemExit()
else:
if target == 'run':
warnings.warn(CommandLineArgumentWarning, b'--target=run always ignores --output')

warning_limit = kwarg_or_environ_int(kwargs, environ, 'warning-limit', 'RPAHEUI_WARNING_LIMIT', 3)
trace_limit = kwarg_or_environ_int(kwargs, environ, 'trace-limit', 'RPAHEUI_TRACE_LIMIT', -1)
Expand Down
38 changes: 24 additions & 14 deletions aheui/warning.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,36 +5,46 @@


class Warning(object):
def __init__(self, name, message):
self.name = name
self.message = message

def format(self, *args):
return self.message % args
return self.MESSAGE % args


class NoRpythonWarning(Warning):
MESSAGE = b"[Warning:VirtualMachine] Running without rlib/jit."


class CommandLineArgumentWarning(Warning):
MESSAGE = b"[Warning:CommandLine] Invalid command line argument is ignored: %s."


class WriteUtf8RangeWarning(Warning):
MESSAGE = b'[Warning:UndefinedBehavior:write-utf8-range] value %x is out of unicode codepoint range.'


WARNING_LIST = [
Warning(b'no-rpython', b"[Warning:VirtualMachine] Running without rlib/jit.\n"),
Warning(b'write-utf8-range', b'[Warning:UndefinedBehavior:write-utf8-range] value %x is out of unicode codepoint range.'),
NoRpythonWarning(),
CommandLineArgumentWarning(),
WriteUtf8RangeWarning(),
]


class WarningPool(object):
def __init__(self):
self.limit = -1
self.warnings = {}
self.counters = {}
for w in WARNING_LIST:
self.warnings[w.name] = w
self.counters[w.name] = 0
self.counters[type(w).__name__] = 0

@jit.dont_look_inside
def warn(self, name, *args):
warning = self.warnings[name]
def warn(self, warning, *args):
name = warning.__name__
if self.limit != -1 and self.limit <= self.counters[name]:
return
self.counters[name] = self.counters[name] + 1
os.write(2, warning.format(*args))
os.write(2, warning().format(*args))
os.write(2, b'\n')
if self.limit != -1 and self.limit <= self.counters[name]:
os.write(2, b"[Warning:Meta] The warning '%s' has reached the limit %d and will be suppressed\n" % (warning.name, self.limit))
os.write(2, b"[Warning:Meta] The warning '%s' has reached the limit %d and will be suppressed\n" % (name, self.limit))


warnings = WarningPool()
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ def get_readme():


tests_require = [
'ruff', 'tox', 'pytest>=3.0.1',
'ruff', 'tox', 'pytest>=3.0.1', 'pytest-mock'
]

setup(
Expand Down
Loading

0 comments on commit ceaae87

Please sign in to comment.