Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

옵션 에러 처리 방식 변경 및 테스트 추가, .aheui.aheuis 방지 #40

Merged
merged 4 commits into from
Apr 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading