Fix interactive assembly on Python 3

Closes #81
Closes #78
Closes #65
Closes #64
Closes #63
This commit is contained in:
Mike Naberezny 2024-04-12 12:24:29 -07:00
parent 8dce37f6b8
commit 1870d65982
6 changed files with 137 additions and 13 deletions

View File

@ -21,11 +21,14 @@ jobs:
run: python -V
- name: Install dependencies
run: $PIP install setuptools
run: $PIP install setuptools pexpect
- name: Run the tests
run: python setup.py test -q
- name: Run the end-to-end tests
run: END_TO_END=1 python setup.py test -q
build_py34:
runs-on: ubuntu-20.04
container: python:3.4
@ -44,11 +47,14 @@ jobs:
run: python -V
- name: Install dependencies
run: $PIP install setuptools
run: $PIP install setuptools pexpect
- name: Run the tests
run: python setup.py test -q
- name: Run the end-to-end tests
run: END_TO_END=1 python setup.py test -q
build_py35:
runs-on: ubuntu-20.04
container: python:3.5
@ -62,11 +68,14 @@ jobs:
run: python -V
- name: Install dependencies
run: $PIP install setuptools
run: $PIP install setuptools pexpect
- name: Run the tests
run: python setup.py test -q
- name: Run the end-to-end tests
run: END_TO_END=1 python setup.py test -q
build_py3x:
strategy:
fail-fast: false
@ -88,7 +97,10 @@ jobs:
run: python -V
- name: Install dependencies
run: $PIP install setuptools
run: $PIP install setuptools pexpect
- name: Run the tests
run: python setup.py test -q
- name: Run the end-to-end tests
run: END_TO_END=1 python setup.py test -q

View File

@ -5,6 +5,8 @@
dropped when pasting in larger amounts of text. This makes it possible
to paste programs into EhBASIC and Taliforth. Patch by SamCoVT.
- Fixed interactive assembly on Python 3.
- Fixed regular expression warnings on Python 3.12.
- The ``fill`` command in the monitor now shows an error message if an

View File

@ -92,7 +92,7 @@ class Assembler:
and parsing the address part using AddressParser. The result of
the normalization is a tuple of two strings (opcode, operand).
"""
statement = ' '.join(str.split(statement))
statement = ' '.join(statement.split())
# normalize target in operand
match = self.Statement.match(statement)

21
py65/compat.py Normal file
View File

@ -0,0 +1,21 @@
import sys
PY2 = sys.version_info[0] == 2
if PY2:
unicode = unicode
def as_string(s, encoding='utf-8'):
if isinstance(s, unicode):
return s
else:
return s.decode(encoding)
else:
unicode = str
def as_string(s, encoding='utf-8'):
if isinstance(s, str):
return s
else:
return s.decode(encoding)

88
py65/tests/end_to_end.py Normal file
View File

@ -0,0 +1,88 @@
import os
import signal
import sys
import tempfile
import unittest
from py65.compat import unicode
# end-to-test tests are slow so only run them when asked
if 'END_TO_END' in os.environ:
if sys.platform == "win32":
raise NotImplementedError()
else:
import pexpect
BaseTestCase = unittest.TestCase
else:
BaseTestCase = object
class EndToEndTests(BaseTestCase):
def _spawn(self):
mon = pexpect.spawn(
sys.executable,
['-u', '-m', 'py65.monitor'],
encoding='utf-8'
)
mon.expect_exact(unicode("Py65 Monitor"))
self.addCleanup(mon.kill, signal.SIGINT)
return mon
def test_putc(self):
mon = self._spawn()
mon.sendline(unicode("add_label f001 putc"))
mon.sendline(unicode("a c000 lda #'H"))
mon.sendline(unicode("a c002 sta putc"))
mon.sendline(unicode("a c005 lda #'I"))
mon.sendline(unicode("a c007 sta putc"))
mon.sendline(unicode("a c00a brk"))
mon.sendline(unicode("g c000"))
mon.expect_exact(unicode("HI"))
mon.sendline(unicode("q"))
def test_getc(self):
mon = self._spawn()
mon.sendline(unicode("add_label f004 getc"))
mon.sendline(unicode("a c000 ldx #0"))
mon.sendline(unicode("a c002 lda getc"))
mon.sendline(unicode("a c005 beq c002"))
mon.sendline(unicode("a c007 cmp #'!"))
mon.sendline(unicode("a c009 bne c00c"))
mon.sendline(unicode("a c00b brk"))
mon.sendline(unicode("a c00c sta 1000,x"))
mon.sendline(unicode("a c00f inx"))
mon.sendline(unicode("a c010 jmp c002"))
mon.sendline(unicode("g c000"))
mon.send(unicode("HELLO!"))
mon.expect_exact(unicode("6502:"))
mon.sendline(unicode("m 1000:1004"))
mon.expect_exact(unicode("48 45 4c 4c 4f"))
def test_assemble_interactive(self):
mon = self._spawn()
mon.sendline(unicode("assemble 0"))
mon.expect_exact(unicode("$0000"))
mon.sendline(unicode("lda $1234"))
mon.expect_exact(unicode("ad 34 12"))
mon.expect_exact(unicode("$0003"))
mon.sendline(unicode("sta $4567"))
mon.expect_exact(unicode("8d 67 45"))
mon.sendline(unicode("invalid"))
mon.expect_exact(unicode("?Syntax"))
mon.sendline()
mon.sendline(unicode("quit"))
if __name__ == '__main__':
unittest.main()

View File

@ -1,5 +1,7 @@
import sys
from py65.compat import as_string
if sys.platform[:3] == "win":
import msvcrt
@ -24,10 +26,7 @@ if sys.platform[:3] == "win":
is available. Does not echo the character. The stdin argument is
for function signature compatibility and is ignored.
"""
c = msvcrt.getch()
if isinstance(c, bytes): # Python 3
c = c.decode('latin-1')
return c
return as_string(msvcrt.getch())
def getch_noblock(stdin):
""" Read one character from the Windows console without blocking.
@ -36,8 +35,8 @@ if sys.platform[:3] == "win":
available, an empty string is returned.
"""
if msvcrt.kbhit():
return getch(stdin)
return ''
return as_string(getch(stdin))
return u''
else:
import termios
@ -157,7 +156,7 @@ else:
# use select to make sure there is at least one char to read.
rd,wr,er = select([stdin], [], [], 0.01)
if rd != []:
char = stdin.read(1)
char = as_string(stdin.read(1))
except KeyboardInterrupt:
# Pass along a CTRL-C interrupt.
raise
@ -180,7 +179,7 @@ else:
# use select to make sure there is at least one char to read.
rd,wr,er = select([stdin], [], [], 0.01)
if rd != []:
char = stdin.read(1)
char = as_string(stdin.read(1))
except KeyboardInterrupt:
# Pass along a CTRL-C interrupt.
raise
@ -200,6 +199,7 @@ def line_input(prompt='', stdin=sys.stdin, stdout=sys.stdout):
is useful in modes like the interactive assembler.
"""
stdout.write(prompt)
stdout.flush()
line = ''
while True:
char = getch(stdin)
@ -211,6 +211,7 @@ def line_input(prompt='', stdin=sys.stdin, stdout=sys.stdout):
line = line[:-1]
stdout.write("\r%s\r%s%s" %
(' ' * (len(prompt + line) + 5), prompt, line))
stdout.flush()
elif code == 0x1b: # escape
pass
else: