├── test.h ├── testbed ├── __init__.py ├── conversion.tokens ├── conversionLexer.tokens ├── conversion.g4 ├── test_noconversion.py ├── conversionLexer.g4 ├── Makefile ├── conversionVisitor.py ├── conversion.interp ├── __main__.py ├── conversionLexer.interp ├── conversionLexer.py ├── conversion.py ├── test_lib.prg ├── test_lib.py └── test_conversion.py ├── vfp2py ├── __init__.py ├── Makefile ├── __main__.py ├── function_abbreviations.py ├── vfpdatabase.py ├── vfp2py.py ├── VisualFoxpro9Visitor.py └── VisualFoxpro9.g4 ├── Makefile ├── LICENSE ├── setup.py ├── extras ├── vfp2py.vim └── xbase_count ├── .gitignore └── README.md /test.h: -------------------------------------------------------------------------------- 1 | x = CHR(10) 2 | #DEFINE _SCREEN VFP_SCREEN 3 | -------------------------------------------------------------------------------- /testbed/__init__.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | from __future__ import absolute_import, division, print_function 3 | -------------------------------------------------------------------------------- /testbed/conversion.tokens: -------------------------------------------------------------------------------- 1 | FoxStart=1 2 | PyStart=2 3 | Line=3 4 | FoxEnd=4 5 | FoxLine=5 6 | PyEnd=6 7 | PyLine=7 8 | -------------------------------------------------------------------------------- /testbed/conversionLexer.tokens: -------------------------------------------------------------------------------- 1 | FoxStart=1 2 | PyStart=2 3 | Line=3 4 | FoxEnd=4 5 | FoxLine=5 6 | PyEnd=6 7 | PyLine=7 8 | -------------------------------------------------------------------------------- /vfp2py/__init__.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | from __future__ import absolute_import, division, print_function 3 | 4 | from . import vfpfunc 5 | from . import vfp2py 6 | -------------------------------------------------------------------------------- /testbed/conversion.g4: -------------------------------------------------------------------------------- 1 | parser grammar conversion; 2 | options { tokenVocab=conversionLexer; } 3 | 4 | conversionTests: conversionTest*; 5 | 6 | conversionTest: FoxStart FoxLine* FoxEnd PyStart PyLine* PyEnd; 7 | -------------------------------------------------------------------------------- /testbed/test_noconversion.py: -------------------------------------------------------------------------------- 1 | from vfp2py.vfpfunc import S, F, DB 2 | 3 | def scope_test(): 4 | S['test'] = 3 5 | print(S.test) 6 | print(DB.open_tables[:10]) 7 | assert S.test == 3 8 | S.test = 4 9 | assert S['test'] == 4 10 | -------------------------------------------------------------------------------- /vfp2py/Makefile: -------------------------------------------------------------------------------- 1 | test: VisualFoxpro9Lexer.py VisualFoxpro9Parser.py VisualFoxpro9Visitor.py vfp2py.py vfpfunc.py 2 | 3 | %Lexer.py %Parser.py %Visitor.py: %.g4 4 | ${Antlr} -visitor -no-listener -Dlanguage=Python${PyVer} $^ 5 | 6 | clean: 7 | rm -rf VisualFoxpro9*.py *.tokens *.pyc __pycache__ 8 | -------------------------------------------------------------------------------- /testbed/conversionLexer.g4: -------------------------------------------------------------------------------- 1 | lexer grammar conversionLexer; 2 | 3 | FoxStart: '@begin=vfp@' ('&&' [A-Za-z_0-9]*)? '\r'? '\n' -> pushMode(Fox); 4 | PyStart: '@begin=python@' '\r'? '\n' -> pushMode(Py); 5 | Line: .*? '\r'? '\n' -> skip; 6 | 7 | mode Fox; 8 | FoxEnd: '@end=vfp@' '\r'? '\n' -> popMode; 9 | FoxLine: .*? '\r'? '\n'; 10 | 11 | mode Py; 12 | PyEnd: '@end=python@' '\r'? '\n' -> popMode; 13 | PyLine: .*? '\r'? '\n'; 14 | -------------------------------------------------------------------------------- /testbed/Makefile: -------------------------------------------------------------------------------- 1 | all: conversion.vfp2py conversionLexer.py conversion.py conversionVisitor.py conversionLexer.g4 conversion.g4 test_conversion.py test_lib.py 2 | 3 | test_conversion.py: __main__.py conversion.vfp2py 4 | python __main__.py conversion.vfp2py > $@ 5 | 6 | test_lib.py: test_lib.prg 7 | python -m vfp2py test_lib.prg test_lib.py 8 | 9 | .DELETE_ON_ERROR: 10 | 11 | %Lexer.py: %Lexer.g4 12 | ${Antlr} -Dlanguage=Python${PyVer} $^ 13 | 14 | %.py: %Lexer.g4 %.g4 15 | ${Antlr} -visitor -no-listener -Dlanguage=Python${PyVer} $^ 16 | 17 | clean: 18 | rm -rf conversion*.py *.tokens *.pyc __pycache__ test_conversion.py test_lib.py 19 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | AntlrJar=antlr-4.11.1-complete.jar 2 | Antlr=java -jar $(shell realpath $(AntlrJar)) 3 | PyVer=$(shell python -c 'import sys; print(sys.version_info[0])') 4 | 5 | all: ${AntlrJar} vfp2py.egg-info 6 | make -C vfp2py Antlr="${Antlr}" PyVer=${PyVer} 7 | make -C testbed Antlr="${Antlr}" PyVer=${PyVer} 8 | python setup.py test 9 | 10 | vfp2py.egg-info: 11 | python setup.py develop 12 | 13 | antlr%.jar: 14 | wget http://www.antlr.org/download/$@ 15 | 16 | install-vim-syntax: extras/vfp2py.vim 17 | mkdir -p ~/.vim/syntax 18 | cp $^ ~/.vim/syntax 19 | 20 | clean: 21 | rm -rf ${AntlrJar} vfp2py.egg-info 22 | make -C vfp2py clean 23 | make -C testbed clean 24 | -------------------------------------------------------------------------------- /testbed/conversionVisitor.py: -------------------------------------------------------------------------------- 1 | # Generated from conversion.g4 by ANTLR 4.11.1 2 | from antlr4 import * 3 | if __name__ is not None and "." in __name__: 4 | from .conversion import conversion 5 | else: 6 | from conversion import conversion 7 | 8 | # This class defines a complete generic visitor for a parse tree produced by conversion. 9 | 10 | class conversionVisitor(ParseTreeVisitor): 11 | 12 | # Visit a parse tree produced by conversion#conversionTests. 13 | def visitConversionTests(self, ctx:conversion.ConversionTestsContext): 14 | return self.visitChildren(ctx) 15 | 16 | 17 | # Visit a parse tree produced by conversion#conversionTest. 18 | def visitConversionTest(self, ctx:conversion.ConversionTestContext): 19 | return self.visitChildren(ctx) 20 | 21 | 22 | 23 | del conversion -------------------------------------------------------------------------------- /testbed/conversion.interp: -------------------------------------------------------------------------------- 1 | token literal names: 2 | null 3 | null 4 | null 5 | null 6 | null 7 | null 8 | null 9 | null 10 | 11 | token symbolic names: 12 | null 13 | FoxStart 14 | PyStart 15 | Line 16 | FoxEnd 17 | FoxLine 18 | PyEnd 19 | PyLine 20 | 21 | rule names: 22 | conversionTests 23 | conversionTest 24 | 25 | 26 | atn: 27 | [4, 1, 7, 28, 2, 0, 7, 0, 2, 1, 7, 1, 1, 0, 5, 0, 6, 8, 0, 10, 0, 12, 0, 9, 9, 0, 1, 1, 1, 1, 5, 1, 13, 8, 1, 10, 1, 12, 1, 16, 9, 1, 1, 1, 1, 1, 1, 1, 5, 1, 21, 8, 1, 10, 1, 12, 1, 24, 9, 1, 1, 1, 1, 1, 1, 1, 0, 0, 2, 0, 2, 0, 0, 28, 0, 7, 1, 0, 0, 0, 2, 10, 1, 0, 0, 0, 4, 6, 3, 2, 1, 0, 5, 4, 1, 0, 0, 0, 6, 9, 1, 0, 0, 0, 7, 5, 1, 0, 0, 0, 7, 8, 1, 0, 0, 0, 8, 1, 1, 0, 0, 0, 9, 7, 1, 0, 0, 0, 10, 14, 5, 1, 0, 0, 11, 13, 5, 5, 0, 0, 12, 11, 1, 0, 0, 0, 13, 16, 1, 0, 0, 0, 14, 12, 1, 0, 0, 0, 14, 15, 1, 0, 0, 0, 15, 17, 1, 0, 0, 0, 16, 14, 1, 0, 0, 0, 17, 18, 5, 4, 0, 0, 18, 22, 5, 2, 0, 0, 19, 21, 5, 7, 0, 0, 20, 19, 1, 0, 0, 0, 21, 24, 1, 0, 0, 0, 22, 20, 1, 0, 0, 0, 22, 23, 1, 0, 0, 0, 23, 25, 1, 0, 0, 0, 24, 22, 1, 0, 0, 0, 25, 26, 5, 6, 0, 0, 26, 3, 1, 0, 0, 0, 3, 7, 14, 22] -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 Michael Wisslead 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | from __future__ import absolute_import, division, print_function 3 | 4 | import sys 5 | from setuptools import setup 6 | 7 | VERSION='0.1.1' 8 | 9 | setup( 10 | name='vfp2py', 11 | version=VERSION, 12 | description='Convert foxpro code to python', 13 | author='Michael Wisslead', 14 | author_email='michael.wisslead@gmail.com', 15 | url='https://github.com/mwisslead/vfp2py', 16 | packages=['vfp2py'], 17 | classifiers=[ 18 | "Intended Audience :: Developers", 19 | "License :: OSI Approved :: MIT License", 20 | "Operating System :: OS Independent", 21 | "Programming Language :: Python", 22 | "Topic :: Software Development :: Libraries :: Python Modules", 23 | ], 24 | install_requires=[ 25 | 'antlr4-python3-runtime==4.11.1', 26 | 'dbf', 27 | 'autopep8', 28 | 'isort<5', 29 | 'python-dateutil', 30 | 'pyodbc' 31 | ], 32 | test_suite='nose.collector', 33 | tests_require=['nose', 'Faker'], 34 | entry_points = { 35 | 'console_scripts': ['vfp2py=vfp2py.__main__:main'], 36 | } 37 | ) 38 | -------------------------------------------------------------------------------- /extras/vfp2py.vim: -------------------------------------------------------------------------------- 1 | syntax match genComment ".*" 2 | hi def link genComment Comment 3 | 4 | "from http://vim.wikia.com/wiki/Different_syntax_highlighting_within_regions_of_a_file 5 | function! TextEnableCodeSnip(filetype,start,end,textSnipHl) abort 6 | let ft=toupper(a:filetype) 7 | let group='textGroup'.ft 8 | if exists('b:current_syntax') 9 | let s:current_syntax=b:current_syntax 10 | " Remove current syntax definition, as some syntax files (e.g. cpp.vim) 11 | " do nothing if b:current_syntax is defined. 12 | unlet b:current_syntax 13 | endif 14 | execute 'syntax include @'.group.' syntax/'.a:filetype.'.vim' 15 | try 16 | execute 'syntax include @'.group.' after/syntax/'.a:filetype.'.vim' 17 | catch 18 | endtry 19 | if exists('s:current_syntax') 20 | let b:current_syntax=s:current_syntax 21 | else 22 | unlet b:current_syntax 23 | endif 24 | execute 'syntax region textSnip'.ft.' 25 | \ matchgroup='.a:textSnipHl.' 26 | \ start="'.a:start.'" end="'.a:end.'" 27 | \ contains=@'.group 28 | endfunction 29 | 30 | call TextEnableCodeSnip( 'vfp9', '@begin=vfp@', '@end=vfp@', 'SpecialComment') 31 | call TextEnableCodeSnip('python', '@begin=python@', '@end=python@', 'SpecialComment') 32 | -------------------------------------------------------------------------------- /extras/xbase_count: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | from __future__ import print_function, unicode_literals 3 | 4 | '''Count lines in xbase source files''' 5 | '''Used by sloccount''' 6 | 7 | import sys 8 | import argparse 9 | import re 10 | 11 | def parse_args(argv=None): 12 | parser = argparse.ArgumentParser(description='Count lines in xbase programs') 13 | parser.add_argument("-f", metavar=('file',), nargs=1, type=str) 14 | parser.add_argument("files", type=str, nargs='*') 15 | return parser.parse_args(argv) 16 | 17 | def countline(line): 18 | line = line.strip() 19 | return line and not (line.startswith('*') or line.startswith('&&') or re.match(r'^[Ee][Nn][Dd][A-Za-z]*$', line)) 20 | 21 | def main(argv=None): 22 | encoding = 'cp1252' 23 | args = parse_args(argv) 24 | files = [] 25 | if args.f: 26 | args.f = args.f[0] 27 | if args.f == '-': 28 | files += sys.stdin.readlines() 29 | else: 30 | with open(args.f, 'rb') as fid: 31 | files += fid.readlines() 32 | files += args.files 33 | total=0 34 | for filename in files: 35 | filename = filename.strip() 36 | with open(filename, 'rb') as fid: 37 | lines = len([None for line in fid.readlines() if countline(line.decode(encoding))]) 38 | print('{} {}'.format(lines, filename)) 39 | total += lines 40 | 41 | print('Total:\n{}'.format(total)) 42 | 43 | if __name__ == '__main__': 44 | main() 45 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | env/ 12 | build/ 13 | develop-eggs/ 14 | dist/ 15 | downloads/ 16 | eggs/ 17 | .eggs/ 18 | lib/ 19 | lib64/ 20 | parts/ 21 | sdist/ 22 | var/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | 27 | # PyInstaller 28 | # Usually these files are written by a python script from a template 29 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 30 | *.manifest 31 | *.spec 32 | 33 | # Installer logs 34 | pip-log.txt 35 | pip-delete-this-directory.txt 36 | 37 | # Unit test / coverage reports 38 | htmlcov/ 39 | .tox/ 40 | .coverage 41 | .coverage.* 42 | .cache 43 | nosetests.xml 44 | coverage.xml 45 | *,cover 46 | .hypothesis/ 47 | 48 | # Translations 49 | *.mo 50 | *.pot 51 | 52 | # Django stuff: 53 | *.log 54 | local_settings.py 55 | 56 | # Flask stuff: 57 | instance/ 58 | .webassets-cache 59 | 60 | # Scrapy stuff: 61 | .scrapy 62 | 63 | # Sphinx documentation 64 | docs/_build/ 65 | 66 | # PyBuilder 67 | target/ 68 | 69 | # IPython Notebook 70 | .ipynb_checkpoints 71 | 72 | # pyenv 73 | .python-version 74 | 75 | # celery beat schedule file 76 | celerybeat-schedule 77 | 78 | # dotenv 79 | .env 80 | 81 | # virtualenv 82 | venv/ 83 | ENV/ 84 | 85 | # Spyder project settings 86 | .spyderproject 87 | 88 | # Rope project settings 89 | .ropeproject 90 | 91 | VisualFoxpro9*.tokens 92 | test.py 93 | *.dbf 94 | *.diff 95 | *.rej 96 | *.orig 97 | -------------------------------------------------------------------------------- /vfp2py/__main__.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | from __future__ import absolute_import, division, print_function 3 | 4 | import argparse 5 | import logging 6 | 7 | from . import vfp2py 8 | 9 | def parse_args(argv=None): 10 | parser = argparse.ArgumentParser(description='Tool for rewriting Foxpro code in Python') 11 | parser.add_argument("--logging", help="output logging information", action='store_true') 12 | parser.add_argument("--profile", help="turn on profiling", action='store_true') 13 | parser.add_argument("--encoding", help="set file encoding", default="cp1252") 14 | parser.add_argument("infile", help="file to convert - supported file types are prg, mpr, spr, scx, vcx, or pjx,", type=str) 15 | parser.add_argument("outpath", help="path to output converted code, will be a filename for all but pjx which will be a directory", type=str) 16 | parser.add_argument("search", help="directory to search for included files", type=str, nargs='*') 17 | return parser.parse_args(argv) 18 | 19 | def main(argv=None): 20 | args = parse_args(argv) 21 | if args.logging: 22 | logging.basicConfig(level=logging.DEBUG) 23 | vfp2py.SEARCH_PATH += args.search 24 | if args.profile: 25 | import cProfile 26 | cProfile.runctx('vfp2py.convert_file(args.infile, args.outpath)', globals(), locals()) 27 | else: 28 | vfp2py.convert_file(args.infile, args.outpath, encoding=args.encoding) 29 | 30 | if __name__ == '__main__': 31 | try: 32 | main() 33 | except KeyboardInterrupt: 34 | pass 35 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # vfp2py 2 | vfp2py is an attempt to create a tool to automatically create python code from 3 | foxpro 9 code. 4 | 5 | Many but not nearly all commands are currently supported with missing commands 6 | to be added as need arises. Conversion can be done on individual prg files or 7 | whole projects pjx files. 8 | 9 | ## Features 10 | - Translates code comments 11 | - Handles preprocessor commands 12 | - Support for reading and writing dbf files via Ethan Furman's dbf package 13 | - Many functions are inlined in the generated python code. 14 | - Many complex functions and commands are available through a runtime - vfpfunc.py 15 | - Somewhat functioning gui using PySide 16 | 17 | ## Future work 18 | - Add more commands to parser 19 | - Improve gui 20 | - Rework scoping to facilitate operation of some commands. 21 | - Add missing code conversion for some commands currently supported by parser 22 | - Add more runtime functions 23 | - Put package on pypi for easier install 24 | - Speed up parsing 25 | 26 | ## Installation 27 | `python -m pip install vfp2py` 28 | 29 | ## Usage 30 | ``` 31 | $ vfp2py --help 32 | usage: vfp2py [-h] [--logging] infile outpath [search [search ...]] 33 | 34 | Tool for rewriting Foxpro code in Python 35 | 36 | positional arguments: 37 | infile file to convert - supported file types are prg, mpr, spr, scx, 38 | vcx, or pjx, 39 | outpath path to output converted code, will be a filename for all but 40 | pjx which will be a directory 41 | search directory to search for included files 42 | 43 | optional arguments: 44 | -h, --help show this help message and exit 45 | --logging file to convert 46 | ``` 47 | 48 | To convert a file simply run `vfp2py --logging input_file.prg output_file.py` or `vfp2py --logging input_project.pjx output_directory` 49 | 50 | ### Acknowledgments 51 | Jayanta Narayan Choudhuri for providing a list of keyword and function abbreviations. 52 | -------------------------------------------------------------------------------- /testbed/__main__.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | from __future__ import absolute_import, division, print_function 3 | 4 | import argparse 5 | 6 | import antlr4 7 | 8 | from conversion import * 9 | from conversionLexer import * 10 | from conversionVisitor import * 11 | 12 | 13 | def add_indent(code, level): 14 | retval = '' 15 | for line in code: 16 | if isinstance(line, list): 17 | retval += add_indent(line, level+1) 18 | else: 19 | retval += ' '*level + line + '\n' 20 | return retval 21 | 22 | 23 | def docstring(string): 24 | return "'''\n{}'''".format(string.replace('\\', '\\\\').replace('\'', '\\\'')) 25 | 26 | 27 | class TestsGenVisitor(conversionVisitor): 28 | '''Visitor that extracts conversion sections''' 29 | 30 | def visitConversionTests(self, ctx): 31 | t = ''' 32 | ! from __future__ import print_function 33 | ! 34 | ! import difflib 35 | ! 36 | ! import vfp2py 37 | '''.split('! ')[1:] 38 | for i, test in enumerate(ctx.conversionTest()): 39 | foxlines, pylines = self.visit(test) 40 | test_func = ''' 41 | ! 42 | ! 43 | ! def Test{}(): 44 | ! input_str = {}.strip() 45 | ! output_str = {}.strip() 46 | ! test_output_str = {}.strip() 47 | ! try: 48 | ! assert test_output_str == output_str 49 | ! except AssertionError: 50 | ! diff = difflib.unified_diff((test_output_str + '\\n').splitlines(1), (output_str + '\\n').splitlines(1)) 51 | ! print(''.join(diff)) 52 | ! raise 53 | ''' 54 | special_directive = str(test.FoxStart().symbol.text[13:].strip()) 55 | if special_directive: 56 | test_output = 'vfp2py.vfp2py.prg2py(input_str, \'cp1252\', parser_start={}, prepend_data=\'\')'.format(repr(special_directive)) 57 | else: 58 | test_output = 'vfp2py.vfp2py.prg2py(input_str, \'cp1252\')' 59 | test_func = test_func.format(i, docstring(foxlines), docstring(pylines), test_output) 60 | t += test_func.split('! ')[1:] 61 | 62 | t = [l.rstrip() for l in t] 63 | 64 | return add_indent(t, 0) 65 | 66 | 67 | def visitConversionTest(self, ctx): 68 | foxlines = ''.join(tok.symbol.text for tok in ctx.FoxLine()) 69 | pylines = ''.join(tok.symbol.text for tok in ctx.PyLine()) 70 | return foxlines, pylines 71 | 72 | 73 | def generate_tests(filename): 74 | with open(filename, 'rb') as fid: 75 | file_contents = fid.read().decode('utf-8') 76 | input_stream = antlr4.InputStream(file_contents) 77 | lexer = conversionLexer(input_stream) 78 | stream = antlr4.CommonTokenStream(lexer) 79 | parser = conversion(stream) 80 | tree = parser.conversionTests() 81 | visitor = TestsGenVisitor() 82 | return visitor.visit(tree) 83 | 84 | 85 | def parse_args(argv=None): 86 | parser = argparse.ArgumentParser(description='Tool for generating vfp to python conversion tests from conversion file') 87 | parser.add_argument("infile", help="file of conversions", type=str) 88 | return parser.parse_args(argv) 89 | 90 | 91 | def main(argv=None): 92 | args = parse_args(argv) 93 | print(generate_tests(args.infile)) 94 | 95 | 96 | if __name__ == '__main__': 97 | try: 98 | main() 99 | except KeyboardInterrupt: 100 | pass 101 | -------------------------------------------------------------------------------- /testbed/conversionLexer.interp: -------------------------------------------------------------------------------- 1 | token literal names: 2 | null 3 | null 4 | null 5 | null 6 | null 7 | null 8 | null 9 | null 10 | 11 | token symbolic names: 12 | null 13 | FoxStart 14 | PyStart 15 | Line 16 | FoxEnd 17 | FoxLine 18 | PyEnd 19 | PyLine 20 | 21 | rule names: 22 | FoxStart 23 | PyStart 24 | Line 25 | FoxEnd 26 | FoxLine 27 | PyEnd 28 | PyLine 29 | 30 | channel names: 31 | DEFAULT_TOKEN_CHANNEL 32 | HIDDEN 33 | 34 | mode names: 35 | DEFAULT_MODE 36 | Fox 37 | Py 38 | 39 | atn: 40 | [4, 0, 7, 141, 6, -1, 6, -1, 6, -1, 2, 0, 7, 0, 2, 1, 7, 1, 2, 2, 7, 2, 2, 3, 7, 3, 2, 4, 7, 4, 2, 5, 7, 5, 2, 6, 7, 6, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 5, 0, 34, 8, 0, 10, 0, 12, 0, 37, 9, 0, 3, 0, 39, 8, 0, 1, 0, 3, 0, 42, 8, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, 64, 8, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 5, 2, 71, 8, 2, 10, 2, 12, 2, 74, 9, 2, 1, 2, 3, 2, 77, 8, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 3, 3, 94, 8, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 4, 5, 4, 101, 8, 4, 10, 4, 12, 4, 104, 9, 4, 1, 4, 3, 4, 107, 8, 4, 1, 4, 1, 4, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 3, 5, 125, 8, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 6, 5, 6, 132, 8, 6, 10, 6, 12, 6, 135, 9, 6, 1, 6, 3, 6, 138, 8, 6, 1, 6, 1, 6, 3, 72, 102, 133, 0, 7, 3, 1, 5, 2, 7, 3, 9, 4, 11, 5, 13, 6, 15, 7, 3, 0, 1, 2, 1, 4, 0, 48, 57, 65, 90, 95, 95, 97, 122, 150, 0, 3, 1, 0, 0, 0, 0, 5, 1, 0, 0, 0, 0, 7, 1, 0, 0, 0, 1, 9, 1, 0, 0, 0, 1, 11, 1, 0, 0, 0, 2, 13, 1, 0, 0, 0, 2, 15, 1, 0, 0, 0, 3, 17, 1, 0, 0, 0, 5, 47, 1, 0, 0, 0, 7, 72, 1, 0, 0, 0, 9, 82, 1, 0, 0, 0, 11, 102, 1, 0, 0, 0, 13, 110, 1, 0, 0, 0, 15, 133, 1, 0, 0, 0, 17, 18, 5, 64, 0, 0, 18, 19, 5, 98, 0, 0, 19, 20, 5, 101, 0, 0, 20, 21, 5, 103, 0, 0, 21, 22, 5, 105, 0, 0, 22, 23, 5, 110, 0, 0, 23, 24, 5, 61, 0, 0, 24, 25, 5, 118, 0, 0, 25, 26, 5, 102, 0, 0, 26, 27, 5, 112, 0, 0, 27, 28, 5, 64, 0, 0, 28, 38, 1, 0, 0, 0, 29, 30, 5, 38, 0, 0, 30, 31, 5, 38, 0, 0, 31, 35, 1, 0, 0, 0, 32, 34, 7, 0, 0, 0, 33, 32, 1, 0, 0, 0, 34, 37, 1, 0, 0, 0, 35, 33, 1, 0, 0, 0, 35, 36, 1, 0, 0, 0, 36, 39, 1, 0, 0, 0, 37, 35, 1, 0, 0, 0, 38, 29, 1, 0, 0, 0, 38, 39, 1, 0, 0, 0, 39, 41, 1, 0, 0, 0, 40, 42, 5, 13, 0, 0, 41, 40, 1, 0, 0, 0, 41, 42, 1, 0, 0, 0, 42, 43, 1, 0, 0, 0, 43, 44, 5, 10, 0, 0, 44, 45, 1, 0, 0, 0, 45, 46, 6, 0, 0, 0, 46, 4, 1, 0, 0, 0, 47, 48, 5, 64, 0, 0, 48, 49, 5, 98, 0, 0, 49, 50, 5, 101, 0, 0, 50, 51, 5, 103, 0, 0, 51, 52, 5, 105, 0, 0, 52, 53, 5, 110, 0, 0, 53, 54, 5, 61, 0, 0, 54, 55, 5, 112, 0, 0, 55, 56, 5, 121, 0, 0, 56, 57, 5, 116, 0, 0, 57, 58, 5, 104, 0, 0, 58, 59, 5, 111, 0, 0, 59, 60, 5, 110, 0, 0, 60, 61, 5, 64, 0, 0, 61, 63, 1, 0, 0, 0, 62, 64, 5, 13, 0, 0, 63, 62, 1, 0, 0, 0, 63, 64, 1, 0, 0, 0, 64, 65, 1, 0, 0, 0, 65, 66, 5, 10, 0, 0, 66, 67, 1, 0, 0, 0, 67, 68, 6, 1, 1, 0, 68, 6, 1, 0, 0, 0, 69, 71, 9, 0, 0, 0, 70, 69, 1, 0, 0, 0, 71, 74, 1, 0, 0, 0, 72, 73, 1, 0, 0, 0, 72, 70, 1, 0, 0, 0, 73, 76, 1, 0, 0, 0, 74, 72, 1, 0, 0, 0, 75, 77, 5, 13, 0, 0, 76, 75, 1, 0, 0, 0, 76, 77, 1, 0, 0, 0, 77, 78, 1, 0, 0, 0, 78, 79, 5, 10, 0, 0, 79, 80, 1, 0, 0, 0, 80, 81, 6, 2, 2, 0, 81, 8, 1, 0, 0, 0, 82, 83, 5, 64, 0, 0, 83, 84, 5, 101, 0, 0, 84, 85, 5, 110, 0, 0, 85, 86, 5, 100, 0, 0, 86, 87, 5, 61, 0, 0, 87, 88, 5, 118, 0, 0, 88, 89, 5, 102, 0, 0, 89, 90, 5, 112, 0, 0, 90, 91, 5, 64, 0, 0, 91, 93, 1, 0, 0, 0, 92, 94, 5, 13, 0, 0, 93, 92, 1, 0, 0, 0, 93, 94, 1, 0, 0, 0, 94, 95, 1, 0, 0, 0, 95, 96, 5, 10, 0, 0, 96, 97, 1, 0, 0, 0, 97, 98, 6, 3, 3, 0, 98, 10, 1, 0, 0, 0, 99, 101, 9, 0, 0, 0, 100, 99, 1, 0, 0, 0, 101, 104, 1, 0, 0, 0, 102, 103, 1, 0, 0, 0, 102, 100, 1, 0, 0, 0, 103, 106, 1, 0, 0, 0, 104, 102, 1, 0, 0, 0, 105, 107, 5, 13, 0, 0, 106, 105, 1, 0, 0, 0, 106, 107, 1, 0, 0, 0, 107, 108, 1, 0, 0, 0, 108, 109, 5, 10, 0, 0, 109, 12, 1, 0, 0, 0, 110, 111, 5, 64, 0, 0, 111, 112, 5, 101, 0, 0, 112, 113, 5, 110, 0, 0, 113, 114, 5, 100, 0, 0, 114, 115, 5, 61, 0, 0, 115, 116, 5, 112, 0, 0, 116, 117, 5, 121, 0, 0, 117, 118, 5, 116, 0, 0, 118, 119, 5, 104, 0, 0, 119, 120, 5, 111, 0, 0, 120, 121, 5, 110, 0, 0, 121, 122, 5, 64, 0, 0, 122, 124, 1, 0, 0, 0, 123, 125, 5, 13, 0, 0, 124, 123, 1, 0, 0, 0, 124, 125, 1, 0, 0, 0, 125, 126, 1, 0, 0, 0, 126, 127, 5, 10, 0, 0, 127, 128, 1, 0, 0, 0, 128, 129, 6, 5, 3, 0, 129, 14, 1, 0, 0, 0, 130, 132, 9, 0, 0, 0, 131, 130, 1, 0, 0, 0, 132, 135, 1, 0, 0, 0, 133, 134, 1, 0, 0, 0, 133, 131, 1, 0, 0, 0, 134, 137, 1, 0, 0, 0, 135, 133, 1, 0, 0, 0, 136, 138, 5, 13, 0, 0, 137, 136, 1, 0, 0, 0, 137, 138, 1, 0, 0, 0, 138, 139, 1, 0, 0, 0, 139, 140, 5, 10, 0, 0, 140, 16, 1, 0, 0, 0, 15, 0, 1, 2, 35, 38, 41, 63, 72, 76, 93, 102, 106, 124, 133, 137, 4, 5, 1, 0, 5, 2, 0, 6, 0, 0, 4, 0, 0] -------------------------------------------------------------------------------- /testbed/conversionLexer.py: -------------------------------------------------------------------------------- 1 | # Generated from conversionLexer.g4 by ANTLR 4.11.1 2 | from antlr4 import * 3 | from io import StringIO 4 | import sys 5 | if sys.version_info[1] > 5: 6 | from typing import TextIO 7 | else: 8 | from typing.io import TextIO 9 | 10 | 11 | def serializedATN(): 12 | return [ 13 | 4,0,7,141,6,-1,6,-1,6,-1,2,0,7,0,2,1,7,1,2,2,7,2,2,3,7,3,2,4,7,4, 14 | 2,5,7,5,2,6,7,6,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0, 15 | 1,0,1,0,1,0,1,0,5,0,34,8,0,10,0,12,0,37,9,0,3,0,39,8,0,1,0,3,0,42, 16 | 8,0,1,0,1,0,1,0,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 17 | 1,1,1,1,1,1,1,1,1,1,3,1,64,8,1,1,1,1,1,1,1,1,1,1,2,5,2,71,8,2,10, 18 | 2,12,2,74,9,2,1,2,3,2,77,8,2,1,2,1,2,1,2,1,2,1,3,1,3,1,3,1,3,1,3, 19 | 1,3,1,3,1,3,1,3,1,3,1,3,3,3,94,8,3,1,3,1,3,1,3,1,3,1,4,5,4,101,8, 20 | 4,10,4,12,4,104,9,4,1,4,3,4,107,8,4,1,4,1,4,1,5,1,5,1,5,1,5,1,5, 21 | 1,5,1,5,1,5,1,5,1,5,1,5,1,5,1,5,1,5,3,5,125,8,5,1,5,1,5,1,5,1,5, 22 | 1,6,5,6,132,8,6,10,6,12,6,135,9,6,1,6,3,6,138,8,6,1,6,1,6,3,72,102, 23 | 133,0,7,3,1,5,2,7,3,9,4,11,5,13,6,15,7,3,0,1,2,1,4,0,48,57,65,90, 24 | 95,95,97,122,150,0,3,1,0,0,0,0,5,1,0,0,0,0,7,1,0,0,0,1,9,1,0,0,0, 25 | 1,11,1,0,0,0,2,13,1,0,0,0,2,15,1,0,0,0,3,17,1,0,0,0,5,47,1,0,0,0, 26 | 7,72,1,0,0,0,9,82,1,0,0,0,11,102,1,0,0,0,13,110,1,0,0,0,15,133,1, 27 | 0,0,0,17,18,5,64,0,0,18,19,5,98,0,0,19,20,5,101,0,0,20,21,5,103, 28 | 0,0,21,22,5,105,0,0,22,23,5,110,0,0,23,24,5,61,0,0,24,25,5,118,0, 29 | 0,25,26,5,102,0,0,26,27,5,112,0,0,27,28,5,64,0,0,28,38,1,0,0,0,29, 30 | 30,5,38,0,0,30,31,5,38,0,0,31,35,1,0,0,0,32,34,7,0,0,0,33,32,1,0, 31 | 0,0,34,37,1,0,0,0,35,33,1,0,0,0,35,36,1,0,0,0,36,39,1,0,0,0,37,35, 32 | 1,0,0,0,38,29,1,0,0,0,38,39,1,0,0,0,39,41,1,0,0,0,40,42,5,13,0,0, 33 | 41,40,1,0,0,0,41,42,1,0,0,0,42,43,1,0,0,0,43,44,5,10,0,0,44,45,1, 34 | 0,0,0,45,46,6,0,0,0,46,4,1,0,0,0,47,48,5,64,0,0,48,49,5,98,0,0,49, 35 | 50,5,101,0,0,50,51,5,103,0,0,51,52,5,105,0,0,52,53,5,110,0,0,53, 36 | 54,5,61,0,0,54,55,5,112,0,0,55,56,5,121,0,0,56,57,5,116,0,0,57,58, 37 | 5,104,0,0,58,59,5,111,0,0,59,60,5,110,0,0,60,61,5,64,0,0,61,63,1, 38 | 0,0,0,62,64,5,13,0,0,63,62,1,0,0,0,63,64,1,0,0,0,64,65,1,0,0,0,65, 39 | 66,5,10,0,0,66,67,1,0,0,0,67,68,6,1,1,0,68,6,1,0,0,0,69,71,9,0,0, 40 | 0,70,69,1,0,0,0,71,74,1,0,0,0,72,73,1,0,0,0,72,70,1,0,0,0,73,76, 41 | 1,0,0,0,74,72,1,0,0,0,75,77,5,13,0,0,76,75,1,0,0,0,76,77,1,0,0,0, 42 | 77,78,1,0,0,0,78,79,5,10,0,0,79,80,1,0,0,0,80,81,6,2,2,0,81,8,1, 43 | 0,0,0,82,83,5,64,0,0,83,84,5,101,0,0,84,85,5,110,0,0,85,86,5,100, 44 | 0,0,86,87,5,61,0,0,87,88,5,118,0,0,88,89,5,102,0,0,89,90,5,112,0, 45 | 0,90,91,5,64,0,0,91,93,1,0,0,0,92,94,5,13,0,0,93,92,1,0,0,0,93,94, 46 | 1,0,0,0,94,95,1,0,0,0,95,96,5,10,0,0,96,97,1,0,0,0,97,98,6,3,3,0, 47 | 98,10,1,0,0,0,99,101,9,0,0,0,100,99,1,0,0,0,101,104,1,0,0,0,102, 48 | 103,1,0,0,0,102,100,1,0,0,0,103,106,1,0,0,0,104,102,1,0,0,0,105, 49 | 107,5,13,0,0,106,105,1,0,0,0,106,107,1,0,0,0,107,108,1,0,0,0,108, 50 | 109,5,10,0,0,109,12,1,0,0,0,110,111,5,64,0,0,111,112,5,101,0,0,112, 51 | 113,5,110,0,0,113,114,5,100,0,0,114,115,5,61,0,0,115,116,5,112,0, 52 | 0,116,117,5,121,0,0,117,118,5,116,0,0,118,119,5,104,0,0,119,120, 53 | 5,111,0,0,120,121,5,110,0,0,121,122,5,64,0,0,122,124,1,0,0,0,123, 54 | 125,5,13,0,0,124,123,1,0,0,0,124,125,1,0,0,0,125,126,1,0,0,0,126, 55 | 127,5,10,0,0,127,128,1,0,0,0,128,129,6,5,3,0,129,14,1,0,0,0,130, 56 | 132,9,0,0,0,131,130,1,0,0,0,132,135,1,0,0,0,133,134,1,0,0,0,133, 57 | 131,1,0,0,0,134,137,1,0,0,0,135,133,1,0,0,0,136,138,5,13,0,0,137, 58 | 136,1,0,0,0,137,138,1,0,0,0,138,139,1,0,0,0,139,140,5,10,0,0,140, 59 | 16,1,0,0,0,15,0,1,2,35,38,41,63,72,76,93,102,106,124,133,137,4,5, 60 | 1,0,5,2,0,6,0,0,4,0,0 61 | ] 62 | 63 | class conversionLexer(Lexer): 64 | 65 | atn = ATNDeserializer().deserialize(serializedATN()) 66 | 67 | decisionsToDFA = [ DFA(ds, i) for i, ds in enumerate(atn.decisionToState) ] 68 | 69 | Fox = 1 70 | Py = 2 71 | 72 | FoxStart = 1 73 | PyStart = 2 74 | Line = 3 75 | FoxEnd = 4 76 | FoxLine = 5 77 | PyEnd = 6 78 | PyLine = 7 79 | 80 | channelNames = [ u"DEFAULT_TOKEN_CHANNEL", u"HIDDEN" ] 81 | 82 | modeNames = [ "DEFAULT_MODE", "Fox", "Py" ] 83 | 84 | literalNames = [ "", 85 | ] 86 | 87 | symbolicNames = [ "", 88 | "FoxStart", "PyStart", "Line", "FoxEnd", "FoxLine", "PyEnd", 89 | "PyLine" ] 90 | 91 | ruleNames = [ "FoxStart", "PyStart", "Line", "FoxEnd", "FoxLine", "PyEnd", 92 | "PyLine" ] 93 | 94 | grammarFileName = "conversionLexer.g4" 95 | 96 | def __init__(self, input=None, output:TextIO = sys.stdout): 97 | super().__init__(input, output) 98 | self.checkVersion("4.11.1") 99 | self._interp = LexerATNSimulator(self, self.atn, self.decisionsToDFA, PredictionContextCache()) 100 | self._actions = None 101 | self._predicates = None 102 | 103 | 104 | -------------------------------------------------------------------------------- /testbed/conversion.py: -------------------------------------------------------------------------------- 1 | # Generated from conversion.g4 by ANTLR 4.11.1 2 | # encoding: utf-8 3 | from antlr4 import * 4 | from io import StringIO 5 | import sys 6 | if sys.version_info[1] > 5: 7 | from typing import TextIO 8 | else: 9 | from typing.io import TextIO 10 | 11 | def serializedATN(): 12 | return [ 13 | 4,1,7,28,2,0,7,0,2,1,7,1,1,0,5,0,6,8,0,10,0,12,0,9,9,0,1,1,1,1,5, 14 | 1,13,8,1,10,1,12,1,16,9,1,1,1,1,1,1,1,5,1,21,8,1,10,1,12,1,24,9, 15 | 1,1,1,1,1,1,1,0,0,2,0,2,0,0,28,0,7,1,0,0,0,2,10,1,0,0,0,4,6,3,2, 16 | 1,0,5,4,1,0,0,0,6,9,1,0,0,0,7,5,1,0,0,0,7,8,1,0,0,0,8,1,1,0,0,0, 17 | 9,7,1,0,0,0,10,14,5,1,0,0,11,13,5,5,0,0,12,11,1,0,0,0,13,16,1,0, 18 | 0,0,14,12,1,0,0,0,14,15,1,0,0,0,15,17,1,0,0,0,16,14,1,0,0,0,17,18, 19 | 5,4,0,0,18,22,5,2,0,0,19,21,5,7,0,0,20,19,1,0,0,0,21,24,1,0,0,0, 20 | 22,20,1,0,0,0,22,23,1,0,0,0,23,25,1,0,0,0,24,22,1,0,0,0,25,26,5, 21 | 6,0,0,26,3,1,0,0,0,3,7,14,22 22 | ] 23 | 24 | class conversion ( Parser ): 25 | 26 | grammarFileName = "conversion.g4" 27 | 28 | atn = ATNDeserializer().deserialize(serializedATN()) 29 | 30 | decisionsToDFA = [ DFA(ds, i) for i, ds in enumerate(atn.decisionToState) ] 31 | 32 | sharedContextCache = PredictionContextCache() 33 | 34 | literalNames = [ ] 35 | 36 | symbolicNames = [ "", "FoxStart", "PyStart", "Line", "FoxEnd", 37 | "FoxLine", "PyEnd", "PyLine" ] 38 | 39 | RULE_conversionTests = 0 40 | RULE_conversionTest = 1 41 | 42 | ruleNames = [ "conversionTests", "conversionTest" ] 43 | 44 | EOF = Token.EOF 45 | FoxStart=1 46 | PyStart=2 47 | Line=3 48 | FoxEnd=4 49 | FoxLine=5 50 | PyEnd=6 51 | PyLine=7 52 | 53 | def __init__(self, input:TokenStream, output:TextIO = sys.stdout): 54 | super().__init__(input, output) 55 | self.checkVersion("4.11.1") 56 | self._interp = ParserATNSimulator(self, self.atn, self.decisionsToDFA, self.sharedContextCache) 57 | self._predicates = None 58 | 59 | 60 | 61 | 62 | class ConversionTestsContext(ParserRuleContext): 63 | __slots__ = 'parser' 64 | 65 | def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1): 66 | super().__init__(parent, invokingState) 67 | self.parser = parser 68 | 69 | def conversionTest(self, i:int=None): 70 | if i is None: 71 | return self.getTypedRuleContexts(conversion.ConversionTestContext) 72 | else: 73 | return self.getTypedRuleContext(conversion.ConversionTestContext,i) 74 | 75 | 76 | def getRuleIndex(self): 77 | return conversion.RULE_conversionTests 78 | 79 | def accept(self, visitor:ParseTreeVisitor): 80 | if hasattr( visitor, "visitConversionTests" ): 81 | return visitor.visitConversionTests(self) 82 | else: 83 | return visitor.visitChildren(self) 84 | 85 | 86 | 87 | 88 | def conversionTests(self): 89 | 90 | localctx = conversion.ConversionTestsContext(self, self._ctx, self.state) 91 | self.enterRule(localctx, 0, self.RULE_conversionTests) 92 | self._la = 0 # Token type 93 | try: 94 | self.enterOuterAlt(localctx, 1) 95 | self.state = 7 96 | self._errHandler.sync(self) 97 | _la = self._input.LA(1) 98 | while _la==1: 99 | self.state = 4 100 | self.conversionTest() 101 | self.state = 9 102 | self._errHandler.sync(self) 103 | _la = self._input.LA(1) 104 | 105 | except RecognitionException as re: 106 | localctx.exception = re 107 | self._errHandler.reportError(self, re) 108 | self._errHandler.recover(self, re) 109 | finally: 110 | self.exitRule() 111 | return localctx 112 | 113 | 114 | class ConversionTestContext(ParserRuleContext): 115 | __slots__ = 'parser' 116 | 117 | def __init__(self, parser, parent:ParserRuleContext=None, invokingState:int=-1): 118 | super().__init__(parent, invokingState) 119 | self.parser = parser 120 | 121 | def FoxStart(self): 122 | return self.getToken(conversion.FoxStart, 0) 123 | 124 | def FoxEnd(self): 125 | return self.getToken(conversion.FoxEnd, 0) 126 | 127 | def PyStart(self): 128 | return self.getToken(conversion.PyStart, 0) 129 | 130 | def PyEnd(self): 131 | return self.getToken(conversion.PyEnd, 0) 132 | 133 | def FoxLine(self, i:int=None): 134 | if i is None: 135 | return self.getTokens(conversion.FoxLine) 136 | else: 137 | return self.getToken(conversion.FoxLine, i) 138 | 139 | def PyLine(self, i:int=None): 140 | if i is None: 141 | return self.getTokens(conversion.PyLine) 142 | else: 143 | return self.getToken(conversion.PyLine, i) 144 | 145 | def getRuleIndex(self): 146 | return conversion.RULE_conversionTest 147 | 148 | def accept(self, visitor:ParseTreeVisitor): 149 | if hasattr( visitor, "visitConversionTest" ): 150 | return visitor.visitConversionTest(self) 151 | else: 152 | return visitor.visitChildren(self) 153 | 154 | 155 | 156 | 157 | def conversionTest(self): 158 | 159 | localctx = conversion.ConversionTestContext(self, self._ctx, self.state) 160 | self.enterRule(localctx, 2, self.RULE_conversionTest) 161 | self._la = 0 # Token type 162 | try: 163 | self.enterOuterAlt(localctx, 1) 164 | self.state = 10 165 | self.match(conversion.FoxStart) 166 | self.state = 14 167 | self._errHandler.sync(self) 168 | _la = self._input.LA(1) 169 | while _la==5: 170 | self.state = 11 171 | self.match(conversion.FoxLine) 172 | self.state = 16 173 | self._errHandler.sync(self) 174 | _la = self._input.LA(1) 175 | 176 | self.state = 17 177 | self.match(conversion.FoxEnd) 178 | self.state = 18 179 | self.match(conversion.PyStart) 180 | self.state = 22 181 | self._errHandler.sync(self) 182 | _la = self._input.LA(1) 183 | while _la==7: 184 | self.state = 19 185 | self.match(conversion.PyLine) 186 | self.state = 24 187 | self._errHandler.sync(self) 188 | _la = self._input.LA(1) 189 | 190 | self.state = 25 191 | self.match(conversion.PyEnd) 192 | except RecognitionException as re: 193 | localctx.exception = re 194 | self._errHandler.reportError(self, re) 195 | self._errHandler.recover(self, re) 196 | finally: 197 | self.exitRule() 198 | return localctx 199 | 200 | 201 | 202 | 203 | 204 | -------------------------------------------------------------------------------- /vfp2py/function_abbreviations.py: -------------------------------------------------------------------------------- 1 | function_abbreviations = [ 2 | 'ACLA|SS', 3 | 'ADBO|BJECTS', 4 | 'ADAT|ABASES', 5 | 'ADOC|KSTATE', 6 | 'AERR|OR', 7 | 'AEVE|NTS', 8 | 'AFIE|LDS', 9 | 'AFON|T', 10 | 'AGETC|LASS', 11 | 'AGETF|ILEVERSION', 12 | 'AINST|ANCE', 13 | 'ALAN|GUAGE', 14 | 'ALIN|ES', 15 | 'AMEM|BERS', 16 | 'AMOU|SEOBJ', 17 | 'ANET|RESOURCES', 18 | 'APRI|NTERS', 19 | 'APRO|CINFO', 20 | 'ASQL|HANDLES', 21 | 'ASEL|OBJ', 22 | 'ASTA|CKINFO', 23 | 'ATAG|INFO', 24 | 'AUSE|D', 25 | 'AVCX|CLASSES', 26 | 'ACOP|Y', 27 | 'ADDP|ROPERTY', 28 | 'ADDB|S', 29 | 'ADLL|S', 30 | 'AELE|MENT', 31 | 'ALIA|S', 32 | 'ALLT|RIM', 33 | 'ASCA|N', 34 | 'ASES|SIONS', 35 | 'ASOR|T', 36 | 'ASUB|SCRIPT', 37 | 'ATCL|INE', 38 | 'ATLI|NE', 39 | 'BETW|EEN', 40 | 'BINT|OC', 41 | 'BIND|EVENT', 42 | 'BITA|ND', 43 | 'BITC|LEAR', 44 | 'BITL|SHIFT', 45 | 'BITN|OT', 46 | 'BITO|R', 47 | 'BITR|SHIFT', 48 | 'BITS|ET', 49 | 'BITT|EST', 50 | 'BITX|OR', 51 | 'CTOB|IN', 52 | 'CAND|IDATE', 53 | 'CAPS|LOCK', 54 | 'CEIL|ING', 55 | 'CHRS|AW', 56 | 'CHRT|RAN', 57 | 'CLEARR|ESULTSET', 58 | 'CMON|TH', 59 | 'CNTB|AR', 60 | 'CNTP|AD', 61 | 'COMA|RRAY', 62 | 'COMC|LASSINFO', 63 | 'COMR|ETURNERROR', 64 | 'COMP|OBJ', 65 | 'CPCO|NVERT', 66 | 'CPCU|RRENT', 67 | 'CPDB|F', 68 | 'CREATEB|INARY', 69 | 'CREATE|OBJECT', 70 | 'CREATEOF|FLINE', 71 | 'CURD|IR', 72 | 'CURSORG|ETPROP', 73 | 'CURSORS|ETPROP', 74 | 'CURSORTOX|ML', 75 | 'CURV|AL', 76 | 'DBAL|IAS', 77 | 'DBGE|TPROP', 78 | 'DBSE|TPROP', 79 | 'DBUS|ED', 80 | 'DDEAB|ORTTRANS', 81 | 'DDEAD|VISE', 82 | 'DDEEN|ABLED', 83 | 'DDEEX|ECUTE', 84 | 'DDEI|NITIATE', 85 | 'DDEL|ASTERROR', 86 | 'DDEP|OKE', 87 | 'DDER|EQUEST', 88 | 'DDES|ETOPTION', 89 | 'DDESETS|ERVICE', 90 | 'DDESETT|OPIC', 91 | 'DDET|ERMINATE', 92 | 'DATET|IME', 93 | 'DEFA|ULTEXT', 94 | 'DELE|TED', 95 | 'DESC|ENDING', 96 | 'DIFF|ERENCE', 97 | 'DIRE|CTORY', 98 | 'DISK|SPACE', 99 | 'DISP|LAYPATH', 100 | 'DODE|FAULT', 101 | 'DRIV|ETYPE', 102 | 'DROP|OFFLINE', 103 | 'EDIT|SOURCE', 104 | 'EMPT|Y', 105 | 'ERRO|R', 106 | 'EVAL|UATE', 107 | 'EVENT|HANDLER', 108 | 'EXEC|SCRIPT', 109 | 'FCHS|IZE', 110 | 'FCLO|SE', 111 | 'FCOU|NT', 112 | 'FCRE|ATE', 113 | 'FDAT|E', 114 | 'FERR|OR', 115 | 'FFLU|SH', 116 | 'FGET|S', 117 | 'FIEL|D', 118 | 'FILET|OSTR', 119 | 'FILT|ER', 120 | 'FKLA|BEL', 121 | 'FKMA|X', 122 | 'FLOC|K', 123 | 'FLOO|R', 124 | 'FONT|METRIC', 125 | 'FOPE|N', 126 | 'FORC|EEXT', 127 | 'FORCEP|ATH', 128 | 'FOUN|D', 129 | 'FPUT|S', 130 | 'FREA|D', 131 | 'FSEE|K', 132 | 'FSIZ|E', 133 | 'FTIM|E', 134 | 'FULL|PATH', 135 | 'FWRI|TE', 136 | 'GETA|UTOINCVALUE', 137 | 'GETC|OLOR', 138 | 'GETCU|RSORADAPTER', 139 | 'GETD|IR', 140 | 'GETE|NV', 141 | 'GETF|ILE', 142 | 'GETFL|DSTATE', 143 | 'GETFO|NT', 144 | 'GETH|OST', 145 | 'GETI|NTERFACE', 146 | 'GETN|EXTMODIFIED', 147 | 'GETO|BJECT', 148 | 'GETP|EM', 149 | 'GETPI|CT', 150 | 'GETPR|INTER', 151 | 'GETR|ESULTSET', 152 | 'GETWORDC|OUNT', 153 | 'GETWORDN|UM', 154 | 'GETB|AR', 155 | 'GETPA|D', 156 | 'GOMO|NTH', 157 | 'HEAD|ER', 158 | 'ICAS|E', 159 | 'IMES|TATUS', 160 | 'IDXC|OLLATE', 161 | 'INLI|ST', 162 | 'INDB|C', 163 | 'INDE|XSEEK', 164 | 'INKE|Y', 165 | 'INPU|TBOX', 166 | 'INSM|ODE', 167 | 'ISAL|PHA', 168 | 'ISBL|ANK', 169 | 'ISCO|LOR', 170 | 'ISDI|GIT', 171 | 'ISEX|CLUSIVE', 172 | 'ISFL|OCKED', 173 | 'ISHO|STED', 174 | 'ISLE|ADBYTE', 175 | 'ISLO|WER', 176 | 'ISME|MOFETCHED', 177 | 'ISMO|USE', 178 | 'ISNU|LL', 179 | 'ISPE|N', 180 | 'ISRL|OCKED', 181 | 'ISRE|ADONLY', 182 | 'ISTR|ANSACTABLE', 183 | 'ISUP|PER', 184 | 'JUSTD|RIVE', 185 | 'JUSTE|XT', 186 | 'JUSTF|NAME', 187 | 'JUSTP|ATH', 188 | 'JUSTS|TEM', 189 | 'KEYM|ATCH', 190 | 'LAST|KEY', 191 | 'LINE|NO', 192 | 'LOAD|PICTURE', 193 | 'LOCF|ILE', 194 | 'LOG1|0', 195 | 'LOOK|UP', 196 | 'LOWE|R', 197 | 'LTRI|M', 198 | 'LUPD|ATE', 199 | 'MAKE|TRANSACTABLE', 200 | 'MDOW|N', 201 | 'MEML|INES', 202 | 'MEMO|RY', 203 | 'MESS|AGE', 204 | 'MESSAGEB|OX', 205 | 'MINU|TE', 206 | 'MLIN|E', 207 | 'MONT|H', 208 | 'MRKB|AR', 209 | 'MRKP|AD', 210 | 'MWIN|DOW', 211 | 'NEW|OBJECT', 212 | 'NORM|ALIZE', 213 | 'NUML|OCK', 214 | 'OBJT|OCLIENT', 215 | 'OCCU|RS', 216 | 'OLDV|AL', 217 | 'ORDE|R', 218 | 'PARA|METERS', 219 | 'PAYM|ENT', 220 | 'PCOU|NT', 221 | 'PEMS|TATUS', 222 | 'POPU|P', 223 | 'PRIM|ARY', 224 | 'PRIN|TSTATUS', 225 | 'PRMB|AR', 226 | 'PRMP|AD', 227 | 'PROG|RAM', 228 | 'PROM|PT', 229 | 'PROP|ER', 230 | 'PRTI|NFO', 231 | 'PUTF|ILE', 232 | 'QUAR|TER', 233 | 'RAIS|EEVENT', 234 | 'RATL|INE', 235 | 'RECC|OUNT', 236 | 'RECN|O', 237 | 'RECS|IZE', 238 | 'REFR|ESH', 239 | 'RELA|TION', 240 | 'REMO|VEPROPERTY', 241 | 'REPL|ICATE', 242 | 'REQU|ERY', 243 | 'RGBS|CHEME', 244 | 'RIGH|T', 245 | 'RLOC|K', 246 | 'ROUN|D', 247 | 'RTRI|M', 248 | 'SQLCA|NCEL', 249 | 'SQLCOL|UMNS', 250 | 'SQLCOM|MIT', 251 | 'SQLCON|NECT', 252 | 'SQLD|ISCONNECT', 253 | 'SQLE|XEC', 254 | 'SQLG|ETPROP', 255 | 'SQLI|DLEDISCONNECT', 256 | 'SQLM|ORERESULTS', 257 | 'SQLP|REPARE', 258 | 'SQLR|OLLBACK', 259 | 'SQLSE|TPROP', 260 | 'SQLST|RINGCONNECT', 261 | 'SQLT|ABLES', 262 | 'SAVE|PICTURE', 263 | 'SCHE|ME', 264 | 'SCOL|S', 265 | 'SECO|NDS', 266 | 'SELE|CT', 267 | 'SETF|LDSTATE', 268 | 'SETR|ESULTSET', 269 | 'SKPB|AR', 270 | 'SKPP|AD', 271 | 'SOUN|DEX', 272 | 'SPAC|E', 273 | 'SROW|S', 274 | 'STRE|XTRACT', 275 | 'STRTO|FILE', 276 | 'STRC|ONV', 277 | 'STRTR|AN', 278 | 'STUF|F', 279 | 'SUBS|TR', 280 | 'SYSM|ETRIC', 281 | 'TABLER|EVERT', 282 | 'TABLEU|PDATE', 283 | 'TAGC|OUNT', 284 | 'TAGN|O', 285 | 'TARG|ET', 286 | 'TEXT|MERGE', 287 | 'TRAN|SFORM', 288 | 'TXNL|EVEL', 289 | 'TXTW|IDTH', 290 | 'UNBI|NDEVENTS', 291 | 'UPPE|R', 292 | 'VART|YPE', 293 | 'VERS|ION', 294 | 'WDOC|KABLE', 295 | 'WBOR|DER', 296 | 'WCHI|LD', 297 | 'WCOL|S', 298 | 'WEXI|ST', 299 | 'WFON|T', 300 | 'WLAS|T', 301 | 'WLCO|L', 302 | 'WLRO|W', 303 | 'WMAX|IMUM', 304 | 'WMIN|IMUM', 305 | 'WONT|OP', 306 | 'WOUT|PUT', 307 | 'WPAR|ENT', 308 | 'WROW|S', 309 | 'WTIT|LE', 310 | 'WVIS|IBLE', 311 | 'XMLT|OCURSOR', 312 | 'XMLU|PDATEGRAM', 313 | ] 314 | 315 | expander = { 316 | 'MSGB' : 'MESSAGEBOX', 317 | 'TRIM' : 'RTRIM', 318 | } 319 | expander = {key.lower(): val.lower() for key, val in expander.items()} 320 | 321 | for abbr in function_abbreviations: 322 | cut = abbr.find('|') 323 | keyword = (abbr[:cut] + abbr[cut+1:]).lower() 324 | for cut in range(cut, len(keyword)+1): 325 | expander[keyword[:cut]] = keyword 326 | del function_abbreviations, abbr, keyword, cut 327 | -------------------------------------------------------------------------------- /testbed/test_lib.prg: -------------------------------------------------------------------------------- 1 | procedure select_tests 2 | assert select() = 1 3 | assert select(0) = 1 4 | assert select(1) = 32767 5 | assert select(2) = 0 6 | assert select('test') = 0 7 | ENDPROC 8 | 9 | procedure chr_tests 10 | assert asc(chr(0)) = 0 11 | endproc 12 | 13 | procedure set_tests 14 | assert set('compatible') = 'OFF' 15 | assert set('compatible', 1) = 'PROMPT' 16 | ENDPROC 17 | 18 | procedure used_tests 19 | assert used('test') = .f. 20 | endproc 21 | 22 | procedure date_tests 23 | local somedate 24 | somedate = {^2017-6-30} 25 | assert somedate == Date(2017, 6, 30) 26 | assert dow(somedate) == 6 27 | assert cdow(somedate) == 'Friday' 28 | assert month(somedate) == 6 29 | assert cmonth(somedate) == 'June' 30 | assert dmy(somedate) == '30 June 2017' 31 | assert dtos(somedate) == '20170630' 32 | assert dtoc(somedate) == '06/30/2017' 33 | assert len(time()) == 8 34 | assert len(time(1)) == 11 35 | assert dtot(somedate) == Datetime(2017, 6, 30, 0) 36 | assert gomonth(somedate, -4) = date(2017, 2, 28) 37 | assert vartype(somedate) == 'D' 38 | assert vartype(dtot(somedate)) == 'T' 39 | endproc 40 | 41 | procedure math_tests 42 | local num_value 43 | num_value = pi() 44 | assert round(pi(), 2) == 3.14 45 | assert abs(tan(dtor(45)) - 1) < 0.001 46 | assert abs(sin(dtor(90)) - 1) < 0.001 47 | assert abs(cos(dtor(90)) - 0) < 0.001 48 | assert abs(cos(dtor(45)) - sqrt(2)/2) < 0.001 49 | assert 0 < rand() and rand() < 1 50 | assert mod(5, 2) == 1 51 | 52 | local stringval 53 | stringval = '1e5' 54 | assert val(stringval) = 100000 55 | assert vartype(num_value) == 'N' 56 | assert not ((.t. or .t.) and .f.) 57 | assert .t. or .f. and .t. 58 | endproc 59 | 60 | procedure string_tests 61 | cString = "AAA aaa, BBB bbb, CCC ccc." 62 | assert vartype(cString) == 'C' 63 | assert GetWordCount(cString) == 6 64 | assert GetWordCount(cString, ",") = 3 65 | ASSERT GetWordCount(cString, ".") == 1 66 | assert GetWordNUM(cString, 2) == 'aaa,' 67 | assert GetWordNum(cString, 2, ",") = ' BBB bbb' 68 | ASSERT GETWORDNUM(cString, 2, ".") == '' 69 | assert like('Ab*t.???', 'About.txt') 70 | assert not like('Ab*t.???', 'about.txt') 71 | assert not isalpha('') 72 | assert isalpha('a123') 73 | assert not isalpha('1abc') 74 | assert not islower('') 75 | assert islower('test') 76 | assert not islower('Test') 77 | assert not isdigit('') 78 | assert isdigit('1abc') 79 | assert not isdigit('a123') 80 | assert not ISUPPER('') 81 | assert ISUPPER('Test') 82 | assert not ISUPPER('test') 83 | assert isblank('') 84 | assert not isblank('test') 85 | assert isblank({ / / }) 86 | cString = ' AAA ' 87 | assert alltrim(cString) = 'AAA' 88 | assert rtrim(cString) = ' AAA' 89 | assert ltrim(cString) = 'AAA ' 90 | assert trim(cString) = rtrim(cString) 91 | assert strextract('This {{is}} a {{template}}', '{{', '}}') == 'is' 92 | assert strextract('This {{is}} a {{template}}', '{{', '}}', 2) == 'template' 93 | assert strextract('This {{is}} a {{template}}', '{{is}}') == ' a {{template}}' 94 | assert strextract('This {{is}} a {{template}}', '{{IS}}', '', 1, 1) == ' a {{template}}' 95 | assert atc('aab', '123AAbbB') == 4 96 | TEXT TO CSTRING NOSHOW 97 | 123AAbbbB 98 | TESTTEST 99 | TEXTLINES 100 | ENDTEXT 101 | assert cstring == '123AAbbbBTESTTESTTEXTLINES' 102 | cstring = '123AAbbbB' + CHR(13) + CHR(10) + 'TESTTEST' + CHR(13) + CHR(10) + 'TEXTLINES' 103 | assert atline('T', cstring) == 2 104 | assert ratline('T', cstring) == 3 105 | ENDPROC 106 | 107 | procedure path_tests 108 | assert HOME() == curdir() 109 | handle = fcreate('test_lib_file') 110 | fclose(handle) 111 | assert not isblank(locfile('test_lib_file')) 112 | CD .. 113 | assert HOME() != curdir() 114 | assert not isblank(locfile('test_lib_file')) 115 | delete file ADDBS(HOME()) + 'test_lib_file' 116 | endproc 117 | 118 | procedure misc_tests 119 | assert version() == 'Not FoxPro 9' 120 | assert version(4) == version() 121 | assert version(5) == 900 122 | endproc 123 | 124 | procedure _add_db_record(seed) 125 | LOCAL fake, fake_name, fake_st, fake_quantity, fake_received 126 | fake = pythonfunctioncall('faker', 'Faker', createobject('pythontuple')) 127 | fake.callmethod('seed', createobject('pythontuple', seed)) 128 | fake_name = fake.callmethod('name', createobject('pythontuple')) 129 | fake_st = fake.callmethod('state_abbr', createobject('pythontuple')) 130 | fake_quantity = fake.callmethod('random_int', createobject('pythontuple', 0, 100)) 131 | fake_received = fake.callmethod('boolean', createobject('pythontuple')) 132 | insert into report values (fake_name, fake_st, fake_quantity, fake_received) 133 | endproc 134 | 135 | procedure _sqlexec_add_record(sqlconn, seed) 136 | LOCAL fake, fake_name, fake_st, fake_quantity, fake_received 137 | fake = pythonfunctioncall('faker', 'Faker', createobject('pythontuple')) 138 | fake.callmethod('seed', createobject('pythontuple', seed)) 139 | fake_name = fake.callmethod('name', createobject('pythontuple')) 140 | fake_st = fake.callmethod('state_abbr', createobject('pythontuple')) 141 | fake_quantity = fake.callmethod('random_int', createobject('pythontuple', 0, 100)) 142 | fake_received = fake.callmethod('boolean', createobject('pythontuple')) 143 | sqlcmd = "insert into REPORT values ('" + fake_name + "','" + fake_st + "'," + alltrim(str(fake_quantity)) + ',' + alltrim(str(cast(fake_received as int))) + ')' 144 | ?sqlcmd 145 | return sqlexec(sqlconn, sqlcmd) 146 | endproc 147 | 148 | procedure database_tests 149 | SET SAFETY OFF 150 | SET ASSERTS ON 151 | try 152 | CREATE TABLE REPORT FREE (NAME C(50), ST C(2), QUANTITY N(5, 0), RECEIVED L(1)) 153 | ASSERT FILE('report.dbf') 154 | ASSERT USED('report') 155 | try 156 | USE report in 0 shared 157 | assert .f. 158 | catch to oerr 159 | ?oerr.message 160 | assert oerr.message == 'File is in use.' 161 | endtry 162 | _add_db_record(0) 163 | _add_db_record(1) 164 | _add_db_record(2) 165 | _add_db_record(3) 166 | assert cpdbf() = 0 167 | ASSERT FCOUNT() == 4 168 | ALTER TABLE REPORT ADD COLUMN AGE N(3, 0) 169 | ASSERT FCOUNT() == 5 170 | assert field(2) == 'st' 171 | assert not found() 172 | go top 173 | local loopcount 174 | loopcount = 0 175 | scan 176 | assert len(alltrim(name)) > 0 177 | loopcount = loopcount + 1 178 | endscan 179 | assert loopcount == 4 180 | go 3 181 | loopcount = 0 182 | scan all for alltrim(st) = 'ID' 183 | assert len(alltrim(name)) > 0 184 | loopcount = loopcount + 1 185 | endscan 186 | assert loopcount == 2 187 | loopcount = 0 188 | scan rest for alltrim(st) = 'ID' 189 | assert len(alltrim(name)) > 0 190 | loopcount = loopcount + 1 191 | endscan 192 | assert loopcount == 0 193 | go top 194 | loopcount = 0 195 | scan for alltrim(st) = 'ID' 196 | assert len(alltrim(name)) > 0 197 | loopcount = loopcount + 1 198 | endscan 199 | assert loopcount == 2 200 | release loopcount 201 | assert alltrim(name) == 'Norma Fisher' MESSAGE alltrim(name) + ' should be Norma Fisher' 202 | assert recno() == 1 203 | scatter name report_record 204 | assert alltrim(report_record.name) == 'Norma Fisher' MESSAGE alltrim(report_record.name) + ' should be Norma Fisher' 205 | go bott 206 | assert alltrim(name) == 'Joshua Wood' MESSAGE alltrim(name) + ' should be Joshua Wood' 207 | assert alltrim(report_record.name) == 'Norma Fisher' MESSAGE alltrim(report_record.name) + ' should be Norma Fisher' 208 | assert recno() == 4 209 | goto 1 210 | locate for st == 'ID' 211 | assert alltrim(name) == 'Norma Fisher' MESSAGE alltrim(name) + ' should be Norma Fisher' 212 | assert found() 213 | continue 214 | assert alltrim(name) == 'Ryan Gallagher' MESSAGE alltrim(name) + ' should be Ryan Gallagher' 215 | continue 216 | assert EOF() 217 | assert recno() == reccount() + 1 218 | assert not found() 219 | count for quantity > 60 to countval 220 | assert countval = 2 221 | assert eof() 222 | sum sqrt(quantity + 205) for quantity > 50 while quantity != 63 to sumval 223 | assert sumval == 0 224 | go top 225 | sum sqrt(quantity + 205) for quantity > 50 while quantity != 63 to sumval 226 | assert sumval == 17 + 16 227 | index on st tag st 228 | seek 'CA' 229 | assert alltrim(st) == 'CA' 230 | go top 231 | DELETE REST FOR QUANTITY > 60 232 | PACK 233 | goto top 234 | assert reccount() == 2 235 | REPLACE REPORT.NAME WITH 'N/A' 236 | assert alltrim(name) == 'N/A' 237 | REPLACE ALL NAME WITH 'Not Available' 238 | assert recno() == reccount() + 1 239 | GO BOTT 240 | assert alltrim(name) == 'Not Available' 241 | ZAP 242 | ASSERT RECCOUNT() == 0 243 | copy structure to report2 244 | USE report2 in 0 shared alias somethingelse 245 | assert alias() == 'report' 246 | SELECT report2 247 | assert alias() == 'somethingelse' 248 | ASSERT FCOUNT() == 5 249 | ALTER TABLE REPORT2 DROP COLUMN ST 250 | ASSERT FCOUNT() == 4 251 | assert alltrim(report_record.name) == 'Norma Fisher' MESSAGE alltrim(report_record.name) + ' should be Norma Fisher' 252 | append blank 253 | gather from report_record 254 | assert alltrim(name) == 'Norma Fisher' MESSAGE alltrim(name) + ' should be Norma Fisher' 255 | assert dbf() == 'report2.dbf' 256 | use in select('report2') 257 | use in select('report') 258 | DELETE FILE REPORT2.DBF 259 | catch to err 260 | ?err.message 261 | browse 262 | throw 263 | finally 264 | DELETE FILE REPORT.DBF 265 | endtry 266 | sqlconn = sqlconnect('testodbc') 267 | assert sqlconn > 0 268 | assert sqlexec(sqlconn, 'CREATE TABLE REPORT (NAME varchar(50), ST char(2), QUANTITY int(5), RECEIVED bit)') > 0 269 | assert _sqlexec_add_record(sqlconn, 0) > 0 270 | assert _sqlexec_add_record(sqlconn, 1) > 0 271 | assert _sqlexec_add_record(sqlconn, 2) > 0 272 | assert _sqlexec_add_record(sqlconn, 3) > 0 273 | assert sqlexec(sqlconn, 'SELECT * FROM REPORT') 274 | select sqlresult 275 | assert alltrim(name) == 'Norma Fisher' 276 | sqlcommit(sqlconn) 277 | sqldisconnect(sqlconn) 278 | sqlconn = sqlstringconnect('dsn=testodbc') 279 | assert sqlconn > 0 280 | assert sqltables(sqlconn) > 0 281 | select sqlresult 282 | assert lower(alltrim(table_name)) == 'report' 283 | assert sqlexec(sqlconn, 'DELETE FROM REPORT;') 284 | assert sqlrollback(sqlconn) 285 | assert sqlexec(sqlconn, 'SELECT * FROM REPORT') 286 | select sqlresult 287 | assert alltrim(name) == 'Norma Fisher' 288 | assert sqlexec(sqlconn, 'DROP TABLE REPORT') > 0 289 | sqlcommit(sqlconn) 290 | sqldisconnect(sqlconn) 291 | close tables 292 | endproc 293 | 294 | procedure user_defined_function(arg1, arg2) 295 | assert pcount() == 1 296 | assert parameters() == 1 297 | user_defined_function2() 298 | assert parameters() == 0 299 | assert pcount() == 1 300 | endproc 301 | 302 | procedure user_defined_function2(arg1) 303 | endproc 304 | 305 | procedure scope_tests 306 | PUBLIC ARRAY somearray[2, 5] 307 | public array def[10] 308 | assert def[1] == .F. 309 | SOMEARRAY(1, 4) = 3 310 | assert somearray[1, 4] == 3 311 | PRIVATE TEST, somearray[2, 5] 312 | RELEASE nonexistantvariable 313 | 314 | set procedure to time 315 | ?localtime() 316 | 317 | set procedure to argparse 318 | t = createobject('namespace') 319 | 320 | user_defined_function(.F.) 321 | ENDPROC 322 | 323 | define class someclass as custom 324 | enddefine 325 | 326 | procedure class_tests 327 | obj = createobject('someclass') 328 | endproc 329 | -------------------------------------------------------------------------------- /testbed/test_lib.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | from __future__ import division, print_function 3 | 4 | import datetime as dt 5 | import math 6 | import os 7 | import random 8 | 9 | import faker 10 | from vfp2py import vfpfunc 11 | from vfp2py.vfpfunc import DB, Array, C, F, M, S, lparameters, parameters, vfpclass 12 | 13 | 14 | @lparameters() 15 | def MAIN(): 16 | pass 17 | 18 | 19 | @lparameters() 20 | def select_tests(): 21 | assert DB.select_function(0 if vfpfunc.set('compatible') == 'OFF' else None) == 1 22 | assert DB.select_function(0) == 1 23 | assert DB.select_function(1) == 32767 24 | assert DB.select_function(2) == 0 25 | assert DB.select_function('test') == 0 26 | 27 | 28 | @lparameters() 29 | def chr_tests(): 30 | assert ord('\x00'[0]) == 0 31 | 32 | 33 | @lparameters() 34 | def set_tests(): 35 | assert vfpfunc.set('compatible') == 'OFF' 36 | assert vfpfunc.set('compatible', 1) == 'PROMPT' 37 | 38 | 39 | @lparameters() 40 | def used_tests(): 41 | assert DB.used('test') == False 42 | 43 | 44 | @lparameters() 45 | def date_tests(): 46 | M.add_local('somedate') 47 | S.somedate = dt.date(2017, 6, 30) 48 | assert S.somedate == dt.date(2017, 6, 30) 49 | assert vfpfunc.dow_fix(S.somedate.weekday()) == 6 50 | assert S.somedate.strftime('%A') == 'Friday' 51 | assert S.somedate.month == 6 52 | assert S.somedate.strftime('%B') == 'June' 53 | assert S.somedate.strftime('%d %B %Y') == '30 June 2017' 54 | assert vfpfunc.dtos(S.somedate) == '20170630' 55 | assert vfpfunc.dtoc(S.somedate) == '06/30/2017' 56 | assert len(dt.datetime.now().time().strftime('%H:%M:%S')) == 8 57 | assert len(dt.datetime.now().time().strftime('%H:%M:%S.%f')[:11]) == 11 58 | assert dt.datetime.combine(S.somedate, dt.datetime.min.time()) == dt.datetime(2017, 6, 30, 0) 59 | assert vfpfunc.gomonth(S.somedate, -4) == dt.date(2017, 2, 28) 60 | assert vfpfunc.vartype(S.somedate) == 'D' 61 | assert vfpfunc.vartype(dt.datetime.combine(S.somedate, dt.datetime.min.time())) == 'T' 62 | 63 | 64 | @lparameters() 65 | def math_tests(): 66 | M.add_local('num_value') 67 | S.num_value = math.pi 68 | assert round(math.pi, 2) == 3.14 69 | assert abs(math.tan(math.radians(45)) - 1) < 0.001 70 | assert abs(math.sin(math.radians(90)) - 1) < 0.001 71 | assert abs(math.cos(math.radians(90)) - 0) < 0.001 72 | assert abs(math.cos(math.radians(45)) - math.sqrt(2) / 2) < 0.001 73 | assert 0 < random.random() and random.random() < 1 74 | assert (5 % 2) == 1 75 | 76 | M.add_local('stringval') 77 | S.stringval = '1e5' 78 | assert float(S.stringval) == 100000 79 | assert vfpfunc.vartype(S.num_value) == 'N' 80 | assert not ((True or True) and False) 81 | assert True or False and True 82 | 83 | 84 | @lparameters() 85 | def string_tests(): 86 | S.cstring = 'AAA aaa, BBB bbb, CCC ccc.' 87 | assert vfpfunc.vartype(S.cstring) == 'C' 88 | assert len([w for w in S.cstring.split() if w]) == 6 89 | assert len([w for w in S.cstring.split(',') if w]) == 3 90 | assert len([w for w in S.cstring.split('.') if w]) == 1 91 | assert vfpfunc.getwordnum(S.cstring, 2) == 'aaa,' 92 | assert vfpfunc.getwordnum(S.cstring, 2, ',') == ' BBB bbb' 93 | assert vfpfunc.getwordnum(S.cstring, 2, '.') == '' 94 | assert vfpfunc.like('Ab*t.???', 'About.txt') 95 | assert not vfpfunc.like('Ab*t.???', 'about.txt') 96 | assert not ''[:1].isalpha() 97 | assert 'a123'[:1].isalpha() 98 | assert not '1abc'[:1].isalpha() 99 | assert not ''[:1].islower() 100 | assert 'test'[:1].islower() 101 | assert not 'Test'[:1].islower() 102 | assert not ''[:1].isdigit() 103 | assert '1abc'[:1].isdigit() 104 | assert not 'a123'[:1].isdigit() 105 | assert not ''[:1].isupper() 106 | assert 'Test'[:1].isupper() 107 | assert not 'test'[:1].isupper() 108 | assert vfpfunc.isblank('') 109 | assert not vfpfunc.isblank('test') 110 | assert vfpfunc.isblank(None) 111 | S.cstring = ' AAA ' 112 | assert S.cstring.strip() == 'AAA' 113 | assert S.cstring.rstrip() == ' AAA' 114 | assert S.cstring.lstrip() == 'AAA ' 115 | assert S.cstring.rstrip() == S.cstring.rstrip() 116 | assert vfpfunc.strextract('This {{is}} a {{template}}', '{{', '}}') == 'is' 117 | assert vfpfunc.strextract('This {{is}} a {{template}}', '{{', '}}', 2) == 'template' 118 | assert vfpfunc.strextract('This {{is}} a {{template}}', '{{is}}') == ' a {{template}}' 119 | assert vfpfunc.strextract('This {{is}} a {{template}}', '{{IS}}', '', 1, 1) == ' a {{template}}' 120 | assert '123AAbbB'.lower().find('aab'.lower()) + 1 == 4 121 | S.cstring = vfpfunc.text([' 123AAbbbB', 122 | ' TESTTEST', 123 | ' TEXTLINES', 124 | ' '], show=False) 125 | assert S.cstring == '123AAbbbBTESTTESTTEXTLINES' 126 | S.cstring = '123AAbbbB\r\nTESTTEST\r\nTEXTLINES' 127 | assert vfpfunc.atline('T', S.cstring) == 2 128 | assert vfpfunc.ratline('T', S.cstring) == 3 129 | 130 | 131 | @lparameters() 132 | def path_tests(): 133 | assert vfpfunc.home() == os.getcwd() 134 | S.handle = open('test_lib_file', 'w') 135 | S.handle.close() 136 | assert not vfpfunc.isblank(vfpfunc.locfile('test_lib_file')) 137 | os.chdir('..') 138 | assert vfpfunc.home() != os.getcwd() 139 | assert not vfpfunc.isblank(vfpfunc.locfile('test_lib_file')) 140 | os.remove(os.path.join(vfpfunc.home(), 'test_lib_file')) 141 | 142 | 143 | @lparameters() 144 | def misc_tests(): 145 | assert vfpfunc.version() == 'Not FoxPro 9' 146 | assert vfpfunc.version(4) == vfpfunc.version() 147 | assert vfpfunc.version(5) == 900 148 | 149 | 150 | @lparameters('seed') 151 | def _add_db_record(): 152 | M.add_local('fake', 'fake_name', 'fake_st', 'fake_quantity', 'fake_received') 153 | S.fake = faker.Faker() 154 | S.fake.seed(S.seed) 155 | S.fake_name = S.fake.name() 156 | S.fake_st = S.fake.state_abbr() 157 | S.fake_quantity = S.fake.random_int(0, 100) 158 | S.fake_received = S.fake.boolean() 159 | DB.insert('report', (S.fake_name, S.fake_st, S.fake_quantity, S.fake_received)) 160 | 161 | 162 | @lparameters('sqlconn', 'seed') 163 | def _sqlexec_add_record(): 164 | M.add_local('fake', 'fake_name', 'fake_st', 'fake_quantity', 'fake_received') 165 | S.fake = faker.Faker() 166 | S.fake.seed(S.seed) 167 | S.fake_name = S.fake.name() 168 | S.fake_st = S.fake.state_abbr() 169 | S.fake_quantity = S.fake.random_int(0, 100) 170 | S.fake_received = S.fake.boolean() 171 | S.sqlcmd = "insert into REPORT values ('" + S.fake_name + "','" + S.fake_st + "'," + vfpfunc.str(S.fake_quantity).strip() + ',' + vfpfunc.str(int(S.fake_received)).strip() + ')' 172 | print(S.sqlcmd) 173 | return vfpfunc.sqlexec(S.sqlconn, S.sqlcmd) 174 | 175 | 176 | @lparameters() 177 | def database_tests(): 178 | # FIX ME: SET SAFETY OFF 179 | # FIX ME: SET ASSERTS ON 180 | try: 181 | DB.create_table('report', 'name c(50); st c(2); quantity n(5, 0); received l', 'free') 182 | assert os.path.isfile('report.dbf') 183 | assert DB.used('report') 184 | try: 185 | DB.use('report', 0, 'shared') 186 | assert False 187 | except Exception as err: 188 | S.oerr = vfpfunc.Exception.from_pyexception(err) 189 | print(S.oerr.message) 190 | assert S.oerr.message == 'File is in use.' 191 | _add_db_record(0) 192 | _add_db_record(1) 193 | _add_db_record(2) 194 | _add_db_record(3) 195 | assert DB.cpdbf() == 0 196 | assert DB.fcount() == 4 197 | DB.alter_table('report', 'add', 'age n(3, 0)') 198 | assert DB.fcount() == 5 199 | assert DB.field(2) == 'st' 200 | assert not DB.found() 201 | DB.goto(None, 0) 202 | M.add_local('loopcount') 203 | S.loopcount = 0 204 | for _ in DB.scanner(scope=('rest',)): 205 | assert len(S.name.strip()) > 0 206 | S.loopcount += 1 207 | assert S.loopcount == 4 208 | DB.goto(None, 3) 209 | S.loopcount = 0 210 | for _ in DB.scanner(condition=lambda: S.st.strip() == 'ID', scope=('all',)): 211 | assert len(S.name.strip()) > 0 212 | S.loopcount += 1 213 | assert S.loopcount == 2 214 | S.loopcount = 0 215 | for _ in DB.scanner(condition=lambda: S.st.strip() == 'ID', scope=('rest',)): 216 | assert len(S.name.strip()) > 0 217 | S.loopcount += 1 218 | assert S.loopcount == 0 219 | DB.goto(None, 0) 220 | S.loopcount = 0 221 | for _ in DB.scanner(condition=lambda: S.st.strip() == 'ID', scope=('rest',)): 222 | assert len(S.name.strip()) > 0 223 | S.loopcount += 1 224 | assert S.loopcount == 2 225 | del M.loopcount 226 | assert S.name.strip() == 'Norma Fisher', S.name.strip() + ' should be Norma Fisher' 227 | assert DB.recno() == 1 228 | S.report_record = vfpfunc.scatter(totype='name') 229 | assert S.report_record.name.strip() == 'Norma Fisher', S.report_record.name.strip() + ' should be Norma Fisher' 230 | DB.goto(None, -1) 231 | assert S.name.strip() == 'Joshua Wood', S.name.strip() + ' should be Joshua Wood' 232 | assert S.report_record.name.strip() == 'Norma Fisher', S.report_record.name.strip() + ' should be Norma Fisher' 233 | assert DB.recno() == 4 234 | DB.goto(None, 1) 235 | DB.locate(for_cond=lambda: S.st == 'ID') 236 | assert S.name.strip() == 'Norma Fisher', S.name.strip() + ' should be Norma Fisher' 237 | assert DB.found() 238 | DB.continue_locate() 239 | assert S.name.strip() == 'Ryan Gallagher', S.name.strip() + ' should be Ryan Gallagher' 240 | DB.continue_locate() 241 | assert DB.eof() 242 | assert DB.recno() == DB.reccount() + 1 243 | assert not DB.found() 244 | S.countval = DB.count(None, ('all',), for_cond=lambda: S.quantity > 60) 245 | assert S.countval == 2 246 | assert DB.eof() 247 | S.sumval = DB.sum(None, ('rest',), lambda: math.sqrt(S.quantity + 205), for_cond=lambda: S.quantity > 50, while_cond=lambda: S.quantity != 63) 248 | assert S.sumval == 0 249 | DB.goto(None, 0) 250 | S.sumval = DB.sum(None, ('rest',), lambda: math.sqrt(S.quantity + 205), for_cond=lambda: S.quantity > 50, while_cond=lambda: S.quantity != 63) 251 | assert S.sumval == 17 + 16 252 | DB.index_on('st', 'st', 'ascending', True, False, False) 253 | DB.seek(None, 'CA') 254 | assert S.st.strip() == 'CA' 255 | DB.goto(None, 0) 256 | DB.delete_record(None, ('rest',), for_cond=lambda: S.quantity > 60) 257 | DB.pack('both', None, None) 258 | DB.goto(None, 0) 259 | assert DB.reccount() == 2 260 | DB.replace('report', ('next', 1), 'name', 'N/A') 261 | assert S.name.strip() == 'N/A' 262 | DB.replace(None, ('all',), 'name', 'Not Available') 263 | assert DB.recno() == DB.reccount() + 1 264 | DB.goto(None, -1) 265 | assert S.name.strip() == 'Not Available' 266 | DB.zap(None) 267 | assert DB.reccount() == 0 268 | DB.copy_structure('report2') 269 | DB.use('report2', 0, 'shared', alias='somethingelse') 270 | assert DB.alias() == 'report' 271 | DB.select('report2') 272 | assert DB.alias() == 'somethingelse' 273 | assert DB.fcount() == 5 274 | DB.alter_table('report2', 'drop', 'st') 275 | assert DB.fcount() == 4 276 | assert S.report_record.name.strip() == 'Norma Fisher', S.report_record.name.strip() + ' should be Norma Fisher' 277 | DB.append(None, False) 278 | vfpfunc.gather(val=S.report_record) 279 | assert S.name.strip() == 'Norma Fisher', S.name.strip() + ' should be Norma Fisher' 280 | assert DB.dbf() == 'report2.dbf' 281 | DB.use(None, DB.select_function('report2'), None) 282 | DB.use(None, DB.select_function('report'), None) 283 | os.remove('report2.dbf') 284 | except Exception as err: 285 | S.err = vfpfunc.Exception.from_pyexception(err) 286 | print(S.err.message) 287 | DB.browse() 288 | raise 289 | finally: 290 | os.remove('report.dbf') 291 | S.sqlconn = vfpfunc.sqlconnect('testodbc') 292 | assert S.sqlconn > 0 293 | assert vfpfunc.sqlexec(S.sqlconn, 'CREATE TABLE REPORT (NAME varchar(50), ST char(2), QUANTITY int(5), RECEIVED bit)') > 0 294 | assert _sqlexec_add_record(S.sqlconn, 0) > 0 295 | assert _sqlexec_add_record(S.sqlconn, 1) > 0 296 | assert _sqlexec_add_record(S.sqlconn, 2) > 0 297 | assert _sqlexec_add_record(S.sqlconn, 3) > 0 298 | assert vfpfunc.sqlexec(S.sqlconn, 'SELECT * FROM REPORT') 299 | DB.select('sqlresult') 300 | assert S.name.strip() == 'Norma Fisher' 301 | vfpfunc.sqlcommit(S.sqlconn) 302 | vfpfunc.sqldisconnect(S.sqlconn) 303 | S.sqlconn = vfpfunc.sqlstringconnect('dsn=testodbc') 304 | assert S.sqlconn > 0 305 | assert vfpfunc.sqltables(S.sqlconn) > 0 306 | DB.select('sqlresult') 307 | assert S.table_name.strip().lower() == 'report' 308 | assert vfpfunc.sqlexec(S.sqlconn, 'DELETE FROM REPORT;') 309 | assert vfpfunc.sqlrollback(S.sqlconn) 310 | assert vfpfunc.sqlexec(S.sqlconn, 'SELECT * FROM REPORT') 311 | DB.select('sqlresult') 312 | assert S.name.strip() == 'Norma Fisher' 313 | assert vfpfunc.sqlexec(S.sqlconn, 'DROP TABLE REPORT') > 0 314 | vfpfunc.sqlcommit(S.sqlconn) 315 | vfpfunc.sqldisconnect(S.sqlconn) 316 | # FIX ME: close tables 317 | 318 | 319 | @lparameters('arg1', 'arg2') 320 | def user_defined_function(): 321 | assert vfpfunc.pcount() == 1 322 | assert vfpfunc.PARAMETERS == 1 323 | user_defined_function2() 324 | assert vfpfunc.PARAMETERS == 0 325 | assert vfpfunc.pcount() == 1 326 | 327 | 328 | @lparameters('arg1') 329 | def user_defined_function2(): 330 | pass 331 | 332 | 333 | @lparameters() 334 | def scope_tests(): 335 | M.add_public(somearray=Array(2, 5)) 336 | M.add_public(**{'def': Array(10)}) 337 | assert F['def'](1) == False 338 | S.somearray[1, 4] = 3 339 | assert F.somearray(1, 4) == 3 340 | M.add_private('test', somearray=Array(2, 5)) 341 | del M.nonexistantvariable 342 | 343 | vfpfunc.set('procedure', 'time', set_value=True) 344 | print(F.localtime()) 345 | 346 | vfpfunc.set('procedure', 'argparse', set_value=True) 347 | S.t = vfpfunc.create_object('Namespace') 348 | 349 | user_defined_function(False) 350 | 351 | 352 | @vfpclass 353 | def Someclass(): 354 | BaseClass = vfpfunc.Custom 355 | 356 | class Someclass(BaseClass): 357 | @lparameters() 358 | def _assign(self): 359 | BaseClass._assign(self) 360 | return Someclass 361 | 362 | 363 | @lparameters() 364 | def class_tests(): 365 | S.obj = Someclass() 366 | -------------------------------------------------------------------------------- /vfp2py/vfpdatabase.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | from __future__ import absolute_import, division, print_function 3 | 4 | import os 5 | import inspect 6 | 7 | from argparse import Namespace 8 | 9 | import dbf 10 | 11 | 12 | class DatabaseWorkspace(object): 13 | def __init__(self, tablename, alias=None): 14 | self.name = (alias or '').lower() 15 | self.table = dbf.Table(tablename) 16 | self.table.open(dbf.READ_WRITE) 17 | if len(self.table) > 0: 18 | self.table.goto(0) 19 | self.locate = None 20 | self.found = False 21 | self.seek = None 22 | self.index = {} 23 | self.current_index = None 24 | 25 | def _is_alias(self, alias): 26 | alias = alias.lower() 27 | filename = self.table.filename.lower() 28 | possible_aliases = ( 29 | self.name, 30 | filename, 31 | os.path.splitext(filename)[0], 32 | os.path.basename(filename), 33 | os.path.basename(os.path.splitext(filename)[0]), 34 | ) 35 | return alias in possible_aliases 36 | 37 | class DatabaseContext(object): 38 | def __init__(self): 39 | self.close_tables(None) 40 | self.current_table = 0 41 | 42 | def get_workarea(self, tablename=None): 43 | if not tablename: 44 | ind = self.current_table 45 | elif isinstance(tablename, (int, float)): 46 | ind = tablename 47 | else: 48 | try: 49 | ind = next(idx for idx, open_table in enumerate(self.open_tables) if open_table and open_table._is_alias(tablename)) 50 | except StopIteration: 51 | raise Exception('Table is not currently open') 52 | return ind 53 | 54 | def select_function(self, tablename): 55 | try: 56 | if isinstance(tablename, float): 57 | tablename = int(tablename) 58 | if isinstance(tablename, int): 59 | if tablename == 0: 60 | return self.current_table + 1 61 | elif tablename == 1: 62 | return next(len(self.open_tables) - i for i, table in enumerate(reversed(self.open_tables)) if table is None) 63 | else: 64 | return tablename if self.open_tables[tablename - 1] else 0 65 | if not tablename: 66 | return next(i+1 for i, table in enumerate(self.open_tables) if table is None) 67 | else: 68 | return self.get_workarea(tablename) + 1 69 | except: 70 | return 0 71 | 72 | def _get_table_info(self, tablename=None): 73 | return self.open_tables[self.get_workarea(tablename)] 74 | 75 | @property 76 | def _current_record(self): 77 | return self._get_table_info().table.current_record 78 | 79 | def _current_record_copy(self): 80 | record = Namespace() 81 | table = self._get_table_info().table 82 | for field in table.field_names: 83 | setattr(record, field, table.current_record[field]) 84 | return record 85 | 86 | def _update_from(self, val): 87 | record = self._current_record 88 | if not record: 89 | return 90 | fields = dbf.source_table(record).field_names 91 | update = {field: getattr(val, field) for field in fields if hasattr(val, field)} 92 | if not update: 93 | try: 94 | update = {field: v for field, v in zip(fields, val)} 95 | except: 96 | return 97 | dbf.write(record, **update) 98 | 99 | def create_table(self, tablename, setup_string, free): 100 | if free == 'free': 101 | dbf.Table(tablename, setup_string) 102 | self.use(tablename, 0, 'shared') 103 | 104 | def alter_table(self, tablename, alter_type, field_specs): 105 | if alter_type == 'add': 106 | self._get_table_info(tablename).table.add_fields(field_specs) 107 | else: 108 | self._get_table_info(tablename).table.delete_fields(field_specs) 109 | 110 | def select(self, tablename): 111 | self.current_table = self.get_workarea(tablename) 112 | 113 | def use(self, tablename, workarea, opentype, alias=None): 114 | if tablename is None: 115 | if workarea is 0: 116 | return 117 | self.open_tables[self.get_workarea(workarea) - 1] = None 118 | return 119 | if self.used(tablename): 120 | raise Exception('File is in use.') 121 | if workarea == 0: 122 | workarea = self.open_tables.index(None) 123 | self.open_tables[workarea] = DatabaseWorkspace(tablename, alias) 124 | 125 | def alias(self, tablename=None): 126 | table_info = self._get_table_info(tablename) 127 | return table_info.name or os.path.splitext(os.path.basename(table_info.table.filename))[0] 128 | 129 | def dbf(self, tablename=None): 130 | try: 131 | return self._get_table_info(tablename).table.filename 132 | except: 133 | pass 134 | 135 | def used(self, tablename): 136 | try: 137 | self.get_workarea(tablename) 138 | return True 139 | except: 140 | return False 141 | 142 | def append(self, tablename, editwindow): 143 | table_info = self._get_table_info(tablename) 144 | table_info.table.append() 145 | self.goto(tablename, -1) 146 | 147 | def append_from(self, tablename, from_source, from_type='dbf'): 148 | table = self._get_table_info(tablename).table 149 | if from_type == 'dbf': 150 | with dbf.Table(from_source) as from_table: 151 | for record in from_table: 152 | table.append(record) 153 | 154 | def replace(self, tablename, scope, field, value): 155 | if not tablename: 156 | field = field.lower().rsplit('.', 1) 157 | tablename = field[0] if len(field) == 2 else None 158 | field = field[-1] 159 | for record in self._get_records(tablename, scope): 160 | dbf.write(record, **{field: value}) 161 | 162 | def insert(self, tablename, values): 163 | table_info = self._get_table_info(tablename) 164 | table = table_info.table 165 | if values is None: 166 | local = inspect.stack()[1][0].f_locals 167 | scope = variable.current_scope() 168 | values = {field: scope[field] for field in table.field_names if field in scope} 169 | values.update({field: local[field] for field in table.field_names if field in local}) 170 | try: 171 | for i in range(1,values.alen(1)+1): 172 | table.append(tuple(values[i, j] for j in range(1,values.alen(2)+1))) 173 | except AttributeError: 174 | if not isinstance(values, (tuple, dict)): 175 | values = {field: getattr(values, field) for field in table.field_names if hasattr(values, field)} 176 | table.append(values) 177 | self.goto(tablename, -1) 178 | 179 | def skip(self, tablename, skipnum): 180 | try: 181 | self._get_table_info(tablename).table.skip(skipnum) 182 | except dbf.Eof: 183 | pass 184 | 185 | def goto(self, tablename, num): 186 | table = self._get_table_info(tablename).table 187 | num = num - 1 if num > 0 else num 188 | table.goto(num) 189 | 190 | def _get_records(self, tablename, scope, for_cond=None, while_cond=None): 191 | def reset(used_table, saved_current): 192 | if saved_current < 0: 193 | used_table.top() 194 | elif saved_current == len(used_table): 195 | used_table.bottom() 196 | else: 197 | used_table.goto(saved_current) 198 | save_current_table = self.current_table 199 | self.select(tablename) 200 | table = self._get_table_info().table 201 | if not table: 202 | self.current_table = save_current_table 203 | return 204 | save_current = table.current 205 | if scope[0].lower() == 'all': 206 | table.goto(0) 207 | if scope[0].lower() == 'record': 208 | table.goto(scope[1] - 1) 209 | condition = lambda table: table.current == scope[1] - 1 210 | elif scope[0].lower() == 'next': 211 | final_record = table.current + scope[1] 212 | condition = lambda table: table.current < final_record 213 | else: 214 | condition = lambda table: bool(table.current_record) 215 | if not for_cond: 216 | for_cond = lambda: True 217 | if not while_cond: 218 | while_cond = lambda: True 219 | if not table.current_record: 220 | self.current_table = save_current_table 221 | return 222 | while condition(table) and while_cond(): 223 | if for_cond(): 224 | yield table.current_record 225 | try: 226 | table.skip(1) 227 | except: 228 | break 229 | if scope[0].lower() in ('all', 'rest'): 230 | table.bottom() 231 | elif scope[0].lower() == 'next': 232 | reset(table, save_current + scope[1] - 1) 233 | elif scope[0].lower() == 'record': 234 | reset(table, scope[1] - 1) 235 | self.current_table = save_current_table 236 | 237 | def scanner(self, condition=None, scope=('rest',)): 238 | if not condition: 239 | condition = lambda: True 240 | workspace = self.current_table 241 | record = self.recno() 242 | t = self._get_table_info(workspace).table 243 | if scope[0] == 'all': 244 | self.goto(workspace, 0) 245 | if len(t) > 0 and condition(): 246 | yield 247 | while not self.eof(workspace): 248 | self.skip(workspace, 1) 249 | if not condition(): 250 | continue 251 | yield 252 | self.goto(workspace, record) 253 | 254 | def count(self, tablename, scope, **kwargs): 255 | return len(list(self._get_records(tablename, scope, **kwargs))) 256 | 257 | def copy_structure(self, tablename): 258 | dbf.Table(tablename, self._get_table_info().table.structure()) 259 | 260 | def delete_record(self, tablename, scope, recall=False, **kwargs): 261 | for record in self._get_records(tablename, scope, **kwargs): 262 | (dbf.undelete if recall else dbf.delete)(record) 263 | 264 | def sum(self, tablename, scope, sumexpr, **kwargs): 265 | return sum(sumexpr() for record in self._get_records(tablename, scope, **kwargs)) 266 | 267 | def pack(self, pack, tablename, workarea): 268 | if tablename: 269 | table = dbf.Table(tablename) 270 | table.open(dbf.READ_WRITE) 271 | table.pack() 272 | table.close() 273 | else: 274 | self._get_table_info(workarea).table.pack() 275 | 276 | def reindex(self, compact_flag): 277 | self._get_table_info().table.reindex() 278 | 279 | def index_on(self, field, indexname, order, tag_flag, compact_flag, unique_flag): 280 | table_info = self._get_table_info() 281 | table_info.index[indexname] = table_info.table.create_index(lambda rec, field=field: getattr(rec, field)) 282 | table_info.current_index = indexname 283 | 284 | def close_tables(self, all_flag): 285 | self.open_tables = [None] * 32767 286 | 287 | def recno(self): 288 | try: 289 | return dbf.recno(self.open_tables[self.current_table].table.current_record) + 1 290 | except: 291 | return 0 292 | 293 | def reccount(self): 294 | try: 295 | return len(self.open_tables[self.current_table].table) 296 | except: 297 | return 0 298 | 299 | def recsize(self, workarea=None): 300 | return self._get_table_info(workarea).table.record_length 301 | 302 | def bof(self, workarea=None): 303 | return self._get_table_info(workarea).table.bof 304 | 305 | def eof(self, workarea=None): 306 | return self._get_table_info(workarea).table.eof 307 | 308 | def deleted(self, workarea=None): 309 | return dbf.is_deleted(self._get_table_info(workarea).table.current_record) 310 | 311 | def locate(self, tablename=None, for_cond=None, while_cond=None, nooptimize=None): 312 | kwargs = locals() 313 | kwargs = {key: kwargs[key] for key in ('for_cond', 'while_cond') if kwargs[key] is not None} 314 | self._get_table_info(tablename).locate = (record for record in list(self._get_records(tablename, ('rest',), **kwargs))) 315 | self.continue_locate(tablename) 316 | 317 | def continue_locate(self, tablename=None): 318 | workarea = self._get_table_info(tablename) 319 | if not (workarea and workarea.locate): 320 | return 321 | try: 322 | record = next(workarea.locate) 323 | dbf.source_table(record).goto(dbf.recno(record)) 324 | workarea.found = True 325 | except StopIteration: 326 | workarea.table.bottom() 327 | workarea.locate = None 328 | workarea.found = False 329 | 330 | def seek(self, tablename, key_expr, key_index=None, key_index_file=None, descending=False): 331 | table_info = self._get_table_info(tablename) 332 | if not(table_info.seek and not key_index): 333 | table_info.seek = (record for record in table_info.index[table_info.current_index].search((key_expr,))) 334 | try: 335 | record = next(table_info.seek) 336 | except StopIteration: 337 | table_info.table.bottom() 338 | table_info.found = False 339 | return 340 | dbf.source_table(record).goto(dbf.recno(record)) 341 | table_info.found = True 342 | 343 | def found(self, tablename=None): 344 | workarea = self._get_table_info(tablename) 345 | return workarea.found if workarea else False 346 | 347 | def browse(self): 348 | table_info = self._get_table_info() 349 | table = table_info.table 350 | print('Tablename:', table_info.name) 351 | print(table.field_names) 352 | for record in table: 353 | print('->' if record is table.current_record else ' ', record[:]) 354 | 355 | def zap(self, tablename): 356 | self._get_table_info().table.zap() 357 | 358 | def field(self, field_item, tablename=None, flag=0): 359 | try: 360 | return self._get_table_info(tablename).table.field_names[field_item - 1] 361 | except: 362 | return 0 363 | 364 | def afields(self): 365 | pass 366 | 367 | def fcount(self, tablename=None): 368 | try: 369 | return self._get_table_info().table.field_count 370 | except: 371 | return 0 372 | 373 | def cpdbf(self, tablename=None): 374 | try: 375 | return self._get_table_info(tablename).table.codepage.code 376 | except AttributeError: 377 | return 0 378 | -------------------------------------------------------------------------------- /vfp2py/vfp2py.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | from __future__ import absolute_import, division, print_function 3 | 4 | import sys 5 | import logging 6 | import os 7 | import ntpath 8 | import time 9 | import re 10 | import tempfile 11 | import shutil 12 | import tokenize 13 | import io 14 | 15 | import dbf 16 | 17 | import antlr4 18 | 19 | import autopep8 20 | 21 | from .VisualFoxpro9Lexer import VisualFoxpro9Lexer 22 | from .VisualFoxpro9Parser import VisualFoxpro9Parser 23 | from .VisualFoxpro9Visitor import VisualFoxpro9Visitor 24 | 25 | from . import vfpfunc 26 | from .vfp2py_convert_visitor import PythonConvertVisitor, CodeStr 27 | 28 | SEARCH_PATH = ['.'] 29 | INCLUDE = {} 30 | 31 | def which(filename): 32 | '''find file on path''' 33 | for path in filter(None, SEARCH_PATH): 34 | testpath = os.path.join(path, filename) 35 | if os.path.isfile(testpath): 36 | return testpath 37 | return filename 38 | 39 | 40 | 41 | class PreprocessVisitor(VisualFoxpro9Visitor): 42 | def __init__(self, encoding): 43 | self.tokens = None 44 | self.memory = {} 45 | self.encoding = encoding 46 | 47 | def visitPreprocessorCode(self, ctx): 48 | return self.visit(ctx.preprocessorLines()) 49 | 50 | def visitPreprocessorLines(self, ctx): 51 | lines = [] 52 | for line in ctx.preprocessorLine(): 53 | lines += self.visit(line) 54 | return lines 55 | 56 | def visitPreprocessorDefine(self, ctx): 57 | name = ctx.identifier().getText().lower() 58 | namestart, _ = ctx.identifier().getSourceInterval() 59 | _, stop = ctx.getSourceInterval() 60 | tokens = ctx.parser._input.tokens[namestart+1:stop] 61 | while len(tokens) > 0 and tokens[0].type == ctx.parser.WS: 62 | tokens.pop(0) 63 | while len(tokens) > 0 and tokens[-1].type in (ctx.parser.WS, ctx.parser.COMMENT): 64 | tokens.pop() 65 | self.memory[name] = tokens 66 | return [] 67 | 68 | def visitPreprocessorUndefine(self, ctx): 69 | name = ctx.identifier().getText().lower() 70 | self.memory.pop(name) 71 | return [] 72 | 73 | def visitPreprocessorInclude(self, ctx): 74 | visitor = PythonConvertVisitor('') 75 | visitor.scope = {} 76 | TreeCleanVisitor().visit(ctx.specialExpr()) 77 | filename = visitor.visit(ctx.specialExpr()) 78 | if isinstance(filename, CodeStr): 79 | filename = eval(filename) 80 | filename = which(filename) 81 | if filename in INCLUDE: 82 | include_visitor = INCLUDE[filename] 83 | else: 84 | include_visitor = preprocess_file(filename, self.encoding) 85 | INCLUDE[filename] = include_visitor 86 | self.memory.update(include_visitor.memory) 87 | return include_visitor.tokens 88 | 89 | def visitPreprocessorIf(self, ctx): 90 | if ctx.IF(): 91 | ifexpr = ''.join(x.text for x in self.replace_define_tokens(ctx.expr())) 92 | ifexpr = eval(repr(prg2py_after_preproc(ifexpr, 'expr', ''))) 93 | else: 94 | name = ctx.identifier().getText().lower() 95 | ifexpr = name in self.memory 96 | if ifexpr: 97 | return self.visit(ctx.ifBody) 98 | elif ctx.ELSE(): 99 | return self.visit(ctx.elseBody) 100 | else: 101 | return [] 102 | 103 | def replace_define_tokens(self, ctx): 104 | start, stop = ctx.getSourceInterval() 105 | hidden_tokens = ctx.parser._input.getHiddenTokensToLeft(start) 106 | retval = [] 107 | process_tokens = (hidden_tokens if hidden_tokens else []) + ctx.parser._input.tokens[start:stop+1] 108 | hidden_tokens = [] 109 | for tok in process_tokens: 110 | if tok.text.lower() in self.memory: 111 | retval += self.memory[tok.text.lower()] 112 | else: 113 | if tok.type == ctx.parser.COMMENT: 114 | tok.text = '*' + tok.text[2:] + '\n' 115 | hidden_tokens.append(tok) 116 | continue 117 | retval.append(tok) 118 | return hidden_tokens + retval 119 | 120 | def visitNonpreprocessorLine(self, ctx): 121 | return self.replace_define_tokens(ctx) 122 | 123 | def add_indents(struct, num_indents): 124 | retval = [] 125 | for item in struct: 126 | if isinstance(item, list): 127 | retval.append(add_indents(item, num_indents+1)) 128 | elif item: 129 | retval.append(' '*4*num_indents + repr(item)) 130 | else: 131 | retval.append('') 132 | return '\n'.join(retval) 133 | 134 | def contains_exceptions(ctx): 135 | return (isinstance(ctx, ctx.parser.AtomExprContext) and ctx.trailer() and isinstance(ctx.trailer(), ctx.parser.FuncCallTrailerContext)) or \ 136 | isinstance(ctx, ctx.parser.ConstantExprContext) or \ 137 | isinstance(ctx, ctx.parser.SubExprContext) or \ 138 | any(contains_exceptions(c) for c in ctx.children if isinstance(c, ctx.parser.ExprContext)) 139 | 140 | class TreeCleanVisitor(VisualFoxpro9Visitor): 141 | def visitSpecialExpr(self, ctx): 142 | if ctx.pathname(): 143 | return 144 | 145 | start, stop = ctx.getSourceInterval() 146 | stream = ctx.parser._input 147 | tokens = stream.tokens[start:stop+1] 148 | 149 | if not (any(tok.type == ctx.parser.WS for tok in tokens) or contains_exceptions(ctx)): 150 | stream.seek(start) 151 | ctx.removeLastChild() 152 | pathname = VisualFoxpro9Parser(stream).pathname() 153 | ctx.addChild(pathname) 154 | pathname.stop = stream.tokens[stop] 155 | while pathname.children and pathname.children[-1].getSourceInterval()[0] > stop: 156 | pathname.removeLastChild() 157 | 158 | self.visitChildren(ctx) 159 | 160 | def visitSubExpr(self, ctx): 161 | self.visit(ctx.expr()) 162 | if isinstance(ctx.expr(), ctx.parser.SubExprContext): 163 | ctx.removeLastChild() 164 | newexpr = ctx.expr().expr() 165 | ctx.removeLastChild() 166 | ctx.addChild(newexpr) 167 | 168 | def visitPower(self, ctx): 169 | self.visitChildren(ctx) 170 | left, right = ctx.expr() 171 | if isinstance(right, ctx.parser.SubExprContext) and isinstance(right.expr(), ctx.parser.PowerContext): 172 | ctx.removeLastChild() 173 | right = right.expr() 174 | ctx.addChild(right) 175 | if isinstance(left, ctx.parser.PowerContext): 176 | newleft = ctx.parser.SubExprContext(ctx.parser, ctx.parser.ExprContext(ctx.parser, ctx)) 177 | newleft.addChild(left) 178 | while ctx.children: 179 | ctx.removeLastChild() 180 | ctx.addChild(newleft) 181 | ctx.addChild(right) 182 | 183 | def visitUnaryNegation(self, ctx): 184 | self.visit(ctx.expr()) 185 | if ctx.op.type == ctx.parser.MINUS_SIGN and isinstance(ctx.expr(), ctx.parser.UnaryNegationContext): 186 | ctx.expr().op.type = ctx.parser.PLUS_SIGN 187 | ctx.op.type = ctx.parser.PLUS_SIGN 188 | 189 | def preprocess_code(data, encoding): 190 | input_stream = antlr4.InputStream(data) 191 | lexer = VisualFoxpro9Lexer(input_stream) 192 | stream = antlr4.CommonTokenStream(lexer) 193 | parser = VisualFoxpro9Parser(stream) 194 | tree = run_parser(stream, parser, 'preprocessorCode') 195 | visitor = PreprocessVisitor(encoding) 196 | visitor.tokens = visitor.visit(tree) 197 | return visitor 198 | 199 | def preprocess_file(filename, encoding): 200 | with open(filename, 'rb') as fid: 201 | data = fid.read().decode(encoding) 202 | return preprocess_code(data, encoding) 203 | 204 | def find_file_ignore_case(filename, directories): 205 | for directory in directories: 206 | for testfile in os.listdir(directory): 207 | if testfile.lower() == filename.lower(): 208 | return os.path.join(directory, testfile) 209 | 210 | def memo_filename(filename, ext): 211 | directory = os.path.dirname(filename) or '.' 212 | basename = os.path.basename(filename) 213 | memofile = os.path.splitext(basename)[0] + '.' + ext 214 | return find_file_ignore_case(memofile, [directory]) 215 | 216 | def copy_obscured_dbf(filename, memo_ext, dbf_basename): 217 | memofile = memo_filename(filename, memo_ext) 218 | dbffile = dbf_basename + '.dbf' 219 | shutil.copy(filename, dbffile) 220 | if memofile: 221 | shutil.copy(memofile, dbf_basename + '.fpt') 222 | return dbffile 223 | 224 | def convert_vcx_to_vfp_code(mnxfile): 225 | with tempfile.NamedTemporaryFile() as tmpfile: 226 | pass 227 | tmpfile = tmpfile.name 228 | dbffile = copy_obscured_dbf(mnxfile, 'vct', tmpfile) 229 | 230 | codes = [] 231 | with dbf.Table(dbffile) as table: 232 | for record in table: 233 | code = '\n'.join('#include "{}"'.format(x) for x in record.reserved8.splitlines()) + '\n' 234 | if not (record.objname and record['class']): 235 | continue 236 | code += 'DEFINE CLASS {} AS {}\n'.format(record.objname, record['class']) 237 | props = [] 238 | for line in record.properties.splitlines(): 239 | if not line: 240 | props.append('') 241 | continue 242 | prop, value = line.split(' = ', 1) 243 | if not value: 244 | value = '""' 245 | elif re.match(r'^[0-9]*,[0-9]*,[0-9]*$', value): 246 | value = 'RGB({})'.format(value) 247 | elif re.match(r'^\(.*\)$', value): 248 | pass 249 | else: 250 | if value.startswith('-'): 251 | input_stream = antlr4.InputStream(value[1:]) 252 | else: 253 | input_stream = antlr4.InputStream(value) 254 | lexer = VisualFoxpro9Lexer(input_stream) 255 | stream = antlr4.CommonTokenStream(lexer) 256 | parser = VisualFoxpro9Parser(stream) 257 | parser._interp.PredictionMode = antlr4.PredictionMode.SLL 258 | parser.removeErrorListeners() 259 | parser._errHandler = antlr4.error.ErrorStrategy.BailErrorStrategy() 260 | try: 261 | tree = parser.constant() 262 | TreeCleanVisitor().visit(tree) 263 | output_tree = PythonConvertVisitor('').visit(tree) 264 | except: 265 | if '"' not in value: 266 | format_string = '"{}"' 267 | else: 268 | if "'" not in value: 269 | format_string = "'{}'" 270 | else: 271 | if '[' not in value and ']' not in value: 272 | format_string = '[{}]' 273 | else: 274 | format_string = '{}' 275 | value = format_string.format(value) 276 | 277 | props.append('{} = {}'.format(prop.strip(), value)) 278 | 279 | code += '\n'.join(props) + '\n\n' 280 | code += '\n'.join(record.methods.splitlines()) + '\n' 281 | code += 'ENDDEFINE\n\n' 282 | codes.append(code) 283 | 284 | os.remove(table.filename) 285 | os.remove(table.memoname) 286 | 287 | return codes 288 | 289 | def convert_scx_to_vfp_code(scxfile): 290 | with tempfile.NamedTemporaryFile() as tmpfile: 291 | pass 292 | tmpfile = tmpfile.name 293 | dbffile = copy_obscured_dbf(scxfile, 'sct', tmpfile) 294 | 295 | table = dbf.Table(dbffile) 296 | table.open() 297 | 298 | children = [list() for record in table] 299 | names = [record.objname for record in table] 300 | for record in table: 301 | if record['class'].lower() == 'form': 302 | form = record.objname 303 | parent = record.parent 304 | if not parent: 305 | continue 306 | parent_ind = names.index(parent) 307 | children[parent_ind].append(record) 308 | 309 | code = [l.format(form, form) for l in ('local {}', '{} = createObject("{}")', '{}.show()')] 310 | for record, child in zip(table, children): 311 | if not record.objname or record.parent or record['class'].lower() == 'dataenvironment': 312 | continue 313 | 314 | code.append('DEFINE CLASS {} AS {}'.format(record.objname, record['class'])) 315 | subcode = [] 316 | for line in record.properties.split('\r\n'): 317 | subcode.append(line) 318 | for child_record in child: 319 | subcode.append('ADD OBJECT {} AS {}'.format(child_record.objname, child_record['class'])) 320 | for line in child_record.properties.split('\r\n'): 321 | line = line.strip() 322 | if not line: 323 | continue 324 | prop, value = line.split(' = ') 325 | if prop == 'Picture': 326 | value = '"{}"'.format(value) 327 | elif prop.endswith('Color'): 328 | value = 'RGB({})'.format(value) 329 | subcode.append(child_record.objname + '.' + prop + ' = ' + value) 330 | subcode.append('') 331 | 332 | for line in record.methods.split('\r\n'): 333 | subcode.append(line) 334 | subcode.append('') 335 | for child_record in child: 336 | for line in child_record.methods.split('\r\n'): 337 | if not line: 338 | continue 339 | line = re.sub(r'PROCEDURE ', 'PROCEDURE {}.'.format(child_record.objname), line) 340 | subcode.append(line) 341 | subcode.append('') 342 | code.append(subcode) 343 | code.append('ENDDEFINE') 344 | code.append('') 345 | 346 | def add_indent(code, level): 347 | retval = '' 348 | for line in code: 349 | if isinstance(line, list): 350 | retval += add_indent(line, level+1) 351 | else: 352 | retval += ' '*level + line + '\n' 353 | return retval 354 | 355 | table.close() 356 | os.remove(table.filename) 357 | os.remove(table.memoname) 358 | 359 | code = add_indent(code, 0) 360 | 361 | code = re.sub(r'(\n\s*)+\n+', '\n\n', code) 362 | return code 363 | 364 | def find_full_path(pathname, start_directory): 365 | name_parts = ntpath.split(pathname) 366 | while ntpath.split(name_parts[0])[1]: 367 | name_parts = ntpath.split(name_parts[0]) + name_parts[1:] 368 | pathname = start_directory 369 | for part in name_parts: 370 | if part in ('..', '.', ''): 371 | pathname = os.path.abspath(os.path.join(pathname, part)) 372 | continue 373 | next_part = find_file_ignore_case(part, [pathname]) 374 | if next_part is None: 375 | badind = name_parts.index(part) 376 | return os.path.join(pathname, *name_parts[badind:]), True 377 | pathname = next_part 378 | return pathname, False 379 | 380 | def read_vfp_project(pjxfile): 381 | directory = os.path.dirname(pjxfile) 382 | with tempfile.NamedTemporaryFile() as tmpfile: 383 | pass 384 | tmpfile = tmpfile.name 385 | dbffile = copy_obscured_dbf(pjxfile, 'pjt', tmpfile) 386 | 387 | table = dbf.Table(dbffile) 388 | table.open() 389 | 390 | files = {} 391 | main_file = '' 392 | 393 | for record in table: 394 | if dbf.is_deleted(record) or record.exclude is None or record.exclude: 395 | continue 396 | name, failed = find_full_path(record.name.rstrip('\x00'), directory) 397 | if failed: 398 | files[name] = None 399 | else: 400 | files[os.path.basename(name).lower()] = name 401 | if record.mainprog: 402 | main_file = os.path.basename(name).lower() 403 | 404 | table.close() 405 | os.remove(table.filename) 406 | os.remove(table.memoname) 407 | 408 | return files, main_file 409 | 410 | def convert_project(infile, directory, encoding): 411 | project_files, main_file = read_vfp_project(infile) 412 | global SEARCH_PATH 413 | search = SEARCH_PATH 414 | search += [project_files[name] for name in project_files] 415 | if not os.path.isdir(directory): 416 | os.mkdir(directory) 417 | directory = os.path.join(directory, os.path.basename(directory)) 418 | if not os.path.isdir(directory): 419 | os.mkdir(directory) 420 | for name in project_files: 421 | outfile = directory 422 | args = [project_files[name], outfile] + search 423 | try: 424 | print('processing {}'.format(name)) 425 | SEARCH_PATH = search 426 | convert_file(project_files[name] or name, outfile, encoding) 427 | except Exception as err: 428 | logging.getLogger().exception(err) 429 | print('failed to convert {}'.format(name)) 430 | if 'config.fpw' in project_files: 431 | with open(project_files['config.fpw']) as fid: 432 | import ConfigParser 433 | import io 434 | config_data = io.StringIO('[config]\r\n' + fid.read().decode('utf-8')) 435 | config = ConfigParser.RawConfigParser() 436 | config.readfp(config_data) 437 | config = {x[0]: x[1] for x in config.items('config')} 438 | else: 439 | config = {} 440 | name = os.path.splitext(main_file)[0] 441 | with open(os.path.join(directory, '__main__.py'), 'wb') as fid: 442 | import pprint 443 | pp = pprint.PrettyPrinter(indent=4) 444 | print('import {}'.format(name, name), file=fid) 445 | print(file=fid) 446 | print('config = {}'.format(pp.pformat(config)), file=fid) 447 | print(file=fid) 448 | print('{}.MAIN()'.format(name), file=fid) 449 | with open(os.path.join(directory, '__init__.py'), 'wb') as fid: 450 | pass 451 | directory = os.path.dirname(directory) 452 | with open(os.path.join(directory, 'setup.py'), 'wb') as fid: 453 | pass 454 | 455 | class ParseKill(antlr4.error.ErrorListener.ErrorListener): 456 | def syntaxError(self, parser, token, line, char, msg, unknown): 457 | linetxt = token.getInputStream().strdata.splitlines()[line - 1].strip() 458 | raise Exception('Syntax Error on line {}: {}'.format(line, linetxt)) 459 | 460 | def run_parser(stream, parser, parser_start): 461 | parser._interp.PredictionMode = antlr4.PredictionMode.SLL 462 | parser.removeErrorListeners() 463 | parser._errHandler = antlr4.error.ErrorStrategy.BailErrorStrategy() 464 | try: 465 | return getattr(parser, parser_start)() 466 | except antlr4.error.Errors.ParseCancellationException as err: 467 | stream.reset(); 468 | parser.reset(); 469 | parser.addErrorListener(ParseKill()) 470 | parser._errHandler = antlr4.error.ErrorStrategy.DefaultErrorStrategy() 471 | parser._interp.PredictionMode = antlr4.PredictionMode.LL 472 | return getattr(parser, parser_start)() 473 | 474 | def prg2py_after_preproc(data, parser_start, input_filename): 475 | input_stream = antlr4.InputStream(data) 476 | lexer = VisualFoxpro9Lexer(input_stream) 477 | stream = antlr4.CommonTokenStream(lexer) 478 | parser = VisualFoxpro9Parser(stream) 479 | tree = run_parser(stream, parser, parser_start) 480 | TreeCleanVisitor().visit(tree) 481 | output_tree = PythonConvertVisitor(input_filename).visit(tree) 482 | if not isinstance(output_tree, list): 483 | return output_tree 484 | output = add_indents(output_tree, 0) 485 | options = autopep8.parse_args(['--max-line-length', '100000', '-']) 486 | output = autopep8.fix_code(output, options) 487 | tokens = list(tokenize.generate_tokens(io.StringIO(output).readline)) 488 | for i, token in enumerate(tokens): 489 | token = list(token) 490 | if token[0] == tokenize.STRING and token[1].startswith('u'): 491 | token[1] = token[1][1:] 492 | tokens[i] = tuple(token) 493 | return tokenize.untokenize(tokens) 494 | 495 | def prg2py(data, encoding, parser_start='prg', prepend_data='procedure _program_main\n', input_filename=''): 496 | tokens = preprocess_code(data, encoding).tokens 497 | data = prepend_data + ''.join(token.text.replace('\r', '') for token in tokens) 498 | return prg2py_after_preproc(data, parser_start, input_filename) 499 | 500 | def convert_file(infile, outfile, encoding): 501 | file_ext = os.path.splitext(infile.lower())[1] 502 | if file_ext == '.pjx': 503 | convert_project(infile, outfile, encoding) 504 | return 505 | elif file_ext in ('.prg', '.mpr', '.spr', '.scx', '.vcx'): 506 | if os.path.isdir(outfile): 507 | basename = os.path.splitext(os.path.basename(infile).lower())[0] 508 | suffix = '' if file_ext == '.prg' else file_ext.replace('.', '_') 509 | name = basename + suffix + '.py' 510 | outfile = os.path.join(outfile, name) 511 | if os.path.isfile(outfile): 512 | return 513 | if file_ext == '.scx': 514 | data = convert_scx_to_vfp_code(infile) 515 | tokens = preprocess_code(data, encoding).tokens 516 | elif file_ext == '.vcx': 517 | datas = convert_vcx_to_vfp_code(infile) 518 | tokens = [token for data in datas for token in preprocess_code(data, encoding).tokens] 519 | else: 520 | tokens = preprocess_file(infile, encoding).tokens 521 | elif file_ext in ('.frx', '.mnx', '.fll', '.app'): 522 | print('{} files not currently supported'.format(file_ext)) 523 | return 524 | elif file_ext in ('.fpw', '.h'): 525 | return 526 | else: 527 | if os.path.isdir(outfile): 528 | name = os.path.basename(infile).lower() 529 | shutil.copy(infile, os.path.join(outfile, name)) 530 | return 531 | data = 'procedure _program_main\n' + ''.join(token.text.replace('\r', '') for token in tokens) 532 | with tempfile.NamedTemporaryFile(suffix='.prg') as fid: 533 | pass 534 | with open(fid.name, 'wb') as fid: 535 | fid.write(data.encode('cp1252')) 536 | output = prg2py_after_preproc(data, 'prg', os.path.splitext(os.path.basename(infile))[0]) 537 | with open(outfile, 'wb') as fid: 538 | fid.write(('# coding=utf-8\n' + output).encode('utf-8')) 539 | -------------------------------------------------------------------------------- /vfp2py/VisualFoxpro9Visitor.py: -------------------------------------------------------------------------------- 1 | # Generated from VisualFoxpro9.g4 by ANTLR 4.11.1 2 | from antlr4 import * 3 | if __name__ is not None and "." in __name__: 4 | from .VisualFoxpro9Parser import VisualFoxpro9Parser 5 | else: 6 | from VisualFoxpro9Parser import VisualFoxpro9Parser 7 | 8 | # This class defines a complete generic visitor for a parse tree produced by VisualFoxpro9Parser. 9 | 10 | class VisualFoxpro9Visitor(ParseTreeVisitor): 11 | 12 | # Visit a parse tree produced by VisualFoxpro9Parser#preprocessorCode. 13 | def visitPreprocessorCode(self, ctx:VisualFoxpro9Parser.PreprocessorCodeContext): 14 | return self.visitChildren(ctx) 15 | 16 | 17 | # Visit a parse tree produced by VisualFoxpro9Parser#preprocessorLines. 18 | def visitPreprocessorLines(self, ctx:VisualFoxpro9Parser.PreprocessorLinesContext): 19 | return self.visitChildren(ctx) 20 | 21 | 22 | # Visit a parse tree produced by VisualFoxpro9Parser#preprocessorIf. 23 | def visitPreprocessorIf(self, ctx:VisualFoxpro9Parser.PreprocessorIfContext): 24 | return self.visitChildren(ctx) 25 | 26 | 27 | # Visit a parse tree produced by VisualFoxpro9Parser#preprocessorDefine. 28 | def visitPreprocessorDefine(self, ctx:VisualFoxpro9Parser.PreprocessorDefineContext): 29 | return self.visitChildren(ctx) 30 | 31 | 32 | # Visit a parse tree produced by VisualFoxpro9Parser#preprocessorUndefine. 33 | def visitPreprocessorUndefine(self, ctx:VisualFoxpro9Parser.PreprocessorUndefineContext): 34 | return self.visitChildren(ctx) 35 | 36 | 37 | # Visit a parse tree produced by VisualFoxpro9Parser#preprocessorInclude. 38 | def visitPreprocessorInclude(self, ctx:VisualFoxpro9Parser.PreprocessorIncludeContext): 39 | return self.visitChildren(ctx) 40 | 41 | 42 | # Visit a parse tree produced by VisualFoxpro9Parser#preprocessorJunk. 43 | def visitPreprocessorJunk(self, ctx:VisualFoxpro9Parser.PreprocessorJunkContext): 44 | return self.visitChildren(ctx) 45 | 46 | 47 | # Visit a parse tree produced by VisualFoxpro9Parser#nonpreprocessorLine. 48 | def visitNonpreprocessorLine(self, ctx:VisualFoxpro9Parser.NonpreprocessorLineContext): 49 | return self.visitChildren(ctx) 50 | 51 | 52 | # Visit a parse tree produced by VisualFoxpro9Parser#prg. 53 | def visitPrg(self, ctx:VisualFoxpro9Parser.PrgContext): 54 | return self.visitChildren(ctx) 55 | 56 | 57 | # Visit a parse tree produced by VisualFoxpro9Parser#lineComment. 58 | def visitLineComment(self, ctx:VisualFoxpro9Parser.LineCommentContext): 59 | return self.visitChildren(ctx) 60 | 61 | 62 | # Visit a parse tree produced by VisualFoxpro9Parser#line. 63 | def visitLine(self, ctx:VisualFoxpro9Parser.LineContext): 64 | return self.visitChildren(ctx) 65 | 66 | 67 | # Visit a parse tree produced by VisualFoxpro9Parser#lineEnd. 68 | def visitLineEnd(self, ctx:VisualFoxpro9Parser.LineEndContext): 69 | return self.visitChildren(ctx) 70 | 71 | 72 | # Visit a parse tree produced by VisualFoxpro9Parser#lines. 73 | def visitLines(self, ctx:VisualFoxpro9Parser.LinesContext): 74 | return self.visitChildren(ctx) 75 | 76 | 77 | # Visit a parse tree produced by VisualFoxpro9Parser#nongreedyLines. 78 | def visitNongreedyLines(self, ctx:VisualFoxpro9Parser.NongreedyLinesContext): 79 | return self.visitChildren(ctx) 80 | 81 | 82 | # Visit a parse tree produced by VisualFoxpro9Parser#classDefStart. 83 | def visitClassDefStart(self, ctx:VisualFoxpro9Parser.ClassDefStartContext): 84 | return self.visitChildren(ctx) 85 | 86 | 87 | # Visit a parse tree produced by VisualFoxpro9Parser#classDef. 88 | def visitClassDef(self, ctx:VisualFoxpro9Parser.ClassDefContext): 89 | return self.visitChildren(ctx) 90 | 91 | 92 | # Visit a parse tree produced by VisualFoxpro9Parser#classProperty. 93 | def visitClassProperty(self, ctx:VisualFoxpro9Parser.ClassPropertyContext): 94 | return self.visitChildren(ctx) 95 | 96 | 97 | # Visit a parse tree produced by VisualFoxpro9Parser#parameter. 98 | def visitParameter(self, ctx:VisualFoxpro9Parser.ParameterContext): 99 | return self.visitChildren(ctx) 100 | 101 | 102 | # Visit a parse tree produced by VisualFoxpro9Parser#parameters. 103 | def visitParameters(self, ctx:VisualFoxpro9Parser.ParametersContext): 104 | return self.visitChildren(ctx) 105 | 106 | 107 | # Visit a parse tree produced by VisualFoxpro9Parser#funcDefStart. 108 | def visitFuncDefStart(self, ctx:VisualFoxpro9Parser.FuncDefStartContext): 109 | return self.visitChildren(ctx) 110 | 111 | 112 | # Visit a parse tree produced by VisualFoxpro9Parser#funcDef. 113 | def visitFuncDef(self, ctx:VisualFoxpro9Parser.FuncDefContext): 114 | return self.visitChildren(ctx) 115 | 116 | 117 | # Visit a parse tree produced by VisualFoxpro9Parser#ifStart. 118 | def visitIfStart(self, ctx:VisualFoxpro9Parser.IfStartContext): 119 | return self.visitChildren(ctx) 120 | 121 | 122 | # Visit a parse tree produced by VisualFoxpro9Parser#ifStmt. 123 | def visitIfStmt(self, ctx:VisualFoxpro9Parser.IfStmtContext): 124 | return self.visitChildren(ctx) 125 | 126 | 127 | # Visit a parse tree produced by VisualFoxpro9Parser#forStart. 128 | def visitForStart(self, ctx:VisualFoxpro9Parser.ForStartContext): 129 | return self.visitChildren(ctx) 130 | 131 | 132 | # Visit a parse tree produced by VisualFoxpro9Parser#forEnd. 133 | def visitForEnd(self, ctx:VisualFoxpro9Parser.ForEndContext): 134 | return self.visitChildren(ctx) 135 | 136 | 137 | # Visit a parse tree produced by VisualFoxpro9Parser#forStmt. 138 | def visitForStmt(self, ctx:VisualFoxpro9Parser.ForStmtContext): 139 | return self.visitChildren(ctx) 140 | 141 | 142 | # Visit a parse tree produced by VisualFoxpro9Parser#singleCase. 143 | def visitSingleCase(self, ctx:VisualFoxpro9Parser.SingleCaseContext): 144 | return self.visitChildren(ctx) 145 | 146 | 147 | # Visit a parse tree produced by VisualFoxpro9Parser#otherwise. 148 | def visitOtherwise(self, ctx:VisualFoxpro9Parser.OtherwiseContext): 149 | return self.visitChildren(ctx) 150 | 151 | 152 | # Visit a parse tree produced by VisualFoxpro9Parser#caseStmt. 153 | def visitCaseStmt(self, ctx:VisualFoxpro9Parser.CaseStmtContext): 154 | return self.visitChildren(ctx) 155 | 156 | 157 | # Visit a parse tree produced by VisualFoxpro9Parser#whileStart. 158 | def visitWhileStart(self, ctx:VisualFoxpro9Parser.WhileStartContext): 159 | return self.visitChildren(ctx) 160 | 161 | 162 | # Visit a parse tree produced by VisualFoxpro9Parser#whileStmt. 163 | def visitWhileStmt(self, ctx:VisualFoxpro9Parser.WhileStmtContext): 164 | return self.visitChildren(ctx) 165 | 166 | 167 | # Visit a parse tree produced by VisualFoxpro9Parser#withStmt. 168 | def visitWithStmt(self, ctx:VisualFoxpro9Parser.WithStmtContext): 169 | return self.visitChildren(ctx) 170 | 171 | 172 | # Visit a parse tree produced by VisualFoxpro9Parser#scanStmt. 173 | def visitScanStmt(self, ctx:VisualFoxpro9Parser.ScanStmtContext): 174 | return self.visitChildren(ctx) 175 | 176 | 177 | # Visit a parse tree produced by VisualFoxpro9Parser#tryStmt. 178 | def visitTryStmt(self, ctx:VisualFoxpro9Parser.TryStmtContext): 179 | return self.visitChildren(ctx) 180 | 181 | 182 | # Visit a parse tree produced by VisualFoxpro9Parser#controlStmt. 183 | def visitControlStmt(self, ctx:VisualFoxpro9Parser.ControlStmtContext): 184 | return self.visitChildren(ctx) 185 | 186 | 187 | # Visit a parse tree produced by VisualFoxpro9Parser#addObject. 188 | def visitAddObject(self, ctx:VisualFoxpro9Parser.AddObjectContext): 189 | return self.visitChildren(ctx) 190 | 191 | 192 | # Visit a parse tree produced by VisualFoxpro9Parser#programControl. 193 | def visitProgramControl(self, ctx:VisualFoxpro9Parser.ProgramControlContext): 194 | return self.visitChildren(ctx) 195 | 196 | 197 | # Visit a parse tree produced by VisualFoxpro9Parser#atPos. 198 | def visitAtPos(self, ctx:VisualFoxpro9Parser.AtPosContext): 199 | return self.visitChildren(ctx) 200 | 201 | 202 | # Visit a parse tree produced by VisualFoxpro9Parser#funcDo. 203 | def visitFuncDo(self, ctx:VisualFoxpro9Parser.FuncDoContext): 204 | return self.visitChildren(ctx) 205 | 206 | 207 | # Visit a parse tree produced by VisualFoxpro9Parser#assign. 208 | def visitAssign(self, ctx:VisualFoxpro9Parser.AssignContext): 209 | return self.visitChildren(ctx) 210 | 211 | 212 | # Visit a parse tree produced by VisualFoxpro9Parser#declaration. 213 | def visitDeclaration(self, ctx:VisualFoxpro9Parser.DeclarationContext): 214 | return self.visitChildren(ctx) 215 | 216 | 217 | # Visit a parse tree produced by VisualFoxpro9Parser#printStmt. 218 | def visitPrintStmt(self, ctx:VisualFoxpro9Parser.PrintStmtContext): 219 | return self.visitChildren(ctx) 220 | 221 | 222 | # Visit a parse tree produced by VisualFoxpro9Parser#waitCmd. 223 | def visitWaitCmd(self, ctx:VisualFoxpro9Parser.WaitCmdContext): 224 | return self.visitChildren(ctx) 225 | 226 | 227 | # Visit a parse tree produced by VisualFoxpro9Parser#deleteFile. 228 | def visitDeleteFile(self, ctx:VisualFoxpro9Parser.DeleteFileContext): 229 | return self.visitChildren(ctx) 230 | 231 | 232 | # Visit a parse tree produced by VisualFoxpro9Parser#copyMoveFile. 233 | def visitCopyMoveFile(self, ctx:VisualFoxpro9Parser.CopyMoveFileContext): 234 | return self.visitChildren(ctx) 235 | 236 | 237 | # Visit a parse tree produced by VisualFoxpro9Parser#chMkRmDir. 238 | def visitChMkRmDir(self, ctx:VisualFoxpro9Parser.ChMkRmDirContext): 239 | return self.visitChildren(ctx) 240 | 241 | 242 | # Visit a parse tree produced by VisualFoxpro9Parser#returnStmt. 243 | def visitReturnStmt(self, ctx:VisualFoxpro9Parser.ReturnStmtContext): 244 | return self.visitChildren(ctx) 245 | 246 | 247 | # Visit a parse tree produced by VisualFoxpro9Parser#onStmt. 248 | def visitOnStmt(self, ctx:VisualFoxpro9Parser.OnStmtContext): 249 | return self.visitChildren(ctx) 250 | 251 | 252 | # Visit a parse tree produced by VisualFoxpro9Parser#release. 253 | def visitRelease(self, ctx:VisualFoxpro9Parser.ReleaseContext): 254 | return self.visitChildren(ctx) 255 | 256 | 257 | # Visit a parse tree produced by VisualFoxpro9Parser#setStmt. 258 | def visitSetStmt(self, ctx:VisualFoxpro9Parser.SetStmtContext): 259 | return self.visitChildren(ctx) 260 | 261 | 262 | # Visit a parse tree produced by VisualFoxpro9Parser#push. 263 | def visitPush(self, ctx:VisualFoxpro9Parser.PushContext): 264 | return self.visitChildren(ctx) 265 | 266 | 267 | # Visit a parse tree produced by VisualFoxpro9Parser#pop. 268 | def visitPop(self, ctx:VisualFoxpro9Parser.PopContext): 269 | return self.visitChildren(ctx) 270 | 271 | 272 | # Visit a parse tree produced by VisualFoxpro9Parser#keyboard. 273 | def visitKeyboard(self, ctx:VisualFoxpro9Parser.KeyboardContext): 274 | return self.visitChildren(ctx) 275 | 276 | 277 | # Visit a parse tree produced by VisualFoxpro9Parser#defineMenu. 278 | def visitDefineMenu(self, ctx:VisualFoxpro9Parser.DefineMenuContext): 279 | return self.visitChildren(ctx) 280 | 281 | 282 | # Visit a parse tree produced by VisualFoxpro9Parser#definePad. 283 | def visitDefinePad(self, ctx:VisualFoxpro9Parser.DefinePadContext): 284 | return self.visitChildren(ctx) 285 | 286 | 287 | # Visit a parse tree produced by VisualFoxpro9Parser#definePopup. 288 | def visitDefinePopup(self, ctx:VisualFoxpro9Parser.DefinePopupContext): 289 | return self.visitChildren(ctx) 290 | 291 | 292 | # Visit a parse tree produced by VisualFoxpro9Parser#defineBar. 293 | def visitDefineBar(self, ctx:VisualFoxpro9Parser.DefineBarContext): 294 | return self.visitChildren(ctx) 295 | 296 | 297 | # Visit a parse tree produced by VisualFoxpro9Parser#activateWindow. 298 | def visitActivateWindow(self, ctx:VisualFoxpro9Parser.ActivateWindowContext): 299 | return self.visitChildren(ctx) 300 | 301 | 302 | # Visit a parse tree produced by VisualFoxpro9Parser#activateScreen. 303 | def visitActivateScreen(self, ctx:VisualFoxpro9Parser.ActivateScreenContext): 304 | return self.visitChildren(ctx) 305 | 306 | 307 | # Visit a parse tree produced by VisualFoxpro9Parser#activateMenu. 308 | def visitActivateMenu(self, ctx:VisualFoxpro9Parser.ActivateMenuContext): 309 | return self.visitChildren(ctx) 310 | 311 | 312 | # Visit a parse tree produced by VisualFoxpro9Parser#activatePopup. 313 | def visitActivatePopup(self, ctx:VisualFoxpro9Parser.ActivatePopupContext): 314 | return self.visitChildren(ctx) 315 | 316 | 317 | # Visit a parse tree produced by VisualFoxpro9Parser#deactivate. 318 | def visitDeactivate(self, ctx:VisualFoxpro9Parser.DeactivateContext): 319 | return self.visitChildren(ctx) 320 | 321 | 322 | # Visit a parse tree produced by VisualFoxpro9Parser#modifyWindow. 323 | def visitModifyWindow(self, ctx:VisualFoxpro9Parser.ModifyWindowContext): 324 | return self.visitChildren(ctx) 325 | 326 | 327 | # Visit a parse tree produced by VisualFoxpro9Parser#modifyFile. 328 | def visitModifyFile(self, ctx:VisualFoxpro9Parser.ModifyFileContext): 329 | return self.visitChildren(ctx) 330 | 331 | 332 | # Visit a parse tree produced by VisualFoxpro9Parser#raiseError. 333 | def visitRaiseError(self, ctx:VisualFoxpro9Parser.RaiseErrorContext): 334 | return self.visitChildren(ctx) 335 | 336 | 337 | # Visit a parse tree produced by VisualFoxpro9Parser#throwError. 338 | def visitThrowError(self, ctx:VisualFoxpro9Parser.ThrowErrorContext): 339 | return self.visitChildren(ctx) 340 | 341 | 342 | # Visit a parse tree produced by VisualFoxpro9Parser#createTable. 343 | def visitCreateTable(self, ctx:VisualFoxpro9Parser.CreateTableContext): 344 | return self.visitChildren(ctx) 345 | 346 | 347 | # Visit a parse tree produced by VisualFoxpro9Parser#alterTable. 348 | def visitAlterTable(self, ctx:VisualFoxpro9Parser.AlterTableContext): 349 | return self.visitChildren(ctx) 350 | 351 | 352 | # Visit a parse tree produced by VisualFoxpro9Parser#select. 353 | def visitSelect(self, ctx:VisualFoxpro9Parser.SelectContext): 354 | return self.visitChildren(ctx) 355 | 356 | 357 | # Visit a parse tree produced by VisualFoxpro9Parser#use. 358 | def visitUse(self, ctx:VisualFoxpro9Parser.UseContext): 359 | return self.visitChildren(ctx) 360 | 361 | 362 | # Visit a parse tree produced by VisualFoxpro9Parser#locate. 363 | def visitLocate(self, ctx:VisualFoxpro9Parser.LocateContext): 364 | return self.visitChildren(ctx) 365 | 366 | 367 | # Visit a parse tree produced by VisualFoxpro9Parser#continueLocate. 368 | def visitContinueLocate(self, ctx:VisualFoxpro9Parser.ContinueLocateContext): 369 | return self.visitChildren(ctx) 370 | 371 | 372 | # Visit a parse tree produced by VisualFoxpro9Parser#retry. 373 | def visitRetry(self, ctx:VisualFoxpro9Parser.RetryContext): 374 | return self.visitChildren(ctx) 375 | 376 | 377 | # Visit a parse tree produced by VisualFoxpro9Parser#replace. 378 | def visitReplace(self, ctx:VisualFoxpro9Parser.ReplaceContext): 379 | return self.visitChildren(ctx) 380 | 381 | 382 | # Visit a parse tree produced by VisualFoxpro9Parser#indexOn. 383 | def visitIndexOn(self, ctx:VisualFoxpro9Parser.IndexOnContext): 384 | return self.visitChildren(ctx) 385 | 386 | 387 | # Visit a parse tree produced by VisualFoxpro9Parser#count. 388 | def visitCount(self, ctx:VisualFoxpro9Parser.CountContext): 389 | return self.visitChildren(ctx) 390 | 391 | 392 | # Visit a parse tree produced by VisualFoxpro9Parser#sum. 393 | def visitSum(self, ctx:VisualFoxpro9Parser.SumContext): 394 | return self.visitChildren(ctx) 395 | 396 | 397 | # Visit a parse tree produced by VisualFoxpro9Parser#sortCmd. 398 | def visitSortCmd(self, ctx:VisualFoxpro9Parser.SortCmdContext): 399 | return self.visitChildren(ctx) 400 | 401 | 402 | # Visit a parse tree produced by VisualFoxpro9Parser#deleteRecord. 403 | def visitDeleteRecord(self, ctx:VisualFoxpro9Parser.DeleteRecordContext): 404 | return self.visitChildren(ctx) 405 | 406 | 407 | # Visit a parse tree produced by VisualFoxpro9Parser#appendFrom. 408 | def visitAppendFrom(self, ctx:VisualFoxpro9Parser.AppendFromContext): 409 | return self.visitChildren(ctx) 410 | 411 | 412 | # Visit a parse tree produced by VisualFoxpro9Parser#append. 413 | def visitAppend(self, ctx:VisualFoxpro9Parser.AppendContext): 414 | return self.visitChildren(ctx) 415 | 416 | 417 | # Visit a parse tree produced by VisualFoxpro9Parser#insert. 418 | def visitInsert(self, ctx:VisualFoxpro9Parser.InsertContext): 419 | return self.visitChildren(ctx) 420 | 421 | 422 | # Visit a parse tree produced by VisualFoxpro9Parser#skipRecord. 423 | def visitSkipRecord(self, ctx:VisualFoxpro9Parser.SkipRecordContext): 424 | return self.visitChildren(ctx) 425 | 426 | 427 | # Visit a parse tree produced by VisualFoxpro9Parser#pack. 428 | def visitPack(self, ctx:VisualFoxpro9Parser.PackContext): 429 | return self.visitChildren(ctx) 430 | 431 | 432 | # Visit a parse tree produced by VisualFoxpro9Parser#reindex. 433 | def visitReindex(self, ctx:VisualFoxpro9Parser.ReindexContext): 434 | return self.visitChildren(ctx) 435 | 436 | 437 | # Visit a parse tree produced by VisualFoxpro9Parser#seekRecord. 438 | def visitSeekRecord(self, ctx:VisualFoxpro9Parser.SeekRecordContext): 439 | return self.visitChildren(ctx) 440 | 441 | 442 | # Visit a parse tree produced by VisualFoxpro9Parser#updateCmd. 443 | def visitUpdateCmd(self, ctx:VisualFoxpro9Parser.UpdateCmdContext): 444 | return self.visitChildren(ctx) 445 | 446 | 447 | # Visit a parse tree produced by VisualFoxpro9Parser#goRecord. 448 | def visitGoRecord(self, ctx:VisualFoxpro9Parser.GoRecordContext): 449 | return self.visitChildren(ctx) 450 | 451 | 452 | # Visit a parse tree produced by VisualFoxpro9Parser#copyToArray. 453 | def visitCopyToArray(self, ctx:VisualFoxpro9Parser.CopyToArrayContext): 454 | return self.visitChildren(ctx) 455 | 456 | 457 | # Visit a parse tree produced by VisualFoxpro9Parser#copyTo. 458 | def visitCopyTo(self, ctx:VisualFoxpro9Parser.CopyToContext): 459 | return self.visitChildren(ctx) 460 | 461 | 462 | # Visit a parse tree produced by VisualFoxpro9Parser#zapTable. 463 | def visitZapTable(self, ctx:VisualFoxpro9Parser.ZapTableContext): 464 | return self.visitChildren(ctx) 465 | 466 | 467 | # Visit a parse tree produced by VisualFoxpro9Parser#browse. 468 | def visitBrowse(self, ctx:VisualFoxpro9Parser.BrowseContext): 469 | return self.visitChildren(ctx) 470 | 471 | 472 | # Visit a parse tree produced by VisualFoxpro9Parser#scatterExpr. 473 | def visitScatterExpr(self, ctx:VisualFoxpro9Parser.ScatterExprContext): 474 | return self.visitChildren(ctx) 475 | 476 | 477 | # Visit a parse tree produced by VisualFoxpro9Parser#gatherExpr. 478 | def visitGatherExpr(self, ctx:VisualFoxpro9Parser.GatherExprContext): 479 | return self.visitChildren(ctx) 480 | 481 | 482 | # Visit a parse tree produced by VisualFoxpro9Parser#closeStmt. 483 | def visitCloseStmt(self, ctx:VisualFoxpro9Parser.CloseStmtContext): 484 | return self.visitChildren(ctx) 485 | 486 | 487 | # Visit a parse tree produced by VisualFoxpro9Parser#readEvent. 488 | def visitReadEvent(self, ctx:VisualFoxpro9Parser.ReadEventContext): 489 | return self.visitChildren(ctx) 490 | 491 | 492 | # Visit a parse tree produced by VisualFoxpro9Parser#unlockCmd. 493 | def visitUnlockCmd(self, ctx:VisualFoxpro9Parser.UnlockCmdContext): 494 | return self.visitChildren(ctx) 495 | 496 | 497 | # Visit a parse tree produced by VisualFoxpro9Parser#clearStmt. 498 | def visitClearStmt(self, ctx:VisualFoxpro9Parser.ClearStmtContext): 499 | return self.visitChildren(ctx) 500 | 501 | 502 | # Visit a parse tree produced by VisualFoxpro9Parser#report. 503 | def visitReport(self, ctx:VisualFoxpro9Parser.ReportContext): 504 | return self.visitChildren(ctx) 505 | 506 | 507 | # Visit a parse tree produced by VisualFoxpro9Parser#dllDeclare. 508 | def visitDllDeclare(self, ctx:VisualFoxpro9Parser.DllDeclareContext): 509 | return self.visitChildren(ctx) 510 | 511 | 512 | # Visit a parse tree produced by VisualFoxpro9Parser#shellRun. 513 | def visitShellRun(self, ctx:VisualFoxpro9Parser.ShellRunContext): 514 | return self.visitChildren(ctx) 515 | 516 | 517 | # Visit a parse tree produced by VisualFoxpro9Parser#assert. 518 | def visitAssert(self, ctx:VisualFoxpro9Parser.AssertContext): 519 | return self.visitChildren(ctx) 520 | 521 | 522 | # Visit a parse tree produced by VisualFoxpro9Parser#compileCmd. 523 | def visitCompileCmd(self, ctx:VisualFoxpro9Parser.CompileCmdContext): 524 | return self.visitChildren(ctx) 525 | 526 | 527 | # Visit a parse tree produced by VisualFoxpro9Parser#listStmt. 528 | def visitListStmt(self, ctx:VisualFoxpro9Parser.ListStmtContext): 529 | return self.visitChildren(ctx) 530 | 531 | 532 | # Visit a parse tree produced by VisualFoxpro9Parser#saveToCmd. 533 | def visitSaveToCmd(self, ctx:VisualFoxpro9Parser.SaveToCmdContext): 534 | return self.visitChildren(ctx) 535 | 536 | 537 | # Visit a parse tree produced by VisualFoxpro9Parser#restoreCmd. 538 | def visitRestoreCmd(self, ctx:VisualFoxpro9Parser.RestoreCmdContext): 539 | return self.visitChildren(ctx) 540 | 541 | 542 | # Visit a parse tree produced by VisualFoxpro9Parser#zoomCmd. 543 | def visitZoomCmd(self, ctx:VisualFoxpro9Parser.ZoomCmdContext): 544 | return self.visitChildren(ctx) 545 | 546 | 547 | # Visit a parse tree produced by VisualFoxpro9Parser#textBlock. 548 | def visitTextBlock(self, ctx:VisualFoxpro9Parser.TextBlockContext): 549 | return self.visitChildren(ctx) 550 | 551 | 552 | # Visit a parse tree produced by VisualFoxpro9Parser#showCmd. 553 | def visitShowCmd(self, ctx:VisualFoxpro9Parser.ShowCmdContext): 554 | return self.visitChildren(ctx) 555 | 556 | 557 | # Visit a parse tree produced by VisualFoxpro9Parser#hideCmd. 558 | def visitHideCmd(self, ctx:VisualFoxpro9Parser.HideCmdContext): 559 | return self.visitChildren(ctx) 560 | 561 | 562 | # Visit a parse tree produced by VisualFoxpro9Parser#exprCmd. 563 | def visitExprCmd(self, ctx:VisualFoxpro9Parser.ExprCmdContext): 564 | return self.visitChildren(ctx) 565 | 566 | 567 | # Visit a parse tree produced by VisualFoxpro9Parser#complexIdCmd. 568 | def visitComplexIdCmd(self, ctx:VisualFoxpro9Parser.ComplexIdCmdContext): 569 | return self.visitChildren(ctx) 570 | 571 | 572 | # Visit a parse tree produced by VisualFoxpro9Parser#queryCondition. 573 | def visitQueryCondition(self, ctx:VisualFoxpro9Parser.QueryConditionContext): 574 | return self.visitChildren(ctx) 575 | 576 | 577 | # Visit a parse tree produced by VisualFoxpro9Parser#textChunk. 578 | def visitTextChunk(self, ctx:VisualFoxpro9Parser.TextChunkContext): 579 | return self.visitChildren(ctx) 580 | 581 | 582 | # Visit a parse tree produced by VisualFoxpro9Parser#dllArgs. 583 | def visitDllArgs(self, ctx:VisualFoxpro9Parser.DllArgsContext): 584 | return self.visitChildren(ctx) 585 | 586 | 587 | # Visit a parse tree produced by VisualFoxpro9Parser#dllArg. 588 | def visitDllArg(self, ctx:VisualFoxpro9Parser.DllArgContext): 589 | return self.visitChildren(ctx) 590 | 591 | 592 | # Visit a parse tree produced by VisualFoxpro9Parser#tableField. 593 | def visitTableField(self, ctx:VisualFoxpro9Parser.TableFieldContext): 594 | return self.visitChildren(ctx) 595 | 596 | 597 | # Visit a parse tree produced by VisualFoxpro9Parser#setCmd. 598 | def visitSetCmd(self, ctx:VisualFoxpro9Parser.SetCmdContext): 599 | return self.visitChildren(ctx) 600 | 601 | 602 | # Visit a parse tree produced by VisualFoxpro9Parser#declarationItem. 603 | def visitDeclarationItem(self, ctx:VisualFoxpro9Parser.DeclarationItemContext): 604 | return self.visitChildren(ctx) 605 | 606 | 607 | # Visit a parse tree produced by VisualFoxpro9Parser#asType. 608 | def visitAsType(self, ctx:VisualFoxpro9Parser.AsTypeContext): 609 | return self.visitChildren(ctx) 610 | 611 | 612 | # Visit a parse tree produced by VisualFoxpro9Parser#asTypeOf. 613 | def visitAsTypeOf(self, ctx:VisualFoxpro9Parser.AsTypeOfContext): 614 | return self.visitChildren(ctx) 615 | 616 | 617 | # Visit a parse tree produced by VisualFoxpro9Parser#argsItem. 618 | def visitArgsItem(self, ctx:VisualFoxpro9Parser.ArgsItemContext): 619 | return self.visitChildren(ctx) 620 | 621 | 622 | # Visit a parse tree produced by VisualFoxpro9Parser#args. 623 | def visitArgs(self, ctx:VisualFoxpro9Parser.ArgsContext): 624 | return self.visitChildren(ctx) 625 | 626 | 627 | # Visit a parse tree produced by VisualFoxpro9Parser#specialArgs. 628 | def visitSpecialArgs(self, ctx:VisualFoxpro9Parser.SpecialArgsContext): 629 | return self.visitChildren(ctx) 630 | 631 | 632 | # Visit a parse tree produced by VisualFoxpro9Parser#reference. 633 | def visitReference(self, ctx:VisualFoxpro9Parser.ReferenceContext): 634 | return self.visitChildren(ctx) 635 | 636 | 637 | # Visit a parse tree produced by VisualFoxpro9Parser#comparison. 638 | def visitComparison(self, ctx:VisualFoxpro9Parser.ComparisonContext): 639 | return self.visitChildren(ctx) 640 | 641 | 642 | # Visit a parse tree produced by VisualFoxpro9Parser#castExpr. 643 | def visitCastExpr(self, ctx:VisualFoxpro9Parser.CastExprContext): 644 | return self.visitChildren(ctx) 645 | 646 | 647 | # Visit a parse tree produced by VisualFoxpro9Parser#atomExpr. 648 | def visitAtomExpr(self, ctx:VisualFoxpro9Parser.AtomExprContext): 649 | return self.visitChildren(ctx) 650 | 651 | 652 | # Visit a parse tree produced by VisualFoxpro9Parser#subExpr. 653 | def visitSubExpr(self, ctx:VisualFoxpro9Parser.SubExprContext): 654 | return self.visitChildren(ctx) 655 | 656 | 657 | # Visit a parse tree produced by VisualFoxpro9Parser#booleanNegation. 658 | def visitBooleanNegation(self, ctx:VisualFoxpro9Parser.BooleanNegationContext): 659 | return self.visitChildren(ctx) 660 | 661 | 662 | # Visit a parse tree produced by VisualFoxpro9Parser#unaryNegation. 663 | def visitUnaryNegation(self, ctx:VisualFoxpro9Parser.UnaryNegationContext): 664 | return self.visitChildren(ctx) 665 | 666 | 667 | # Visit a parse tree produced by VisualFoxpro9Parser#booleanOr. 668 | def visitBooleanOr(self, ctx:VisualFoxpro9Parser.BooleanOrContext): 669 | return self.visitChildren(ctx) 670 | 671 | 672 | # Visit a parse tree produced by VisualFoxpro9Parser#power. 673 | def visitPower(self, ctx:VisualFoxpro9Parser.PowerContext): 674 | return self.visitChildren(ctx) 675 | 676 | 677 | # Visit a parse tree produced by VisualFoxpro9Parser#multiplication. 678 | def visitMultiplication(self, ctx:VisualFoxpro9Parser.MultiplicationContext): 679 | return self.visitChildren(ctx) 680 | 681 | 682 | # Visit a parse tree produced by VisualFoxpro9Parser#modulo. 683 | def visitModulo(self, ctx:VisualFoxpro9Parser.ModuloContext): 684 | return self.visitChildren(ctx) 685 | 686 | 687 | # Visit a parse tree produced by VisualFoxpro9Parser#constantExpr. 688 | def visitConstantExpr(self, ctx:VisualFoxpro9Parser.ConstantExprContext): 689 | return self.visitChildren(ctx) 690 | 691 | 692 | # Visit a parse tree produced by VisualFoxpro9Parser#addition. 693 | def visitAddition(self, ctx:VisualFoxpro9Parser.AdditionContext): 694 | return self.visitChildren(ctx) 695 | 696 | 697 | # Visit a parse tree produced by VisualFoxpro9Parser#booleanAnd. 698 | def visitBooleanAnd(self, ctx:VisualFoxpro9Parser.BooleanAndContext): 699 | return self.visitChildren(ctx) 700 | 701 | 702 | # Visit a parse tree produced by VisualFoxpro9Parser#andOp. 703 | def visitAndOp(self, ctx:VisualFoxpro9Parser.AndOpContext): 704 | return self.visitChildren(ctx) 705 | 706 | 707 | # Visit a parse tree produced by VisualFoxpro9Parser#orOp. 708 | def visitOrOp(self, ctx:VisualFoxpro9Parser.OrOpContext): 709 | return self.visitChildren(ctx) 710 | 711 | 712 | # Visit a parse tree produced by VisualFoxpro9Parser#complexId. 713 | def visitComplexId(self, ctx:VisualFoxpro9Parser.ComplexIdContext): 714 | return self.visitChildren(ctx) 715 | 716 | 717 | # Visit a parse tree produced by VisualFoxpro9Parser#atom. 718 | def visitAtom(self, ctx:VisualFoxpro9Parser.AtomContext): 719 | return self.visitChildren(ctx) 720 | 721 | 722 | # Visit a parse tree produced by VisualFoxpro9Parser#funcCallTrailer. 723 | def visitFuncCallTrailer(self, ctx:VisualFoxpro9Parser.FuncCallTrailerContext): 724 | return self.visitChildren(ctx) 725 | 726 | 727 | # Visit a parse tree produced by VisualFoxpro9Parser#identTrailer. 728 | def visitIdentTrailer(self, ctx:VisualFoxpro9Parser.IdentTrailerContext): 729 | return self.visitChildren(ctx) 730 | 731 | 732 | # Visit a parse tree produced by VisualFoxpro9Parser#pathname. 733 | def visitPathname(self, ctx:VisualFoxpro9Parser.PathnameContext): 734 | return self.visitChildren(ctx) 735 | 736 | 737 | # Visit a parse tree produced by VisualFoxpro9Parser#pathElement. 738 | def visitPathElement(self, ctx:VisualFoxpro9Parser.PathElementContext): 739 | return self.visitChildren(ctx) 740 | 741 | 742 | # Visit a parse tree produced by VisualFoxpro9Parser#specialExpr. 743 | def visitSpecialExpr(self, ctx:VisualFoxpro9Parser.SpecialExprContext): 744 | return self.visitChildren(ctx) 745 | 746 | 747 | # Visit a parse tree produced by VisualFoxpro9Parser#numberOrCurrency. 748 | def visitNumberOrCurrency(self, ctx:VisualFoxpro9Parser.NumberOrCurrencyContext): 749 | return self.visitChildren(ctx) 750 | 751 | 752 | # Visit a parse tree produced by VisualFoxpro9Parser#boolOrNull. 753 | def visitBoolOrNull(self, ctx:VisualFoxpro9Parser.BoolOrNullContext): 754 | return self.visitChildren(ctx) 755 | 756 | 757 | # Visit a parse tree produced by VisualFoxpro9Parser#date. 758 | def visitDate(self, ctx:VisualFoxpro9Parser.DateContext): 759 | return self.visitChildren(ctx) 760 | 761 | 762 | # Visit a parse tree produced by VisualFoxpro9Parser#string. 763 | def visitString(self, ctx:VisualFoxpro9Parser.StringContext): 764 | return self.visitChildren(ctx) 765 | 766 | 767 | # Visit a parse tree produced by VisualFoxpro9Parser#blob. 768 | def visitBlob(self, ctx:VisualFoxpro9Parser.BlobContext): 769 | return self.visitChildren(ctx) 770 | 771 | 772 | # Visit a parse tree produced by VisualFoxpro9Parser#idAttr2. 773 | def visitIdAttr2(self, ctx:VisualFoxpro9Parser.IdAttr2Context): 774 | return self.visitChildren(ctx) 775 | 776 | 777 | # Visit a parse tree produced by VisualFoxpro9Parser#idAttr. 778 | def visitIdAttr(self, ctx:VisualFoxpro9Parser.IdAttrContext): 779 | return self.visitChildren(ctx) 780 | 781 | 782 | # Visit a parse tree produced by VisualFoxpro9Parser#twoExpr. 783 | def visitTwoExpr(self, ctx:VisualFoxpro9Parser.TwoExprContext): 784 | return self.visitChildren(ctx) 785 | 786 | 787 | # Visit a parse tree produced by VisualFoxpro9Parser#arrayIndex. 788 | def visitArrayIndex(self, ctx:VisualFoxpro9Parser.ArrayIndexContext): 789 | return self.visitChildren(ctx) 790 | 791 | 792 | # Visit a parse tree produced by VisualFoxpro9Parser#datatype. 793 | def visitDatatype(self, ctx:VisualFoxpro9Parser.DatatypeContext): 794 | return self.visitChildren(ctx) 795 | 796 | 797 | # Visit a parse tree produced by VisualFoxpro9Parser#scopeClause. 798 | def visitScopeClause(self, ctx:VisualFoxpro9Parser.ScopeClauseContext): 799 | return self.visitChildren(ctx) 800 | 801 | 802 | # Visit a parse tree produced by VisualFoxpro9Parser#identifier. 803 | def visitIdentifier(self, ctx:VisualFoxpro9Parser.IdentifierContext): 804 | return self.visitChildren(ctx) 805 | 806 | 807 | 808 | del VisualFoxpro9Parser -------------------------------------------------------------------------------- /testbed/test_conversion.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | 3 | import difflib 4 | 5 | import vfp2py 6 | 7 | 8 | def Test0(): 9 | input_str = ''' 10 | *comment; 11 | continued on another line; 12 | and another 13 | LOCAL STRING_VAL, FLOAT_VAL, MONEY_VAL, INT_VAL, BLOB_VAL, BOOL_VAL, NULL_VAL, NULLDATE_VAL, DATE_VAL, DATETIME_VAL, OBJ_VAL 14 | LOCAL Y, Z, W 15 | STRING_VAL = \'str\' 16 | ü1 = "Hellü" 17 | float_val = 3.0 18 | float_val = .5e-5 19 | float_val = 8e+18 20 | float_val = 1.3e12 21 | float_val = .1e 22 | money_val = $3.512345 23 | int_val = 3 24 | int_val = 0x3 25 | int_val = 0X3F 26 | int_val = 0x 27 | blob_val = 0h123456789abcde 28 | blob_val = 0h0123456789abcde 29 | blob_val = 0h 30 | BOOL_VAL = .F. 31 | NULL_VAL = NULL 32 | NULLDATE_VAL = { / / } 33 | NULLDATE_VAL = {:} 34 | DATE_VAL = {^2017-5-6} 35 | DATETIME_VAL = {^2017-5-6 5P} 36 | ?(float_val + INT_VAL + INT_VAL + - - FLOAT_VAL + int_VAL - FLOAT_val) / ++3 / --4 * -5 - INT_VAL * 3 37 | ?CHR(3) 38 | ?CHR(INT_VAL) 39 | ?SPACE(3) 40 | ?space(20) 41 | ?SPACE(INT_VAL) 42 | ?DAY(DATE_VAL) 43 | ?DOW(DATE_VAL) 44 | ?\'chr(65)\' + space(1) + chr(61) + \' \' + chr(65) + \', just letting you know.\' + chr(13) + chr(10) 45 | ?TRIM(\'test \') 46 | ?2 ** 3 ** 4 47 | ?2 ** (3 ** 4) 48 | ?(((2))) 49 | ?10 >= 5 50 | ?10 => 5 51 | ?10 <= 5 52 | ?10 =< 5 53 | ?.t. or .f. AND .f. or .f. 54 | ?x .or. y AND w .or. z 55 | ?CAST(string_val as blob) 56 | ?cast(float_val as currency) 57 | ?substr(string_val, int_val, 13) 58 | ?subs(string_val, 1, 13) 59 | ?at(\'.\', string_val) 60 | ?repli(string_val, FLOAT_VAL) 61 | OBJ_VAL = CREATEOBJECT(\'TEST\') 62 | OBJ_VAL = CREATE(\'FORM\') 63 | RELEASE STRING_VAL, INT_VAL, BOOL_VAL, NULL_VAL 64 | LOCAL ARRAY ITEMS[3, 5] 65 | LOCAL ITEM 66 | FOR EACH ITEM IN ITEMS 67 | IF NOT ITEM 68 | LOOP 69 | *!* IF 70 | *!* TEST() 71 | *!* ENDIF 72 | ENDIF 73 | EXIT 74 | ENDFOR 75 | RELEASE ITEMS, item 76 | FOR X = 4 TO 7 77 | ENDFOR 78 | FOR X = 1 TO 7 STEP 2 79 | ENDFOR 80 | DO CASE 81 | *line comment1 82 | * line comment2 83 | CASE X == 1 84 | CASE X == 2 85 | CASE (X == 2) 86 | OTHERWISE 87 | ?Test 88 | ENDCASE 89 | TEXT TO SOMESTRING NOSHOW 90 | 1234567890 91 | 0123456789 92 | ABCDEFGHIJ 93 | KLMNOPQRST 94 | UVWXYZ 95 | ENDTEXT 96 | QUIT 97 | CANCEL 98 | RESUME 99 | COMPILE test.prg 100 | READ 101 | READ EVENTS 102 | DOEVENTS 103 | DOEVENTS FORCE 104 | UNLOCK ALL 105 | LIST NEXT 5 106 | RESTORE FROM test ADDITIVE 107 | SHOW GETS 108 | HIDE WINDOW test 109 | SORT TO sortedTable ON (sortField) 110 | COPY TO ARRAY FOR X = 3 111 | SAVE TO test ALL LIKE something 112 | ZOOM WINDOW SCREEN MAX 113 | SOMEFUNC(,A,) 114 | MODIFY WINDOW SCREEN FONT "FONT", 12 STYLE "B" TITLE "TITLE" NOFLOAT NOCLOSE NOZOOM 115 | MODIFY COMMAND c:\\test.prg 116 | DEFINE MENU test BAR AT LINE 2 IN SCREEN 117 | DEFINE PAD test OF thing PROMPT "text" MESSAGE "menu" KEY ALT+F, "ALT+F" COLOR SCHEME 3 118 | DEFINE POPUP test 119 | DEFINE BAR 1 OF test PROMPT \'text\' 120 | ON PAD test OF thing ACTIVATE POPUP test 121 | ON BAR 1 of test ACTIVATE POPUP test 122 | ACTIVATE WINDOW window 123 | ACTIVATE SCREEN 124 | ACTIVATE MENU test NOWAIT 125 | ACTIVATE POPUP test 126 | '''.strip() 127 | output_str = ''' 128 | # comment 129 | # continued on another line 130 | # and another 131 | M.add_local(\'string_val\', \'float_val\', \'money_val\', \'int_val\', \'blob_val\', \'bool_val\', \'null_val\', \'nulldate_val\', \'date_val\', \'datetime_val\', \'obj_val\') 132 | M.add_local(\'y\', \'z\', \'w\') 133 | S.string_val = \'str\' 134 | S.ü1 = \'Hellü\' 135 | S.float_val = 3.0 136 | S.float_val = .5e-5 137 | S.float_val = 8e+18 138 | S.float_val = 1.3e12 139 | S.float_val = .1e0 140 | S.money_val = 3.5123 141 | S.int_val = 3 142 | S.int_val = 0x3 143 | S.int_val = 0x3f 144 | S.int_val = 0x0 145 | S.blob_val = bytearray(b\'\\x124Vx\\x9a\\xbc\\xde\') 146 | S.blob_val = bytearray(b\'\\x00\\x124Vx\\x9a\\xbc\\xde\') 147 | S.blob_val = bytearray(b\'\') 148 | S.bool_val = False 149 | S.null_val = None 150 | S.nulldate_val = None 151 | S.nulldate_val = None 152 | S.date_val = dt.date(2017, 5, 6) 153 | S.datetime_val = dt.datetime(2017, 5, 6, 17) 154 | print((S.float_val + S.int_val + S.int_val + S.float_val + S.int_val - S.float_val) / 3 / 4 * -5 - S.int_val * 3) 155 | print(\'\\x03\') 156 | print(chr(int(S.int_val))) 157 | print(\' \') 158 | print(20 * \' \') 159 | print(int(S.int_val) * \' \') 160 | print(S.date_val.day) 161 | print(vfpfunc.dow_fix(S.date_val.weekday())) 162 | print(\'chr(65) = A, just letting you know.\\r\\n\') 163 | print(\'test \'.rstrip()) 164 | print((2 ** 3) ** 4) 165 | print(2 ** 3 ** 4) 166 | print(2) 167 | print(10 >= 5) 168 | print(10 >= 5) 169 | print(10 <= 5) 170 | print(10 <= 5) 171 | print(True or False and False or False) 172 | print(S.x or S.y and S.w or S.z) 173 | print(bytearray(S.string_val)) 174 | print(float(S.float_val)) 175 | print(S.string_val[int(S.int_val) - 1:13 + int(S.int_val) - 1]) 176 | print(S.string_val[:13]) 177 | print(S.string_val.find(\'.\') + 1) 178 | print(S.string_val * int(S.float_val)) 179 | S.obj_val = vfpfunc.create_object(\'Test\') 180 | S.obj_val = vfpfunc.Form() 181 | del M.string_val, M.int_val, M.bool_val, M.null_val 182 | M.add_local(items=Array(3, 5)) 183 | M.add_local(\'item\') 184 | for S.item in S.items: 185 | if not S.item: 186 | continue 187 | # IF 188 | # TEST() 189 | # ENDIF 190 | break 191 | del M.items, M.item 192 | for S.x in range(4, 8): 193 | pass 194 | for S.x in range(1, 8, 2): 195 | pass 196 | # line comment1 197 | # line comment2 198 | if S.x == 1: 199 | pass 200 | elif S.x == 2: 201 | pass 202 | elif S.x == 2: 203 | pass 204 | else: 205 | print(S.test) 206 | S.somestring = vfpfunc.text([\' 1234567890\', 207 | \' 0123456789\', 208 | \' ABCDEFGHIJ\', 209 | \' KLMNOPQRST\', 210 | \' UVWXYZ\'], show=False) 211 | vfpfunc.quit() 212 | # FIX ME: CANCEL 213 | # FIX ME: RESUME 214 | # FIX ME: COMPILE test.prg 215 | # FIX ME: READ 216 | vfpfunc.read_events() 217 | # FIX ME: DOEVENTS 218 | # FIX ME: DOEVENTS FORCE 219 | # FIX ME: UNLOCK ALL 220 | # FIX ME: LIST NEXT 5 221 | # FIX ME: RESTORE FROM test ADDITIVE 222 | # FIX ME: SHOW GETS 223 | # FIX ME: HIDE WINDOW test 224 | # FIX ME: SORT TO sortedTable ON (sortField) 225 | # FIX ME: COPY TO ARRAY FOR X = 3 226 | # FIX ME: SAVE TO test ALL LIKE something 227 | # FIX ME: ZOOM WINDOW SCREEN MAX 228 | F.somefunc(False, S.a, False) 229 | # FIX ME: MODIFY WINDOW SCREEN FONT "FONT", 12 STYLE "B" TITLE "TITLE" NOFLOAT NOCLOSE NOZOOM 230 | # FIX ME: MODIFY COMMAND c:\\test.prg 231 | vfpfunc.define_menu(\'test\', window=vfpfunc.SCREEN, bar=True, line=2) 232 | vfpfunc.define_pad(\'test\', \'thing\', \'text\', color_scheme=3, message=\'menu\', key=(\'alt+f\', \'ALT+F\')) 233 | vfpfunc.define_popup(\'test\') 234 | vfpfunc.define_bar(1, \'test\', \'text\') 235 | vfpfunc.on_pad_bar(\'pad\', \'test\', \'thing\', (\'popup\', \'test\')) 236 | vfpfunc.on_pad_bar(\'bar\', 1, \'test\', (\'popup\', \'test\')) 237 | # FIX ME: ACTIVATE WINDOW window 238 | # FIX ME: ACTIVATE SCREEN 239 | vfpfunc.activate_menu(\'test\', nowait=True) 240 | # FIX ME: ACTIVATE POPUP test 241 | '''.strip() 242 | test_output_str = vfp2py.vfp2py.prg2py(input_str, 'cp1252', parser_start='lines', prepend_data='').strip() 243 | try: 244 | assert test_output_str == output_str 245 | except AssertionError: 246 | diff = difflib.unified_diff((test_output_str + '\n').splitlines(1), (output_str + '\n').splitlines(1)) 247 | print(''.join(diff)) 248 | raise 249 | 250 | 251 | def Test1(): 252 | input_str = ''' 253 | *comment with spaces 254 | #DEFINE cantbewrong 255 | #DEFINE SPACE CHR 256 | #DEFINE THREE 3 &&the number 3 257 | #IFDEF cantbewrong 258 | #IF FILE ( \'test.h\' ) 259 | *************** 260 | *** comment *** 261 | *************** 262 | # include test.h 263 | # include \'test.h\' 264 | # include \'test\' + \'.h\' 265 | STORE 5 to x && set x to 5 266 | #ELSE 267 | #ENDIF 268 | x = \'test\' + CHR(13) + CHR(10) 269 | x = space(5) 270 | x = \'\' + CHR(13) + CHR(10) 271 | #ELSE 272 | _SCREEN.LOGO.TOP = (_SCREEN.HEIGHT-_SCREEN.LOGO.HEIGHT)/2-3 273 | 274 | WAIT WIND space(3) + \'please wait\' + CHR(32) NOWAIT TIMEOUT 1.3 275 | #ENDIF 276 | #IF NOT FILE(\'test.h\') 277 | ?"This shouldn\'t be here" 278 | #ENDIF 279 | DO TEST.PRG 280 | #IF THREE > 1 281 | ?STR(THREE) 282 | ??STR(THREE) 283 | DEBUGOUT STR(THREE) 284 | @ 10, 10 SAY THREE 285 | #ENDIF 286 | RETURN X 287 | '''.strip() 288 | output_str = ''' 289 | from __future__ import division, print_function 290 | 291 | import sys 292 | import test 293 | 294 | from vfp2py import vfpfunc 295 | from vfp2py.vfpfunc import DB, Array, C, F, M, S, lparameters, parameters, vfpclass 296 | 297 | 298 | @lparameters() 299 | def MAIN(): 300 | # comment with spaces 301 | ############### 302 | ### comment ### 303 | ############### 304 | S.x = \'\\n\' 305 | S.x = \'\\n\' 306 | S.x = \'\\n\' 307 | # set x to 5 308 | S.x = 5 309 | S.x = \'test\\r\\n\' 310 | S.x = \'\\x05\' 311 | S.x = \'\\r\\n\' 312 | test.MAIN() 313 | print(vfpfunc.str(3)) 314 | print(vfpfunc.str(3), end=\'\') 315 | print(vfpfunc.str(3), file=sys.stderr) 316 | print(3) # @ 10, 10 SAY 3 317 | return S.x 318 | '''.strip() 319 | test_output_str = vfp2py.vfp2py.prg2py(input_str, 'cp1252').strip() 320 | try: 321 | assert test_output_str == output_str 322 | except AssertionError: 323 | diff = difflib.unified_diff((test_output_str + '\n').splitlines(1), (output_str + '\n').splitlines(1)) 324 | print(''.join(diff)) 325 | raise 326 | 327 | 328 | def Test2(): 329 | input_str = ''' 330 | DEFINE CLASS SUBOBJ AS CUSTOM 331 | X = 3 332 | 333 | FUNCTION INIT(X) 334 | DODEFAULT() 335 | THIS.X = X 336 | THIS.ARGTEST(THIS.X, 2) 337 | ENDFUNC 338 | 339 | PROCEDURE ARGTEST(X, Y) 340 | ENDPROC 341 | 342 | *comment 343 | ENDDEFINE 344 | 345 | *comment about subobj2 346 | DEFINE CLASS SUBOBJ2 AS SUBOBJ 347 | X = 4 348 | ENDDE 349 | 350 | DEFINE CLASS TESTCLASS AS COMMANDBUTTON 351 | PROTECTED SOMEVAR 352 | ADD OBJECT TEST1 as custom 353 | ADD OBJECT TEST2 as subobj WITH X = 4 354 | ADD OBJECT TEST3 as unknownobj WITH X = \'4\' 355 | FUNCTION INIT(X) 356 | NODEFAULT 357 | ENDFUNC 358 | ENDDEFINE 359 | 360 | DEFINE CLASS ABUTTON AS testclass 361 | PROCEDURE Click 362 | TestClass::Click 363 | 364 | ENDDEFINE 365 | 366 | FUNCTION RANDOM_FUNCTION 367 | 368 | * something 369 | LPARAMETERS X 370 | ?x 371 | ENDFUNC 372 | 373 | *comment about testclass2 374 | DEFINE CLASS TESTCLASS2 AS UNKNOWNCLASS 375 | ENDDEFINE 376 | 377 | FUNCTION ANOTHER_RANDOM_FUNCTION 378 | 379 | * something 380 | PARAMETERS X, Y 381 | ?x 382 | ENDFUNC 383 | '''.strip() 384 | output_str = ''' 385 | from __future__ import division, print_function 386 | 387 | from vfp2py import vfpfunc 388 | from vfp2py.vfpfunc import DB, Array, C, F, M, S, lparameters, parameters, vfpclass 389 | 390 | 391 | @lparameters() 392 | def MAIN(): 393 | pass 394 | 395 | 396 | @vfpclass 397 | def Subobj(): 398 | BaseClass = vfpfunc.Custom 399 | 400 | class Subobj(BaseClass): 401 | 402 | @lparameters() 403 | def _assign(self): 404 | BaseClass._assign(self) 405 | self.x = 3 406 | 407 | # comment 408 | @lparameters(\'x\') 409 | def init(self): 410 | super(type(self), self).init() 411 | self.x = S.x 412 | self.argtest(self.x, 2) 413 | 414 | @lparameters(\'x\', \'y\') 415 | def argtest(self): 416 | pass 417 | return Subobj 418 | 419 | # comment about subobj2 420 | 421 | 422 | @vfpclass 423 | def Subobj2(): 424 | BaseClass = SubobjType() 425 | 426 | class Subobj2(BaseClass): 427 | 428 | @lparameters() 429 | def _assign(self): 430 | BaseClass._assign(self) 431 | self.x = 4 432 | return Subobj2 433 | 434 | 435 | @vfpclass 436 | def Testclass(): 437 | BaseClass = vfpfunc.Commandbutton 438 | 439 | class Testclass(BaseClass): 440 | 441 | @lparameters() 442 | def _assign(self): 443 | BaseClass._assign(self) 444 | self.test1 = vfpfunc.Custom(name=\'test1\', parent=self) 445 | self.test2 = Subobj(x=4, name=\'test2\', parent=self) 446 | self.test3 = vfpfunc.create_object(\'Unknownobj\', x=\'4\', name=\'test3\', parent=self) 447 | 448 | @lparameters(\'x\') 449 | def init(self): 450 | # FIX ME: NODEFAULT 451 | pass 452 | return Testclass 453 | 454 | 455 | @vfpclass 456 | def Abutton(): 457 | BaseClass = TestclassType() 458 | 459 | class Abutton(BaseClass): 460 | 461 | @lparameters() 462 | def _assign(self): 463 | BaseClass._assign(self) 464 | 465 | @lparameters() 466 | def click(self): 467 | Testclass.click() 468 | 469 | return Abutton 470 | 471 | 472 | @lparameters(\'x\') 473 | def random_function(): 474 | 475 | # something 476 | print(S.x) 477 | 478 | # comment about testclass2 479 | 480 | 481 | @vfpclass 482 | def Testclass2(): 483 | BaseClass = C[\'Unknownclass\'] 484 | 485 | class Testclass2(BaseClass): 486 | 487 | @lparameters() 488 | def _assign(self): 489 | BaseClass._assign(self) 490 | return Testclass2 491 | 492 | 493 | @parameters(\'x\', \'y\') 494 | def another_random_function(): 495 | 496 | # something 497 | print(S.x) 498 | '''.strip() 499 | test_output_str = vfp2py.vfp2py.prg2py(input_str, 'cp1252').strip() 500 | try: 501 | assert test_output_str == output_str 502 | except AssertionError: 503 | diff = difflib.unified_diff((test_output_str + '\n').splitlines(1), (output_str + '\n').splitlines(1)) 504 | print(''.join(diff)) 505 | raise 506 | 507 | 508 | def Test3(): 509 | input_str = ''' 510 | LOCAL A 511 | DO A 512 | DO A+B 513 | DO A + B 514 | DO ALLTRIM(A) 515 | DO TEST in A 516 | DO TEST in (A) 517 | DO TEST IN A+\'.PRG\' 518 | DO TEST IN A+(B) 519 | DO TEST.PRG 520 | DO TEST IN TEST.PRG 521 | DO FORM SPLASH.SCX 522 | DO FORM SPLASH.SCX NAME splashy 523 | DO FORM SPLASH.SCX NAME splashy LINKED 524 | CD .. 525 | delete file test.txt 526 | erase test.txt 527 | delete file ? 528 | delete file test.txt recycle 529 | '''.strip() 530 | output_str = ''' 531 | M.add_local(\'a\') 532 | F.a() 533 | F[\'a+b\']() 534 | F[S.a + S.b]() 535 | F[S.a.strip()]() 536 | a.test() 537 | vfpfunc.module(S.a).test() 538 | vfpfunc.module(S.a + \'.PRG\').test() 539 | vfpfunc.module(S.a + (S.b)).test() 540 | test.MAIN() 541 | test.test() 542 | vfpfunc.do_form(\'splash.scx\') 543 | vfpfunc.do_form(\'splash.scx\', name=\'splashy\') 544 | vfpfunc.do_form(\'splash.scx\', name=\'splashy\', linked=True) 545 | os.chdir(\'..\') 546 | os.remove(\'test.txt\') 547 | os.remove(\'test.txt\') 548 | os.remove(vfpfunc.getfile(\'\', \'Select file to\', \'Delete\', 0, \'Delete\')) 549 | send2trash(\'test.txt\') 550 | '''.strip() 551 | test_output_str = vfp2py.vfp2py.prg2py(input_str, 'cp1252', parser_start='lines', prepend_data='').strip() 552 | try: 553 | assert test_output_str == output_str 554 | except AssertionError: 555 | diff = difflib.unified_diff((test_output_str + '\n').splitlines(1), (output_str + '\n').splitlines(1)) 556 | print(''.join(diff)) 557 | raise 558 | 559 | 560 | def Test4(): 561 | input_str = ''' 562 | LOCAL test 563 | copy file (test) to tset 564 | rename (test) to tset 565 | mkdir test - test 566 | MD test+test 567 | rmdir (test+test) 568 | rd alltrim(test) 569 | !ls -al &pathname 570 | '''.strip() 571 | output_str = ''' 572 | M.add_local(\'test\') 573 | shutil.copyfile(S.test, \'tset\') 574 | shutil.move(S.test, \'tset\') 575 | os.mkdir(S.test - S.test) 576 | os.mkdir(\'test+test\') 577 | os.rmdir(S.test + S.test) 578 | os.rmdir(S.test.strip()) 579 | vfpfunc.macro_eval(\'!ls -al &pathname\') 580 | '''.strip() 581 | test_output_str = vfp2py.vfp2py.prg2py(input_str, 'cp1252', parser_start='lines', prepend_data='').strip() 582 | try: 583 | assert test_output_str == output_str 584 | except AssertionError: 585 | diff = difflib.unified_diff((test_output_str + '\n').splitlines(1), (output_str + '\n').splitlines(1)) 586 | print(''.join(diff)) 587 | raise 588 | 589 | 590 | def Test5(): 591 | input_str = ''' 592 | CREAT CURSO TEST_CURSOR (SOMEFIELD N(3)) 593 | continue 594 | LOCAL SEARCH_FOR, COUNTVAL, SUMVAL 595 | SEARCH_FOR = \'PAUL\' 596 | SEEK ALLTRIM(SEARCH_FOR) 597 | COUNT FOR TEST = 3 TO COUNTVAL 598 | SUM T * T FOR T > 0 TO SUMVAL 599 | LOCATE WHILE X > 5 NOOPTIMIZE 600 | RELEASE SEARCH_FOR, COUNTVAL, SUMVAL 601 | update test set a=b, c=d, e=f where x=3 602 | '''.strip() 603 | output_str = ''' 604 | DB.create_cursor(\'test_cursor\', \'somefield n(3)\', \'\') 605 | DB.continue_locate() 606 | M.add_local(\'search_for\', \'countval\', \'sumval\') 607 | S.search_for = \'PAUL\' 608 | DB.seek(None, S.search_for.strip()) 609 | S.countval = DB.count(None, (\'all\',), for_cond=lambda: S.test == 3) 610 | S.sumval = DB.sum(None, (\'all\',), lambda: S.t * S.t, for_cond=lambda: S.t > 0) 611 | DB.locate(nooptimize=True, while_cond=lambda: S.x > 5) 612 | del M.search_for, M.countval, M.sumval 613 | DB.update(\'test\', [(\'a\', S.b), (\'c\', S.d), (\'e\', S.f)], where=lambda: S.x == 3) 614 | '''.strip() 615 | test_output_str = vfp2py.vfp2py.prg2py(input_str, 'cp1252', parser_start='lines', prepend_data='').strip() 616 | try: 617 | assert test_output_str == output_str 618 | except AssertionError: 619 | diff = difflib.unified_diff((test_output_str + '\n').splitlines(1), (output_str + '\n').splitlines(1)) 620 | print(''.join(diff)) 621 | raise 622 | 623 | 624 | def Test6(): 625 | input_str = ''' 626 | MKDIR TEST 627 | ?DATE() 628 | ?PI() 629 | '''.strip() 630 | output_str = ''' 631 | from __future__ import division, print_function 632 | 633 | import datetime as dt 634 | import math 635 | import os 636 | 637 | from vfp2py import vfpfunc 638 | from vfp2py.vfpfunc import DB, Array, C, F, M, S, lparameters, parameters, vfpclass 639 | 640 | 641 | @lparameters() 642 | def MAIN(): 643 | os.mkdir(\'test\') 644 | print(dt.datetime.now().date()) 645 | print(math.pi) 646 | '''.strip() 647 | test_output_str = vfp2py.vfp2py.prg2py(input_str, 'cp1252').strip() 648 | try: 649 | assert test_output_str == output_str 650 | except AssertionError: 651 | diff = difflib.unified_diff((test_output_str + '\n').splitlines(1), (output_str + '\n').splitlines(1)) 652 | print(''.join(diff)) 653 | raise 654 | 655 | 656 | def Test7(): 657 | input_str = ''' 658 | PUSH KEY CLEAR 659 | PUSH KEY 660 | PUSH MENU test 661 | PUSH POPUP test 662 | POP KEY ALL 663 | POP KEY 664 | POP MENU TO MASTER test 665 | POP POPUP test 666 | declare integer printf in c as c_printf string 667 | CLEAR DLLS "TEST1", "TEST2" 668 | CLEAR MACROS 669 | CLEAR EVENTS 670 | CLEAR ALL 671 | CLEAR READ 672 | CLEAR READ ALL 673 | CLEAR 674 | '''.strip() 675 | output_str = ''' 676 | # FIX ME: PUSH KEY CLEAR 677 | # FIX ME: PUSH KEY 678 | # FIX ME: PUSH MENU test 679 | # FIX ME: PUSH POPUP test 680 | # FIX ME: POP KEY ALL 681 | # FIX ME: POP KEY 682 | # FIX ME: POP MENU TO MASTER test 683 | # FIX ME: POP POPUP test 684 | F.dll_declare(\'c\', \'printf\', \'c_printf\') 685 | F.dll_clear(\'test1\', \'test2\') 686 | vfpfunc.clear(\'macros\') 687 | vfpfunc.clear(\'events\') 688 | vfpfunc.clear(None, \'all\') 689 | vfpfunc.clear(\'read\') 690 | vfpfunc.clear(\'read\', \'all\') 691 | vfpfunc.clear(None) 692 | '''.strip() 693 | test_output_str = vfp2py.vfp2py.prg2py(input_str, 'cp1252', parser_start='lines', prepend_data='').strip() 694 | try: 695 | assert test_output_str == output_str 696 | except AssertionError: 697 | diff = difflib.unified_diff((test_output_str + '\n').splitlines(1), (output_str + '\n').splitlines(1)) 698 | print(''.join(diff)) 699 | raise 700 | 701 | 702 | def Test8(): 703 | input_str = ''' 704 | LOCAL X, Y 705 | X = .F. 706 | Y = \'failed\' 707 | ASSERT NOT X 708 | ASSERT X = .T. MESSAGE Y + \' ASSERT\' 709 | '''.strip() 710 | output_str = ''' 711 | M.add_local(\'x\', \'y\') 712 | S.x = False 713 | S.y = \'failed\' 714 | assert not S.x 715 | assert S.x == True, S.y + \' ASSERT\' 716 | '''.strip() 717 | test_output_str = vfp2py.vfp2py.prg2py(input_str, 'cp1252', parser_start='lines', prepend_data='').strip() 718 | try: 719 | assert test_output_str == output_str 720 | except AssertionError: 721 | diff = difflib.unified_diff((test_output_str + '\n').splitlines(1), (output_str + '\n').splitlines(1)) 722 | print(''.join(diff)) 723 | raise 724 | 725 | 726 | def Test9(): 727 | input_str = ''' 728 | SET COMPATIBLE OFF 729 | SET COMPATIBLE DB4 730 | SET COMPATIBLE FOXPLUS 731 | SET COMPATIBLE ON 732 | SET COMPATIBLE FOXPLUS PROMPT 733 | SET COMPATIBLE DB4 NOPROMPT 734 | SET CLASSLIB TO TEST 735 | SET CLASSLIB TO TEST IN TEST 736 | SET CLASSLIB TO TEST ALIAS NOTTEST 737 | SET CLASSLIB TO TEST IN TEST ALIAS NOTTEST ADDITIVE 738 | SET TABLEPROMPT ON 739 | SET EXCLUSIVE ON 740 | SET DEFAULT TO SOMETHING 741 | SET HELP OFF 742 | SET HELP TO TEST 743 | SET HELP COLLECTION test 744 | SET HELP SYSTEM 745 | SET RELATION TO a=b INTO test 746 | SET TALK OFF 747 | SET UDFPARMS TO REFERENCE 748 | SET UDFPARMS TO VALUE 749 | '''.strip() 750 | output_str = ''' 751 | vfpfunc.set(\'compatible\', \'OFF\', set_value=True) 752 | vfpfunc.set(\'compatible\', \'ON\', set_value=True) 753 | vfpfunc.set(\'compatible\', \'OFF\', set_value=True) 754 | vfpfunc.set(\'compatible\', \'ON\', set_value=True) 755 | vfpfunc.set(\'compatible\', \'OFF\', \'PROMPT\', set_value=True) 756 | vfpfunc.set(\'compatible\', \'ON\', \'NOPROMPT\', set_value=True) 757 | vfpfunc.set(\'classlib\', \'test\', set_value=True) 758 | vfpfunc.set(\'classlib\', \'test\', class_file=\'test\', set_value=True) 759 | vfpfunc.set(\'classlib\', \'test\', alias=\'nottest\', set_value=True) 760 | vfpfunc.set(\'classlib\', \'test\', alias=\'nottest\', class_file=\'test\', additive=True, set_value=True) 761 | vfpfunc.set(\'tableprompt\', \'ON\', set_value=True) 762 | vfpfunc.set(\'exclusive\', \'ON\', set_value=True) 763 | # FIX ME: SET DEFAULT TO SOMETHING 764 | # FIX ME: SET HELP OFF 765 | # FIX ME: SET HELP TO TEST 766 | # FIX ME: SET HELP COLLECTION test 767 | # FIX ME: SET HELP SYSTEM 768 | # FIX ME: SET RELATION TO a=b INTO test 769 | vfpfunc.set(\'talk\', \'OFF\', set_value=True) 770 | vfpfunc.set(\'udfparms\', \'reference\', set_value=True) 771 | vfpfunc.set(\'udfparms\', \'value\', set_value=True) 772 | '''.strip() 773 | test_output_str = vfp2py.vfp2py.prg2py(input_str, 'cp1252', parser_start='lines', prepend_data='').strip() 774 | try: 775 | assert test_output_str == output_str 776 | except AssertionError: 777 | diff = difflib.unified_diff((test_output_str + '\n').splitlines(1), (output_str + '\n').splitlines(1)) 778 | print(''.join(diff)) 779 | raise 780 | 781 | 782 | def Test10(): 783 | input_str = ''' 784 | APPEND FROM TABLE_NAME 785 | APPEND FROM TABLE_NAME TYPE DELIMITED 786 | APPEND FROM \'table\' + \'_\' + \'name\' TYPE \'Delimited\' 787 | APPEND FROM ARRAY TEST 788 | '''.strip() 789 | output_str = ''' 790 | DB.append_from(None, \'table_name\') 791 | DB.append_from(None, \'table_name\', filetype=\'delimited\') 792 | DB.append_from(None, \'table_name\', filetype=\'delimited\') 793 | DB.insert(None, S.test) 794 | '''.strip() 795 | test_output_str = vfp2py.vfp2py.prg2py(input_str, 'cp1252', parser_start='lines', prepend_data='').strip() 796 | try: 797 | assert test_output_str == output_str 798 | except AssertionError: 799 | diff = difflib.unified_diff((test_output_str + '\n').splitlines(1), (output_str + '\n').splitlines(1)) 800 | print(''.join(diff)) 801 | raise 802 | 803 | 804 | def Test11(): 805 | input_str = ''' 806 | LOCAL MYFILE, mydir 807 | MYFILE = \'c:\\test\\test.prg\' 808 | MYDIR = \'c:\\test\\test\\dir\' 809 | ?file(myfile) 810 | ?justdrive(MYFILE) 811 | ?justpath(MYFILE) 812 | ?justfname(MYFILE) 813 | ?juststem(myfile) 814 | ?JUSTEXT(myfile) 815 | ?FORCEEXT(myfile, \'py\') 816 | ?directory(mydir) 817 | ?justdrive(MYDIR) 818 | ?justpath(MYDIR) 819 | ?justfname(MYDIR) 820 | ?juststem(mydir) 821 | ?JUSTEXT(mydir) 822 | ?FORCEEXT(mydir, \'PY\') 823 | ?ADDBS(MYDIR) + \'dir1\' 824 | ?ADDBS(ADDBS(MYDIR) + \'dir1\') + \'dir2\' 825 | ?ADDBS(ADDBS(ADDBS(MYDIR) + \'dir1\') + \'dir2\') + \'dir3\' 826 | ?CURDIR() 827 | RELEASE MYFILE, MYDIR 828 | '''.strip() 829 | output_str = ''' 830 | M.add_local(\'myfile\', \'mydir\') 831 | S.myfile = \'c:\\\\test\\\\test.prg\' 832 | S.mydir = \'c:\\\\test\\\\test\\\\dir\' 833 | print(os.path.isfile(S.myfile)) 834 | print(os.path.splitdrive(S.myfile)[0]) 835 | print(os.path.dirname(S.myfile)) 836 | print(os.path.basename(S.myfile)) 837 | print(os.path.splitext(os.path.basename(S.myfile))[0]) 838 | print(os.path.splitext(S.myfile)[1][1:]) 839 | print(os.path.splitext(S.myfile)[0] + \'.\' + \'py\') 840 | print(os.path.isdir(S.mydir)) 841 | print(os.path.splitdrive(S.mydir)[0]) 842 | print(os.path.dirname(S.mydir)) 843 | print(os.path.basename(S.mydir)) 844 | print(os.path.splitext(os.path.basename(S.mydir))[0]) 845 | print(os.path.splitext(S.mydir)[1][1:]) 846 | print(os.path.splitext(S.mydir)[0] + \'.\' + \'py\') 847 | print(os.path.join(S.mydir, \'dir1\')) 848 | print(os.path.join(S.mydir, \'dir1\', \'dir2\')) 849 | print(os.path.join(S.mydir, \'dir1\', \'dir2\', \'dir3\')) 850 | print(os.getcwd()) 851 | del M.myfile, M.mydir 852 | '''.strip() 853 | test_output_str = vfp2py.vfp2py.prg2py(input_str, 'cp1252', parser_start='lines', prepend_data='').strip() 854 | try: 855 | assert test_output_str == output_str 856 | except AssertionError: 857 | diff = difflib.unified_diff((test_output_str + '\n').splitlines(1), (output_str + '\n').splitlines(1)) 858 | print(''.join(diff)) 859 | raise 860 | 861 | 862 | def Test12(): 863 | input_str = ''' 864 | LOCAL ARRAY somearray[2, 5] 865 | LOCAL pytuple, pylist, pydict 866 | pytuple = createobject(\'pythontuple\', \'a\', 3, .T.) 867 | pylist = createobject(\'pythonlist\', @somearray) 868 | pylist.callmethod(\'append\', createobject(\'pythontuple\', \'appended value\')) 869 | pydict = createobject(\'pythondictionary\') 870 | pydict.setitem(\'one\', 1) 871 | ?pydict.getitem(\'one\') 872 | pythonfunctioncall(\'test\', \'test\', pytuple) 873 | '''.strip() 874 | output_str = ''' 875 | M.add_local(somearray=Array(2, 5)) 876 | M.add_local(\'pytuple\', \'pylist\', \'pydict\') 877 | S.pytuple = (\'a\', 3, True) 878 | S.pylist = S.somearray.data[:] 879 | S.pylist.append(\'appended value\') 880 | S.pydict = {} 881 | S.pydict[\'one\'] = 1 882 | print(S.pydict[\'one\']) 883 | test.test(*S.pytuple) 884 | '''.strip() 885 | test_output_str = vfp2py.vfp2py.prg2py(input_str, 'cp1252', parser_start='lines', prepend_data='').strip() 886 | try: 887 | assert test_output_str == output_str 888 | except AssertionError: 889 | diff = difflib.unified_diff((test_output_str + '\n').splitlines(1), (output_str + '\n').splitlines(1)) 890 | print(''.join(diff)) 891 | raise 892 | 893 | 894 | def Test13(): 895 | input_str = ''' 896 | PUBLIC ARRAY somearray[2, 5] 897 | public array def[10] 898 | SOMEARRAY(1, 4) = 3 899 | PRIVATE TEST, somearray[2, 5] 900 | EXTERNAL ARRAY someotherarray[3] 901 | EXTERNAL PROCEDURE test 902 | '''.strip() 903 | output_str = ''' 904 | M.add_public(somearray=Array(2, 5)) 905 | M.add_public(**{\'def\': Array(10)}) 906 | S.somearray[1, 4] = 3 907 | M.add_private(\'test\', somearray=Array(2, 5)) 908 | # FIX ME: EXTERNAL ARRAY someotherarray[3] 909 | # FIX ME: EXTERNAL PROCEDURE test 910 | '''.strip() 911 | test_output_str = vfp2py.vfp2py.prg2py(input_str, 'cp1252', parser_start='lines', prepend_data='').strip() 912 | try: 913 | assert test_output_str == output_str 914 | except AssertionError: 915 | diff = difflib.unified_diff((test_output_str + '\n').splitlines(1), (output_str + '\n').splitlines(1)) 916 | print(''.join(diff)) 917 | raise 918 | 919 | 920 | def Test14(): 921 | input_str = ''' 922 | Try 923 | assert .f. 924 | catch to oerr 925 | throw 926 | endtry 927 | throw \'Error\' + \' Message\' 928 | RETRY 929 | '''.strip() 930 | output_str = ''' 931 | try: 932 | assert False 933 | except Exception as err: 934 | S.oerr = vfpfunc.Exception.from_pyexception(err) 935 | raise 936 | raise Exception(\'Error Message\') 937 | # FIX ME: RETRY 938 | '''.strip() 939 | test_output_str = vfp2py.vfp2py.prg2py(input_str, 'cp1252', parser_start='lines', prepend_data='').strip() 940 | try: 941 | assert test_output_str == output_str 942 | except AssertionError: 943 | diff = difflib.unified_diff((test_output_str + '\n').splitlines(1), (output_str + '\n').splitlines(1)) 944 | print(''.join(diff)) 945 | raise 946 | 947 | 948 | def Test15(): 949 | input_str = ''' 950 | LOCAL CNT_FIELDS 951 | LOCAL ARRAY MAIN_ARRAY(1) 952 | CNT_FIELDS = AFIELDS(MAIN_ARRAY) + 32 953 | CNT_FIELDS = AFIELDS(MAIN_ARRAY2, \'report\') + 47 954 | T = aINS(main_array, 3) 955 | T = aINS(main_array, 3, 2) 956 | vals_copied = acopy(main_array, new_array) 957 | vals_copied = acopy(main_array, new_array, 5) 958 | vals_copied = acopy(main_array, new_array, 5, num_elements) 959 | vals_copied = acopy(main_array, new_array, 5, num_elements, 0) 960 | '''.strip() 961 | output_str = ''' 962 | M.add_local(\'cnt_fields\') 963 | M.add_local(main_array=Array(1)) 964 | S.cnt_fields = DB.afields(None, \'main_array\', (locals(), S)) + 32 965 | S.cnt_fields = DB.afields(\'report\', \'main_array2\', (locals(), S)) + 47 966 | S.t = S.main_array.insert(None, 3) 967 | S.t = S.main_array.insert(None, 3, 2) 968 | S.vals_copied = S.main_array.copy(\'new_array\') 969 | S.vals_copied = S.main_array.copy(\'new_array\', 5) 970 | S.vals_copied = S.main_array.copy(\'new_array\', 5, S.num_elements) 971 | S.vals_copied = S.main_array.copy(\'new_array\', 5, S.num_elements, 0) 972 | '''.strip() 973 | test_output_str = vfp2py.vfp2py.prg2py(input_str, 'cp1252', parser_start='lines', prepend_data='').strip() 974 | try: 975 | assert test_output_str == output_str 976 | except AssertionError: 977 | diff = difflib.unified_diff((test_output_str + '\n').splitlines(1), (output_str + '\n').splitlines(1)) 978 | print(''.join(diff)) 979 | raise 980 | 981 | 982 | def Test16(): 983 | input_str = ''' 984 | SKIP 985 | SKIP 10 986 | SKIP IN TEST 987 | SKIP someval IN TEST 988 | '''.strip() 989 | output_str = ''' 990 | DB.skip(None, 1) 991 | DB.skip(None, 10) 992 | DB.skip(\'test\', 1) 993 | DB.skip(\'test\', S.someval) 994 | '''.strip() 995 | test_output_str = vfp2py.vfp2py.prg2py(input_str, 'cp1252', parser_start='lines', prepend_data='').strip() 996 | try: 997 | assert test_output_str == output_str 998 | except AssertionError: 999 | diff = difflib.unified_diff((test_output_str + '\n').splitlines(1), (output_str + '\n').splitlines(1)) 1000 | print(''.join(diff)) 1001 | raise 1002 | 1003 | 1004 | def Test17(): 1005 | input_str = ''' 1006 | ON ERROR 1007 | ON ERROR DO test 1008 | ON SHUTDOWN 1009 | ON SHUTDOWN DO SHUTDOWN IN SHUTDOWN 1010 | ON SHUTDOWN QUIT 1011 | ON ESCAPE QUIT 1012 | ON KEY LABEL F12 ?\'KEY PRESSED\' 1013 | '''.strip() 1014 | output_str = ''' 1015 | vfpfunc.error_func = None 1016 | vfpfunc.error_func = lambda: F.test() 1017 | vfpfunc.shutdown_func = None 1018 | vfpfunc.shutdown_func = lambda: shutdown.shutdown() 1019 | vfpfunc.shutdown_func = lambda: vfpfunc.quit() 1020 | vfpfunc.escape_func = lambda: vfpfunc.quit() 1021 | vfpfunc.on_key[\'f12\'] = lambda: print(\'KEY PRESSED\') 1022 | '''.strip() 1023 | test_output_str = vfp2py.vfp2py.prg2py(input_str, 'cp1252', parser_start='lines', prepend_data='').strip() 1024 | try: 1025 | assert test_output_str == output_str 1026 | except AssertionError: 1027 | diff = difflib.unified_diff((test_output_str + '\n').splitlines(1), (output_str + '\n').splitlines(1)) 1028 | print(''.join(diff)) 1029 | raise 1030 | 1031 | 1032 | def Test18(): 1033 | input_str = ''' 1034 | @x, y 1035 | @x, y CLEAR 1036 | @x, y CLEAR TO a, b 1037 | @x, y say \'hello \' + username 1038 | @x, y say \'hello \' + username STYLE thestyle 1039 | '''.strip() 1040 | output_str = ''' 1041 | print() # @x, y 1042 | print() # @x, y CLEAR 1043 | print() # @x, y CLEAR TO a, b 1044 | print(\'hello \' + S.username) # @x, y say \'hello \' + username 1045 | print(\'hello \' + S.username) # @x, y say \'hello \' + username STYLE thestyle 1046 | '''.strip() 1047 | test_output_str = vfp2py.vfp2py.prg2py(input_str, 'cp1252', parser_start='lines', prepend_data='').strip() 1048 | try: 1049 | assert test_output_str == output_str 1050 | except AssertionError: 1051 | diff = difflib.unified_diff((test_output_str + '\n').splitlines(1), (output_str + '\n').splitlines(1)) 1052 | print(''.join(diff)) 1053 | raise 1054 | 1055 | 1056 | def Test19(): 1057 | input_str = ''' 1058 | scatter name test 1059 | scatter blank memvar 1060 | scatter to somearray 1061 | gather name test 1062 | gather memvar 1063 | gather from somearray 1064 | '''.strip() 1065 | output_str = ''' 1066 | S.test = vfpfunc.scatter(totype=\'name\') 1067 | vfpfunc.scatter(blank=True) 1068 | S.somearray = vfpfunc.scatter(totype=\'array\') 1069 | vfpfunc.gather(val=S.test) 1070 | vfpfunc.gather() 1071 | vfpfunc.gather(val=S.somearray) 1072 | '''.strip() 1073 | test_output_str = vfp2py.vfp2py.prg2py(input_str, 'cp1252', parser_start='lines', prepend_data='').strip() 1074 | try: 1075 | assert test_output_str == output_str 1076 | except AssertionError: 1077 | diff = difflib.unified_diff((test_output_str + '\n').splitlines(1), (output_str + '\n').splitlines(1)) 1078 | print(''.join(diff)) 1079 | raise 1080 | 1081 | 1082 | def Test20(): 1083 | input_str = ''' 1084 | REPORT FORM TEST.FRX TO PRINTER NOCONSOLE 1085 | REPORT FORM ? 1086 | '''.strip() 1087 | output_str = ''' 1088 | vfpfunc.report_form(\'test.frx\') 1089 | vfpfunc.report_form(None) 1090 | '''.strip() 1091 | test_output_str = vfp2py.vfp2py.prg2py(input_str, 'cp1252', parser_start='lines', prepend_data='').strip() 1092 | try: 1093 | assert test_output_str == output_str 1094 | except AssertionError: 1095 | diff = difflib.unified_diff((test_output_str + '\n').splitlines(1), (output_str + '\n').splitlines(1)) 1096 | print(''.join(diff)) 1097 | raise 1098 | 1099 | -------------------------------------------------------------------------------- /vfp2py/VisualFoxpro9.g4: -------------------------------------------------------------------------------- 1 | grammar VisualFoxpro9 2 | ; 3 | 4 | preprocessorCode 5 | : preprocessorLines EOF 6 | ; 7 | 8 | preprocessorLines 9 | : preprocessorLine* 10 | ; 11 | 12 | preprocessorLine 13 | : '#' (IF expr | IFDEF identifier) NL 14 | ifBody=preprocessorLines 15 | ('#' ELSE NL 16 | elseBody=preprocessorLines)? 17 | '#' ENDIF lineEnd #preprocessorIf 18 | | '#' DEFINE identifier (~NL)* lineEnd #preprocessorDefine 19 | | '#' UNDEFINE identifier lineEnd #preprocessorUndefine 20 | | '#' INCLUDE specialExpr lineEnd #preprocessorInclude 21 | | '#' (~(IF | ELSE | ENDIF | DEFINE | INCLUDE | NL) (~NL)*) lineEnd #preprocessorJunk 22 | | (NL | ~('#' | NL | EOF) (~NL)* lineEnd) #nonpreprocessorLine 23 | ; 24 | 25 | prg 26 | : (classDef | funcDef)* EOF 27 | ; 28 | 29 | lineComment 30 | : ('*' | NOTE) (~NL)* lineEnd 31 | | NL 32 | ; 33 | 34 | line 35 | : lineComment 36 | | MACROLINE lineEnd 37 | | (controlStmt | cmd) lineEnd 38 | ; 39 | 40 | lineEnd 41 | : NL 42 | | EOF 43 | ; 44 | 45 | lines 46 | : line* 47 | ; 48 | 49 | nongreedyLines 50 | : line*? 51 | ; 52 | 53 | classDefStart 54 | : DEFINE CLASS identifier asTypeOf? NL 55 | ; 56 | 57 | classDef 58 | : classDefStart classProperty* ENDDEFINE lineEnd lineComment* 59 | ; 60 | 61 | classProperty 62 | : cmd NL 63 | | lineComment 64 | | funcDef 65 | ; 66 | 67 | parameter 68 | : idAttr asType? 69 | ; 70 | 71 | parameters 72 | : parameter (',' parameter)* 73 | ; 74 | 75 | funcDefStart 76 | : SCOPE? PROCEDURE idAttr2 ('(' parameters? ')')? asType? NL 77 | ; 78 | 79 | funcDef 80 | : funcDefStart lines (ENDPROC lineEnd lineComment*)? 81 | ; 82 | 83 | ifStart 84 | : IF expr THEN? NL 85 | ; 86 | 87 | ifStmt 88 | : ifStart ifBody=lines (ELSE NL elseBody=lines)? ENDIF 89 | ; 90 | 91 | forStart 92 | : FOR idAttr '=' loopStart=expr TO loopStop=expr (STEP loopStep=expr)? NL 93 | | FOR EACH idAttr IN expr FOXOBJECT? NL 94 | ; 95 | 96 | forEnd 97 | : (ENDFOR | NEXT idAttr?) 98 | ; 99 | 100 | forStmt 101 | : forStart lines forEnd 102 | ; 103 | 104 | singleCase 105 | : CASE expr NL nongreedyLines 106 | ; 107 | 108 | otherwise 109 | : OTHERWISE NL lines 110 | ; 111 | 112 | caseStmt 113 | : DO CASE NL lineComment* singleCase* otherwise? ENDCASE 114 | ; 115 | 116 | whileStart 117 | : DO? WHILE expr NL 118 | ; 119 | 120 | whileStmt 121 | : whileStart lines ENDDO 122 | ; 123 | 124 | withStmt 125 | : WITH idAttr asTypeOf? NL lines ENDWITH 126 | ; 127 | 128 | scanStmt 129 | : SCAN scopeClause? (FOR expr)? NL lines ENDSCAN 130 | ; 131 | 132 | tryStmt 133 | : TRY NL tryLines=lines (CATCH (WHEN expr | TO identifier)* NL catchLines=lines)? (FINALLY NL finallyLines=lines)? ENDTRY 134 | ; 135 | 136 | controlStmt 137 | : whileStmt 138 | | ifStmt 139 | | caseStmt 140 | | forStmt 141 | | withStmt 142 | | scanStmt 143 | | tryStmt 144 | ; 145 | 146 | cmd 147 | : ADD OBJECT identifier asType (WITH idAttr '=' expr (',' idAttr '=' expr)*)? #addObject 148 | | (PROGRAMCONTROL) #programControl 149 | | '@' args (CLEAR (TO toArgs=args)? | (SAY sayExpr=expr | STYLE styleExpr=expr)+)? #atPos 150 | | DO (FORM ('?' | specialExpr) (NAME nameId=identifier LINKED? | WITH args | TO toId=identifier | NOSHOW)* 151 | | specialExpr (IN specialExpr | WITH args)*) #funcDo 152 | | (STORE expr TO idAttr (',' idAttr)* 153 | | idAttr '=' expr) #assign 154 | | (((SCOPE|EXTERNAL) (ARRAY | DIMENSION | DECLARE)? | DIMENSION | DECLARE | PARAMETER) declarationItem (',' declarationItem)* 155 | | EXTERNAL PROCEDURE specialExpr) #declaration 156 | | ('?' '?'? | DEBUGOUT) args? #printStmt 157 | | WAIT (TO toExpr=expr | WINDOW (AT atExpr1=expr ',' atExpr2=expr)? | NOWAIT | CLEAR | NOCLEAR | TIMEOUT timeout=expr | message=expr)* #waitCmd 158 | | (ERASE | DELETE FILE) (specialExpr|'?') RECYCLE? #deleteFile 159 | | (RENAME | COPY FILE) specialExpr TO specialExpr #copyMoveFile 160 | | (CHDIR | MKDIR | RMDIR) specialExpr #chMkRmDir 161 | | RETURN expr? #returnStmt 162 | | ON ((PAD specialExpr | BAR NUMBER_LITERAL) OF specialExpr (ACTIVATE (POPUP | MENU) specialExpr)? | (KEY (LABEL identifier ('+' identifier)?)? | SELECTION BAR NUMBER_LITERAL OF specialExpr | identifier) cmd?) #onStmt 163 | | RELEASE (ALL | vartype=(PROCEDURE|CLASSLIB)? args | POPUP args EXTENDED?) #release 164 | | SET setCmd #setStmt 165 | | PUSH (KEY CLEAR? | (MENU | POPUP) identifier) #push 166 | | POP (KEY ALL? | MENU (identifier | TO MASTER)* | POPUP identifier) #pop 167 | | KEYBOARD expr PLAIN? CLEAR? #keyboard 168 | 169 | | DEFINE MENU specialExpr (BAR (AT LINE NUMBER_LITERAL)?) (IN (SCREEN | WINDOW? specialExpr))? NOMARGIN? #defineMenu 170 | | DEFINE PAD specialExpr OF specialExpr PROMPT expr (AT NUMBER_LITERAL ',' NUMBER_LITERAL)? 171 | (BEFORE identifier | AFTER identifier)? (NEGOTIATE identifier (',' identifier)?)? 172 | (FONT identifier (',' NUMBER_LITERAL (',' expr (',' identifier)?)?)?)? (STYLE identifier)? 173 | (MESSAGE expr)? (KEY identifier ('+' identifier)? (',' expr)?)? (MARK identifier)? 174 | (SKIPKW (FOR expr)?)? (COLOR SCHEME NUMBER_LITERAL)? #definePad 175 | | DEFINE POPUP specialExpr SHADOW? MARGIN? RELATIVE? (COLOR SCHEME NUMBER_LITERAL)? #definePopup 176 | | DEFINE BAR NUMBER_LITERAL OF specialExpr PROMPT expr (MESSAGE expr)? #defineBar 177 | | ACTIVATE WINDOW (parameters | ALL) (IN (WINDOW? identifier | SCREEN))? (BOTTOM | TOP | SAME)? NOSHOW? #activateWindow 178 | | ACTIVATE SCREEN #activateScreen 179 | | ACTIVATE MENU specialExpr NOWAIT? (PAD specialExpr)? #activateMenu 180 | | ACTIVATE POPUP identifier #activatePopup 181 | 182 | | DEACTIVATE (MENU|POPUP) (ALL | parameters) #deactivate 183 | 184 | | MODIFY WINDOW (SCREEN | identifier) (FROM args TO args | AT args SIZE args | FONT args | STYLE expr | TITLE expr | identifier | ICON FILE specialExpr | FILL FILE specialExpr | COLOR SCHEME expr | COLOR args)* #modifyWindow 185 | | MODIFY (FILE | COMMAND) ('?' | specialExpr) (IN (WINDOW identifier | SCREEN) | AS expr | identifier)* #modifyFile 186 | 187 | | ERROR expr? #raiseError 188 | | THROW expr? #throwError 189 | 190 | | CREATE (TABLE|DBF|CURSOR) specialExpr (FREE? '(' tableField (',' tableField)* ')' | FROM ARRAY expr) #createTable 191 | | ALTER TABLE specialExpr (ADD COLUMN tableField | DROP COLUMN identifier | ALTER COLUMN identifier (NOT NULL)?)* #alterTable 192 | | SELECT (tablename=specialExpr | (DISTINCT? (specialArgs | '*') (FROM fromExpr=specialExpr)? (WHERE whereExpr=expr)? (INTO (TABLE | CURSOR) intoExpr=specialExpr)? (ORDER BY orderbyid=identifier)?)) #select 193 | | USE (IN workArea=specialExpr | ORDER TAG? orderExpr=expr | ALIAS aliasExpr=specialExpr | SHARED | EXCLUSIVE | NOUPDATE | name=specialExpr)* #use 194 | | LOCATE queryCondition* #locate 195 | | CONTINUE #continueLocate 196 | | RETRY #retry 197 | | REPLACE (queryCondition | specialExpr WITH expr)* #replace 198 | | INDEX ON specialExpr (TAG | TO) specialExpr COMPACT? (ASCENDING | DESCENDING)? (UNIQUE | CANDIDATE)? ADDITIVE? #indexOn 199 | | COUNT (TO toExpr=expr | queryCondition)* #count 200 | | SUM (TO toExpr=expr | queryCondition | sumExpr=expr)* #sum 201 | | SORT TO expr ON expr ('/' identifier)* (',' expr ('/' identifier)*)* (ASCENDING | DESCENDING | FIELDS (LIKE | EXCEPT)? args | queryCondition)* #sortCmd 202 | | (RECALL | DELETE) (queryCondition | IN inExpr=specialExpr)* #deleteRecord 203 | | APPEND FROM (ARRAY expr | specialExpr FOR expr | specialExpr ) (TYPE typeExpr=specialExpr)? #appendFrom 204 | | APPEND BLANK? (IN specialExpr NOMENU | IN specialExpr)? #append 205 | | INSERT INTO specialExpr (FROM (ARRAY expr | MEMVAR | NAME expr) | ('(' specialArgs ')')? VALUES '(' args ')') #insert 206 | | SKIPKW expr? (IN specialExpr)? #skipRecord 207 | | PACK (DATABASE | (MEMO | DBF)? (IN workArea=specialExpr | tableName=specialExpr IN workArea=specialExpr | tableName=specialExpr)?) #pack 208 | | REINDEX COMPACT? #reindex 209 | | SEEK seekExpr=expr ((ORDER orderExpr=expr | TAG tagName=specialExpr (OF cdxFileExpr=specialExpr)? | idxFileExpr=specialExpr) (ASCENDING | DESCENDING)?)? (IN tablenameExpr=specialExpr)? #seekRecord 210 | | UPDATE tableExpr=specialExpr SET identifier '=' expr (',' identifier '=' expr)* (FROM FORCE? fromArgs=specialArgs | JOIN joinArgs=specialArgs | WHERE whereExpr=expr)* #updateCmd 211 | | GOTO (TOP | BOTTOM | RECORD? expr) (IN specialExpr)? #goRecord 212 | | COPY (TO ARRAY specialExpr | FIELDS (LIKE | EXCEPT) ? args | queryCondition)* #copyToArray 213 | | COPY STRUCTURE? TO specialExpr #copyTo 214 | | ZAP (IN specialExpr)? #zapTable 215 | | BROWSE (~NL)* #browse 216 | | SCATTER (FIELDS (LIKE | EXCEPT)? args | MEMO | BLANK | MEMVAR | NAME expr ADDITIVE? | TO expr)* #scatterExpr 217 | | GATHER (FIELDS (LIKE | EXCEPT)? args | MEMO | MEMVAR | NAME expr | FROM expr)* #gatherExpr 218 | 219 | | CLOSE ((DATABASE | INDEXES | TABLE) ALL? | ALL) #closeStmt 220 | | (READ EVENTS? | DOEVENTS FORCE?) #readEvent 221 | | UNLOCK ALL #unlockCmd 222 | | CLEAR (ALL | CLASS expr | CLASSLIB specialExpr | DEBUG | DLLS specialArgs | EVENTS | ERROR | FIELDS | GETS | MACROS | MEMORY | MENUS | POPUP | PROGRAM | PROMPT | READ ALL? | RESOURCES expr | TYPEAHEAD | WINDOW)? #clearStmt 223 | | REPORT FORM ('?' | specialExpr) (NOEJECT | TO PRINTER PROMPT? | NOCONSOLE)* #report 224 | | DECLARE returnType=datatype? identifier IN specialExpr (AS alias=identifier)? dllArgs? #dllDeclare 225 | | (RUN | EXCLAMATION) ('/' identifier)? (~NL)* #shellRun 226 | | ASSERT expr (MESSAGE expr)? #assert 227 | | COMPILE (DATABASE | FORM | CLASSLIB | LABEL | REPORT)? (ALL | ENCRYPT | NODEBUG | AS specialExpr | specialExpr)* #compileCmd 228 | | LIST scopeClause #listStmt 229 | | SAVE TO (MEMO specialExpr | specialExpr) (ALL (LIKE | EXCEPT) specialExpr)? #saveToCmd 230 | | RESTORE FROM specialExpr ADDITIVE? #restoreCmd 231 | | ZOOM WINDOW specialExpr (MIN | MAX | NORM) (AT expr ',' expr | FROM AT expr ',' expr (SIZE AT expr ',' expr | TO expr ',' expr)?)? #zoomCmd 232 | | TEXT (TO idAttr | ADDITIVE | TEXTMERGE | NOSHOW | FLAGS flagExpr=expr | PRETEXT pretext=expr)* NL textChunk ENDTEXT #textBlock 233 | | SHOW GETS #showCmd 234 | | HIDE WINDOW (ALL | SCREEN | args) #hideCmd 235 | | '=' expr #exprCmd 236 | | complexId #complexIdCmd 237 | ; 238 | 239 | queryCondition 240 | : scopeClause 241 | | FOR expr 242 | | WHILE expr 243 | | NOOPTIMIZE 244 | ; 245 | 246 | textChunk 247 | : (~ENDTEXT)* 248 | ; 249 | 250 | dllArgs 251 | : dllArg (',' dllArg)* 252 | ; 253 | 254 | dllArg 255 | : datatype '@'? identifier? 256 | ; 257 | 258 | tableField 259 | : identifier identifier arrayIndex? 260 | ; 261 | 262 | setCmd 263 | : setword=ALTERNATE (ON | OFF | TO specialExpr ADDITIVE?) 264 | | setword=(ASSERT | ASSERTS) (ON | OFF) 265 | | setword=BELL (ON | OFF | TO specialExpr) 266 | | setword=CENTURY (ON | OFF | TO (expr (ROLLOVER expr)?)?) 267 | | setword=CLASSLIB TO specialExpr (IN specialExpr)? (ALIAS specialExpr)? ADDITIVE? 268 | | setword=CLOCK (ON | OFF | STATUS | TO (expr ',' expr)?) 269 | | setword=COMPATIBLE (ON | OFF | DB4 | FOXPLUS) (PROMPT | NOPROMPT)? 270 | | setword=CONSOLE (ON | OFF) 271 | | setword=CURSOR (ON | OFF) 272 | | setword=DATABASE TO specialExpr 273 | | setword=DATASESSION TO expr 274 | | setword=DATE TO? identifier 275 | | setword=DEFAULT TO specialExpr 276 | | setword=(DELETE | DELETED) (ON | OFF) 277 | | setword=ESCAPE (ON | OFF) 278 | | setword=EXACT (ON | OFF) 279 | | setword=EXCLUSIVE (ON | OFF) 280 | | setword=FILTER TO (specialExpr (IN specialExpr)?)? 281 | | setword=HELP (ON | OFF | TO specialExpr? | COLLECTION specialExpr? | SYSTEM) 282 | | setword=INDEX TO specialExpr? 283 | | setword=LIBRARY TO (specialExpr ADDITIVE?) 284 | | setword=MEMOWIDTH TO expr 285 | | setword=MULTILOCKS (ON | OFF) 286 | | setword=NEAR (ON | OFF) 287 | | setword=NOTIFY CURSOR? (ON | OFF) 288 | | setword=ORDER TO (specialExpr | TAG? specialExpr (OF ofExpr=specialExpr)? (IN inExpr=specialExpr)? (ASCENDING | DESCENDING)?)? 289 | | setword=PRINTER (ON PROMPT? | OFF | TO (DEFAULT | NAME specialExpr | specialExpr ADDITIVE?)?) 290 | | setword=PROCEDURE TO (specialExpr (',' specialExpr)* ADDITIVE?)? 291 | | setword=REFRESH TO expr (',' expr)? 292 | | setword=RELATION TO expr INTO specialExpr (IN specialExpr)? ADDITIVE? 293 | | setword=SAFETY (ON | OFF) 294 | | setword=STATUS BAR? (ON | OFF) 295 | | setword=STEP (ON | OFF) 296 | | setword=SYSMENU (ON | OFF | TO (DEFAULT | expr)? | SAVE | NOSAVE) 297 | | setword=TABLEPROMPT (ON | OFF) 298 | | setword=TALK (ON | OFF) 299 | | setword=TYPEAHEAD TO expr 300 | | setword=UDFPARMS TO (VALUE | REFERENCE) 301 | | setword=UNIQUE (ON | OFF) 302 | ; 303 | 304 | declarationItem 305 | : (idAttr2 arrayIndex | idAttr asTypeOf?) 306 | ; 307 | 308 | asType 309 | : AS datatype 310 | ; 311 | 312 | asTypeOf 313 | : asType (OF specialExpr)? 314 | ; 315 | 316 | argsItem 317 | : ',' expr? 318 | ; 319 | 320 | args 321 | : expr argsItem* 322 | | argsItem+ 323 | ; 324 | 325 | specialArgs 326 | : specialExpr (',' specialExpr)* 327 | ; 328 | 329 | reference 330 | : '@' idAttr 331 | ; 332 | 333 | expr 334 | : '(' expr ')' #subExpr 335 | | op=('+'|'-') expr #unaryNegation 336 | | ('!'|NOT|OTHERNOT) expr #booleanNegation 337 | | expr ('*' '*'|'^') expr #power 338 | | expr op=('*'|'/') expr #multiplication 339 | | expr '%' expr #modulo 340 | | expr op=('+'|'-') expr #addition 341 | | expr op=('=='|NOTEQUALS|'='|'#'|'>'|GTEQ|'<'|LTEQ|'$') expr #comparison 342 | | expr andOp expr #booleanAnd 343 | | expr orOp expr #booleanOr 344 | | constant #constantExpr 345 | | CAST '(' expr asType ')' #castExpr 346 | | (PERIOD | idAttr ':' ':')? atom trailer? #atomExpr 347 | ; 348 | 349 | andOp 350 | : OTHERAND 351 | | AND 352 | ; 353 | 354 | orOp 355 | : OTHEROR 356 | | OR 357 | ; 358 | 359 | complexId 360 | : (PERIOD | idAttr ':' ':')? atom trailer 361 | | (PERIOD | idAttr ':' ':') atom trailer? 362 | ; 363 | 364 | atom 365 | : identifier 366 | | reference 367 | ; 368 | 369 | trailer 370 | : ('(' args? ')' | '[' args? ']') trailer? #funcCallTrailer 371 | | '.' identifier trailer? #identTrailer 372 | ; 373 | 374 | pathname 375 | : (identifier ':')? pathElement+? 376 | ; 377 | 378 | pathElement 379 | : identifier 380 | | NUMBER_LITERAL 381 | | BACKSLASH 382 | | ';' 383 | | '&' 384 | | '@' 385 | | '+' 386 | | '-' 387 | | '.' 388 | | '[' 389 | | ']' 390 | | '{' 391 | | '}' 392 | | '(' 393 | | ')' 394 | | '!' 395 | | '#' 396 | | '==' 397 | | NOTEQUALS 398 | | '%' 399 | | '=' 400 | | '^' 401 | | ',' 402 | | '$' 403 | | '_' 404 | ; 405 | 406 | specialExpr 407 | : expr 408 | | pathname 409 | ; 410 | 411 | constant 412 | : '$'? NUMBER_LITERAL #numberOrCurrency 413 | | ('.' (BOOLEANCHAR | NULL) '.' | NULL) #boolOrNull 414 | | '{' ( '/' '/' | ':' | '^' (NUMBER_LITERAL '-' NUMBER_LITERAL '-' NUMBER_LITERAL | NUMBER_LITERAL '/' NUMBER_LITERAL '/' NUMBER_LITERAL) (','? NUMBER_LITERAL (':' NUMBER_LITERAL (':' NUMBER_LITERAL)?)? identifier)? )? '}' #date 415 | | ('\'' (~(NL | '\''))* '\'' | '"' (~(NL | '"'))* '"' | '[' (~(NL | ']'))* ']') #string 416 | | BLOB_LITERAL #blob 417 | ; 418 | 419 | idAttr2 420 | : (startPeriod='.')? identifier ('.' identifier)* 421 | ; 422 | 423 | idAttr 424 | : PERIOD? identifier trailer? 425 | ; 426 | 427 | twoExpr 428 | : expr ',' expr 429 | ; 430 | 431 | arrayIndex 432 | : '(' (expr | twoExpr) ')' 433 | | '[' (expr | twoExpr) ']' 434 | ; 435 | 436 | datatype 437 | : idAttr 438 | ; 439 | 440 | scopeClause 441 | : ALL | NEXT expr | RECORD expr | REST 442 | ; 443 | 444 | identifier 445 | : ID 446 | | ACTIVATE 447 | | ADD 448 | | ADDITIVE 449 | | AFTER 450 | | ALIAS 451 | | ALL 452 | | ALTER 453 | | ALTERNATE 454 | | AND 455 | | APPEND 456 | | ARRAY 457 | | AS 458 | | ASCENDING 459 | | ASSERT 460 | | ASSERTS 461 | | AT 462 | | BAR 463 | | BEFORE 464 | | BELL 465 | | BLANK 466 | | BOOLEANCHAR 467 | | BOTTOM 468 | | BROWSE 469 | | BY 470 | | CANDIDATE 471 | | CASE 472 | | CAST 473 | | CATCH 474 | | CENTURY 475 | | CHDIR 476 | | CLASS 477 | | CLASSLIB 478 | | CLEAR 479 | | CLOCK 480 | | CLOSE 481 | | COLLECTION 482 | | COLOR 483 | | COLUMN 484 | | COMMAND 485 | | COMPACT 486 | | COMPATIBLE 487 | | COMPILE 488 | | CONSOLE 489 | | CONTINUE 490 | | COPY 491 | | COUNT 492 | | CREATE 493 | | CURSOR 494 | | DATABASE 495 | | DATASESSION 496 | | DATE 497 | | DBF 498 | | DEACTIVATE 499 | | DEBUG 500 | | DEBUGOUT 501 | | DECLARE 502 | | DEFAULT 503 | | DEFINE 504 | | DELETE 505 | | DELETED 506 | | DESCENDING 507 | | DIMENSION 508 | | DISTINCT 509 | | DLLS 510 | | DO 511 | | DOEVENTS 512 | | DROP 513 | | EACH 514 | | ELIF 515 | | ELSE 516 | | ENCRYPT 517 | | ENDCASE 518 | | ENDDEFINE 519 | | ENDDO 520 | | ENDFOR 521 | | ENDIF 522 | | ENDPROC 523 | | ENDSCAN 524 | | ENDTEXT 525 | | ENDTRY 526 | | ENDWITH 527 | | ERASE 528 | | ERROR 529 | | ESCAPE 530 | | EVENTS 531 | | EXACT 532 | | EXCEPT 533 | | EXCLUSIVE 534 | | EXTENDED 535 | | EXTERNAL 536 | | FIELDS 537 | | FILE 538 | | FILL 539 | | FILTER 540 | | FINALLY 541 | | FLAGS 542 | | FONT 543 | | FOR 544 | | FORCE 545 | | FORM 546 | | FOXOBJECT 547 | | FOXPLUS 548 | | FREE 549 | | FROM 550 | | GATHER 551 | | GETS 552 | | GOTO 553 | | HELP 554 | | HIDE 555 | | ICON 556 | | IF 557 | | IFDEF 558 | | IN 559 | | INCLUDE 560 | | INDEX 561 | | INDEXES 562 | | INSERT 563 | | INTO 564 | | JOIN 565 | | KEY 566 | | KEYBOARD 567 | | LABEL 568 | | LIBRARY 569 | | LIKE 570 | | LINE 571 | | LINKED 572 | | LIST 573 | | LOCATE 574 | | MACROS 575 | | MARGIN 576 | | MARK 577 | | MASTER 578 | | MAX 579 | | MEMO 580 | | MEMORY 581 | | MEMOWIDTH 582 | | MEMVAR 583 | | MENU 584 | | MENUS 585 | | MESSAGE 586 | | MIN 587 | | MKDIR 588 | | MODIFY 589 | | MULTILOCKS 590 | | NAME 591 | | NEAR 592 | | NEGOTIATE 593 | | NEXT 594 | | NOCLEAR 595 | | NOCONSOLE 596 | | NODEBUG 597 | | NOEJECT 598 | | NOMARGIN 599 | | NOMENU 600 | | NOOPTIMIZE 601 | | NOPROMPT 602 | | NORM 603 | | NOSAVE 604 | | NOSHOW 605 | | NOT 606 | | NOTE 607 | | NOTIFY 608 | | NOUPDATE 609 | | NOWAIT 610 | | NULL 611 | | NUMBER 612 | | OBJECT 613 | | OF 614 | | OFF 615 | | ON 616 | | OR 617 | | ORDER 618 | | OTHERWISE 619 | | PACK 620 | | PAD 621 | | PARAMETER 622 | | PLAIN 623 | | POP 624 | | POPUP 625 | | PRETEXT 626 | | PRINTER 627 | | PROCEDURE 628 | | PROGRAM 629 | | PROGRAMCONTROL 630 | | PROMPT 631 | | PUSH 632 | | READ 633 | | RECALL 634 | | RECORD 635 | | RECYCLE 636 | | REFERENCE 637 | | REFRESH 638 | | REINDEX 639 | | RELATION 640 | | RELATIVE 641 | | RELEASE 642 | | RENAME 643 | | REPLACE 644 | | REPORT 645 | | RESOURCES 646 | | REST 647 | | RESTORE 648 | | RETRY 649 | | RETURN 650 | | RMDIR 651 | | ROLLOVER 652 | | RUN 653 | | SAFETY 654 | | SAME 655 | | SAVE 656 | | SAY 657 | | SCAN 658 | | SCATTER 659 | | SCHEME 660 | | SCOPE 661 | | SCREEN 662 | | SEEK 663 | | SELECT 664 | | SELECTION 665 | | SET 666 | | SHADOW 667 | | SHARED 668 | | SHOW 669 | | SHUTDOWN 670 | | SIZE 671 | | SKIPKW 672 | | SORT 673 | | STATUS 674 | | STEP 675 | | STORE 676 | | STRUCTURE 677 | | STYLE 678 | | SUM 679 | | SYSMENU 680 | | SYSTEM 681 | | TABLE 682 | | TABLEPROMPT 683 | | TAG 684 | | TALK 685 | | TEXT 686 | | TEXTMERGE 687 | | THEN 688 | | THROW 689 | | TIMEOUT 690 | | TITLE 691 | | TO 692 | | TOP 693 | | TRY 694 | | TYPE 695 | | TYPEAHEAD 696 | | UDFPARMS 697 | | UNDEFINE 698 | | UNIQUE 699 | | UNLOCK 700 | | UPDATE 701 | | USE 702 | | VALUE 703 | | VALUES 704 | | WAIT 705 | | WHEN 706 | | WHERE 707 | | WHILE 708 | | WINDOW 709 | | WITH 710 | | ZAP 711 | | ZOOM 712 | ; 713 | 714 | NUMBER_LITERAL : (DIGIT* '.')? DIGIT+ (E [+-]? DIGIT*)? 715 | | DIGIT+ '.' 716 | | '0' X HEXDIGIT* 717 | ; 718 | 719 | BLOB_LITERAL : '0' H HEXDIGIT* ; 720 | 721 | SEMICOLON: ';'; 722 | AMPERSAND: '&'; 723 | COMMERCIALAT: '@'; 724 | ASTERISK: '*'; 725 | PLUS_SIGN: '+'; 726 | MINUS_SIGN: '-'; 727 | FORWARDSLASH: '/'; 728 | PERIOD: '.'; 729 | LEFTBRACKET: '['; 730 | RIGHTBRACKET: ']'; 731 | LEFTBRACE: '{'; 732 | RIGHTBRACE: '}'; 733 | LEFTPAREN: '('; 734 | RIGHTPAREN: ')'; 735 | BACKSLASH: '\\'; 736 | LESSTHAN: '<'; 737 | GREATERTHAN: '>'; 738 | EXCLAMATION: '!'; 739 | HASH: '#'; 740 | DOUBLEEQUALS: '=='; 741 | NOTEQUALS: ('!='|'<>'); 742 | GTEQ: ('>='|'=>'); 743 | LTEQ: ('<='|'=<'); 744 | MODULO: '%'; 745 | EQUALS: '='; 746 | CARAT: '^'; 747 | COMMA: ','; 748 | DOLLAR: '$'; 749 | COLON: ':'; 750 | QUESTION: '?'; 751 | DOUBLEQUOTE: '"'; 752 | SINGLEQUOTE: '\''; 753 | 754 | COMMENT: ('&&' (~'\n')* | ';' WS* '&&' (~'\n')* NL) -> channel(1); 755 | 756 | LINECONT : ';' WS* NL -> channel(2); 757 | 758 | MACROLINE : ~[\n&]* '&' ID (~'\n')*; 759 | 760 | ACTIVATE : A C T I (V (A (T E?)?)?)?; 761 | ADD : A D D; 762 | ADDITIVE : A D D I T I V E; 763 | AFTER : A F T E R; 764 | ALIAS : A L I A S; 765 | ALL : A L L; 766 | ALTER: A L T E R; 767 | ALTERNATE : A L T E R N A T E; 768 | AND : A N D; 769 | APPEND : A P P E (N D?)?; 770 | ARRAY : A R R A Y?; 771 | AS : A S; 772 | ASCENDING : A S C E N D I N G; 773 | ASSERT: A S S E (R T?)?; 774 | ASSERTS: A S S E R T S; 775 | AT : A T; 776 | //'AVER|AGE' 777 | BAR : B A R; 778 | BEFORE : B E F O R E; 779 | BELL : B E L L; 780 | BLANK : B L A N K?; 781 | BOOLEANCHAR : (F | N | T | Y); 782 | BOTTOM : B O T T O M | B O T T; 783 | //'BRIT|ISH' 784 | BROWSE: B R O W (S E?)?; 785 | //'BUIL|D' 786 | BY : B Y; 787 | //'CALC|ULATE' 788 | CANDIDATE : C A N D I D A T E; 789 | CASE : C A S E; 790 | CAST: C A S T; 791 | CATCH: C A T C H?; 792 | CENTURY : C E N T (U (R Y?)?)?; 793 | //'CHAN|GE' 794 | CHDIR: C D | C H D I R?; 795 | CLASS : C L A S S; 796 | CLASSLIB : C L A S S L I B; 797 | CLEAR : C L E A R?; 798 | CLOCK : C L O C K; 799 | CLOSE : C L O S E?; 800 | COLLECTION: C O L L E C T I O N; 801 | COLOR : C O L O R; 802 | COLUMN: C O L U M N; 803 | COMMAND: C O M M A N D; 804 | COMPACT : C O M P A C T; 805 | COMPATIBLE: C O M P A T I B L E; 806 | COMPILE: C O M P (I (L E?)?)?; 807 | CONSOLE: C O N S O L E; 808 | CONTINUE : C O N T (I (N (U E?)?)?)?; 809 | COPY : C O P Y; 810 | COUNT : C O U N T?; 811 | CREATE : C R E A (T E?)?; 812 | CURSOR : C U R S (O R?)?; 813 | DATABASE : D A T A (B A S E (S)?)?; 814 | DATASESSION: D A T A S E S S I O N; 815 | DATE : D A T E; 816 | DB4 : D B '4'; 817 | DBF : D B F; 818 | DEACTIVATE : D E A C (T (I (V (A (T E?)?)?)?)?)?; 819 | DEBUG: D E B U G?; 820 | DEBUGOUT: D E B U G O (U T?)?; 821 | DECLARE : D E C L (A (R E?)?)?; 822 | DEFAULT : D E F A U L T; 823 | DEFINE : D E F I (N E?)?; 824 | DELETE : D E L E (T E?)?; 825 | DELETED : D E L E T E D; 826 | DESCENDING : D E S C E N D I N G; 827 | //'DEVI|CE' 828 | DIMENSION : D I M E (N (S (I (O N?)?)?)?)?; 829 | //'DISP|LAY' 830 | DISTINCT : D I S T I N C T; 831 | DLLS : D L L S; 832 | DO : D O; 833 | DOEVENTS: D O E V (E (N (T S?)?)?)?; 834 | DROP: D R O P; 835 | EACH: E A C H; 836 | //'EJEC|T' 837 | ELIF : E L I F; 838 | ELSE : E L S E; 839 | ENCRYPT: E N C R Y P T; 840 | ENDCASE : E N D C (A (S E?)?)?; 841 | ENDDEFINE : E N D D E (F (I (N E?)?)?)?; 842 | ENDDO : E N D D O; 843 | ENDFOR : E N D F O R?; 844 | ENDIF : E N D I F?; 845 | ENDPROC : E N D P (R (O C?)?)? | E N D (F (U (N C?)?)?)?; 846 | //'ENDPRI|NTJOB' 847 | ENDSCAN : E N D S (C (A N?)?)?; 848 | ENDTEXT: E N D T (E (X T?)?)?; 849 | ENDTRY: E N D T R Y?; 850 | ENDWITH : E N D W (I (T H?)?)?; 851 | ERASE : E R A S E?; 852 | ERROR : E R R O R?; 853 | ESCAPE : E S C A P E; 854 | EVENTS : E V E N T S; 855 | EXACT : E X A C T?; 856 | EXCEPT: E X C E P T; 857 | EXCLUSIVE : E X C L (U S I V E)?; 858 | //'EXPO|RT' 859 | EXTENDED : E X T E N D E D; 860 | EXTERNAL: E X T E (R (N (A L?)?)?)?; 861 | FIELDS: F I E L D S; 862 | FILE : F I L E S?; 863 | FILL: F I L L; 864 | FILTER : F I L T (E R?)?; 865 | FINALLY: F I N A (L (L Y?)?)?; 866 | FLAGS: F L A G S; 867 | //'FLUS|H' 868 | FONT : F O N T; 869 | FOR : F O R; 870 | FORCE: F O R C E; 871 | FORM : F O R M; 872 | FOXOBJECT: F O X O B J E C T; 873 | FOXPLUS : F O X P L U S; 874 | FREE : F R E E; 875 | FROM : F R O M; 876 | GATHER: G A T H (E R?)?; 877 | //'GETE|XPR' 878 | GETS: G E T S; 879 | GOTO : G O (T O)?; 880 | HELP: H E L P; 881 | HIDE: H I D E; 882 | ICON: I C O N; 883 | IF : I F; 884 | IFDEF : I F D E F; 885 | //'IMPL|EMENTS' 886 | IN : I N; 887 | INCLUDE : I N C L U D E; 888 | INDEX : I N D E X; 889 | INDEXES : I N D E X E S; 890 | INSERT: I N S E (R T?)?; 891 | INTO : I N T O; 892 | JOIN: J O I N; 893 | KEY : K E Y; 894 | KEYBOARD : K E Y B (O (A (R D?)?)?)?; 895 | LABEL : L A B E L; 896 | LIBRARY : L I B R A R Y; 897 | LIKE: L I K E; 898 | LINE : L I N E; 899 | LINKED: L I N K E D; 900 | LIST: L I S T; 901 | LOCATE : L O C A (T E?)?; 902 | MACROS : M A C R O S; 903 | MARGIN : M A R G I N; 904 | MARK : M A R K; 905 | MASTER: M A S T E R; 906 | MAX: M A X; 907 | MEMO: M E M O; 908 | MEMORY: M E M O R Y; 909 | MEMOWIDTH : M E M O W I D T H; 910 | MEMVAR: M E M V A R; 911 | MENU : M E N U; 912 | MENUS: M E N U S; 913 | MESSAGE : M E S S (A (G E?)?)?; 914 | MIN: M I N; 915 | MKDIR : (M K D I R? | M D); 916 | MODIFY: M O D I (F Y?)?; 917 | //'MOUS|E' 918 | MULTILOCKS: M U L T I L O C K S; 919 | NAME : N A M E; 920 | NEAR : N E A R; 921 | NEGOTIATE : N E G O T I A T E; 922 | NEXT : N E X T; 923 | NOCLEAR : N O C L E A R; 924 | NOCONSOLE : N O C O N S O L E; 925 | NODEBUG: N O DEBUG; 926 | NOEJECT : N O E J E C T; 927 | NOMARGIN : N O M A R G I N; 928 | NOMENU : N O M E N U; 929 | NOOPTIMIZE : N O O P T I M I Z E; 930 | NOPROMPT : N O P R O M P T; 931 | NORM: N O R M; 932 | NOSAVE : N O S A V E; 933 | NOSHOW : N O S H O W; 934 | NOT : N O T; 935 | NOTE: N O T E; 936 | NOTIFY : N O T I F Y; 937 | NOUPDATE: N O U P D A T E; 938 | NOWAIT : N O W A I T; 939 | NULL : N U L L; 940 | NUMBER : N U M B E R; 941 | OBJECT : O B J E C T; 942 | OF : O F; 943 | OFF : O F F; 944 | ON : O N; 945 | OR : O R; 946 | ORDER : O R D E R; 947 | OTHERAND : '.' AND '.'; 948 | OTHERNOT : '.' NOT '.'; 949 | OTHEROR : '.' OR '.'; 950 | OTHERWISE : O T H E (R (W (I (S E?)?)?)?)?; 951 | PACK: P A C K; 952 | PAD : P A D; 953 | PARAMETER : L P A R (A (M (E (T (E (R S?)?)?)?)?)?)? | P A R A (M (E (T (E (R S?)?)?)?)?)?; 954 | //'PICT|URE' 955 | PLAIN : P L A I N; 956 | POP : P O P; 957 | POPUP : P O P U P S?; 958 | PRETEXT: P R E TEXT; 959 | PRINTER : P R I N (T (E R?)?)?; 960 | //'PRIN|TJOB' 961 | PROCEDURE : P R O C (E (D (U (R E?)?)?)?)? | F U N C (T (I (O N?)?)?)?; 962 | PROGRAM: P R O G R A M; 963 | PROGRAMCONTROL: (C A N C (E L?)? | S U S P (E (N D?)?)? | R E S U (M E?)? | Q U I T | E X I T | L O O P | N O D E (F (A (U (L T?)?)?)?)?); 964 | PROMPT : P R O M P T; 965 | PUSH : P U S H; 966 | READ : R E A D; 967 | RECALL : R E C A (L L?)?; 968 | RECORD : R E C O R D; 969 | RECYCLE : R E C Y C L E; 970 | REFERENCE: R E F E R E N C E; 971 | REFRESH : R E F R E S H; 972 | REINDEX: R E I N (D (E X?)?)?; 973 | RELATION: R E L A T I O N; 974 | RELATIVE : R E L A T I V E; 975 | RELEASE : R E L E (A (S E?)?)?; 976 | //'REMO|VE' 977 | RENAME : R E N A (M E?)?; 978 | REPLACE : R E P L (A (C E?)?)?; 979 | REPORT : R E P O R T; 980 | RESOURCES: R E S O U R C E S; 981 | REST : R E S T; 982 | RESTORE: R E S T (O (R E?)?)?; 983 | RETRY: R E T R Y?; 984 | RETURN : R E T U (R N?)?; 985 | RMDIR : (R M D I R? | R D); 986 | //'ROLL|BACK' 987 | ROLLOVER : R O L L O V E R; 988 | RUN: R U N; 989 | SAFETY: S A F E (T Y?)?; 990 | SAME : S A M E; 991 | SAVE : S A V E; 992 | SAY: S A Y; 993 | SCAN : S C A N; 994 | SCATTER: S C A T (T (E R?)?)?; 995 | SCHEME : S C H E M E; 996 | SCOPE: (P R O T (E (C (T (E D?)?)?)?)? | H I D D (E N?)? | P U B L (I C?)? | P R I V (A (T E?)?)? | L O C A L); 997 | SCREEN : S C R E (E N?)?; 998 | SEEK : S E E K; 999 | SELECT : S E L E (C T?)?; 1000 | SELECTION : S E L E C T I O N; 1001 | SET : S E T; 1002 | SHADOW : S H A D O W; 1003 | SHARED : S H A R E D?; 1004 | SHOW: S H O W; 1005 | SHUTDOWN : S H U T D O W N; 1006 | SIZE: S I Z E; 1007 | SKIPKW : S K I P; 1008 | SORT: S O R T; 1009 | STATUS : S T A T U S; 1010 | STEP : S T E P; 1011 | STORE : S T O R E?; 1012 | STRUCTURE : S T R U (C (T (U (R E?)?)?)?)?; 1013 | STYLE : S T Y L E; 1014 | SUM : S U M; 1015 | SYSMENU : S Y S M E N U; 1016 | SYSTEM: S Y S T E M; 1017 | TABLE : T A B L (E S?)?; 1018 | TABLEPROMPT: T A B L E P R O M P T; 1019 | TAG : T A G; 1020 | TALK: T A L K; 1021 | TEXT: T E X T; 1022 | TEXTMERGE: TEXT M E R G E; 1023 | THEN : T H E N; 1024 | THROW: T H R O W?; 1025 | TIMEOUT : T I M E (O (U T?)?)?; 1026 | TITLE: T I T L E; 1027 | TO : T O; 1028 | TOP : T O P; 1029 | //'TOTA|L' 1030 | TRY: T R Y; 1031 | TYPE: T Y P E; 1032 | TYPEAHEAD : T Y P E A H E A D; 1033 | UDFPARMS: U D F P A R M S; 1034 | UNDEFINE : U N D E F (I N E)?; 1035 | UNIQUE : U N I Q U E; 1036 | UNLOCK: U N L O (C K?)?; 1037 | UPDATE: U P D A (T E?)?; 1038 | USE : U S E; 1039 | VALUE: V A L U E; 1040 | VALUES: V A L U E S; 1041 | WAIT : W A I T; 1042 | WHEN : W H E N; 1043 | WHERE : W H E R E; 1044 | WHILE : W H I L E; 1045 | WINDOW : W I N D (O (W S?)?)?; 1046 | WITH : W I T H; 1047 | ZAP : Z A P; 1048 | ZOOM: Z O O M; 1049 | 1050 | //ID : [A-Za-z_] [a-zA-Z0-9_]*; 1051 | ID : ID_START ID_CONTINUE*; 1052 | 1053 | NL : '\n'; 1054 | 1055 | WS : [ \t\r] -> channel(1); 1056 | 1057 | UNMATCHED : . ; 1058 | 1059 | fragment A : [Aa]; 1060 | fragment B : [Bb]; 1061 | fragment C : [Cc]; 1062 | fragment D : [Dd]; 1063 | fragment E : [Ee]; 1064 | fragment F : [Ff]; 1065 | fragment G : [Gg]; 1066 | fragment H : [Hh]; 1067 | fragment I : [Ii]; 1068 | fragment J : [Jj]; 1069 | fragment K : [Kk]; 1070 | fragment L : [Ll]; 1071 | fragment M : [Mm]; 1072 | fragment N : [Nn]; 1073 | fragment O : [Oo]; 1074 | fragment P : [Pp]; 1075 | fragment Q : [Qq]; 1076 | fragment R : [Rr]; 1077 | fragment S : [Ss]; 1078 | fragment T : [Tt]; 1079 | fragment U : [Uu]; 1080 | fragment V : [Vv]; 1081 | fragment W : [Ww]; 1082 | fragment X : [Xx]; 1083 | fragment Y : [Yy]; 1084 | fragment Z : [Zz]; 1085 | fragment DIGIT : [0-9]; 1086 | fragment HEXDIGIT : [0-9A-Fa-f]; 1087 | 1088 | /// id_start ::= 1089 | fragment ID_START 1090 | : '_' 1091 | | [A-Z] 1092 | | [a-z] 1093 | | '\u00AA' 1094 | | '\u00B5' 1095 | | '\u00BA' 1096 | | [\u00C0-\u00D6] 1097 | | [\u00D8-\u00F6] 1098 | | [\u00F8-\u01BA] 1099 | | '\u01BB' 1100 | | [\u01BC-\u01BF] 1101 | | [\u01C0-\u01C3] 1102 | | [\u01C4-\u0241] 1103 | | [\u0250-\u02AF] 1104 | | [\u02B0-\u02C1] 1105 | | [\u02C6-\u02D1] 1106 | | [\u02E0-\u02E4] 1107 | | '\u02EE' 1108 | | '\u037A' 1109 | | '\u0386' 1110 | | [\u0388-\u038A] 1111 | | '\u038C' 1112 | | [\u038E-\u03A1] 1113 | | [\u03A3-\u03CE] 1114 | | [\u03D0-\u03F5] 1115 | | [\u03F7-\u0481] 1116 | | [\u048A-\u04CE] 1117 | | [\u04D0-\u04F9] 1118 | | [\u0500-\u050F] 1119 | | [\u0531-\u0556] 1120 | | '\u0559' 1121 | | [\u0561-\u0587] 1122 | | [\u05D0-\u05EA] 1123 | | [\u05F0-\u05F2] 1124 | | [\u0621-\u063A] 1125 | | '\u0640' 1126 | | [\u0641-\u064A] 1127 | | [\u066E-\u066F] 1128 | | [\u0671-\u06D3] 1129 | | '\u06D5' 1130 | | [\u06E5-\u06E6] 1131 | | [\u06EE-\u06EF] 1132 | | [\u06FA-\u06FC] 1133 | | '\u06FF' 1134 | | '\u0710' 1135 | | [\u0712-\u072F] 1136 | | [\u074D-\u076D] 1137 | | [\u0780-\u07A5] 1138 | | '\u07B1' 1139 | | [\u0904-\u0939] 1140 | | '\u093D' 1141 | | '\u0950' 1142 | | [\u0958-\u0961] 1143 | | '\u097D' 1144 | | [\u0985-\u098C] 1145 | | [\u098F-\u0990] 1146 | | [\u0993-\u09A8] 1147 | | [\u09AA-\u09B0] 1148 | | '\u09B2' 1149 | | [\u09B6-\u09B9] 1150 | | '\u09BD' 1151 | | '\u09CE' 1152 | | [\u09DC-\u09DD] 1153 | | [\u09DF-\u09E1] 1154 | | [\u09F0-\u09F1] 1155 | | [\u0A05-\u0A0A] 1156 | | [\u0A0F-\u0A10] 1157 | | [\u0A13-\u0A28] 1158 | | [\u0A2A-\u0A30] 1159 | | [\u0A32-\u0A33] 1160 | | [\u0A35-\u0A36] 1161 | | [\u0A38-\u0A39] 1162 | | [\u0A59-\u0A5C] 1163 | | '\u0A5E' 1164 | | [\u0A72-\u0A74] 1165 | | [\u0A85-\u0A8D] 1166 | | [\u0A8F-\u0A91] 1167 | | [\u0A93-\u0AA8] 1168 | | [\u0AAA-\u0AB0] 1169 | | [\u0AB2-\u0AB3] 1170 | | [\u0AB5-\u0AB9] 1171 | | '\u0ABD' 1172 | | '\u0AD0' 1173 | | [\u0AE0-\u0AE1] 1174 | | [\u0B05-\u0B0C] 1175 | | [\u0B0F-\u0B10] 1176 | | [\u0B13-\u0B28] 1177 | | [\u0B2A-\u0B30] 1178 | | [\u0B32-\u0B33] 1179 | | [\u0B35-\u0B39] 1180 | | '\u0B3D' 1181 | | [\u0B5C-\u0B5D] 1182 | | [\u0B5F-\u0B61] 1183 | | '\u0B71' 1184 | | '\u0B83' 1185 | | [\u0B85-\u0B8A] 1186 | | [\u0B8E-\u0B90] 1187 | | [\u0B92-\u0B95] 1188 | | [\u0B99-\u0B9A] 1189 | | '\u0B9C' 1190 | | [\u0B9E-\u0B9F] 1191 | | [\u0BA3-\u0BA4] 1192 | | [\u0BA8-\u0BAA] 1193 | | [\u0BAE-\u0BB9] 1194 | | [\u0C05-\u0C0C] 1195 | | [\u0C0E-\u0C10] 1196 | | [\u0C12-\u0C28] 1197 | | [\u0C2A-\u0C33] 1198 | | [\u0C35-\u0C39] 1199 | | [\u0C60-\u0C61] 1200 | | [\u0C85-\u0C8C] 1201 | | [\u0C8E-\u0C90] 1202 | | [\u0C92-\u0CA8] 1203 | | [\u0CAA-\u0CB3] 1204 | | [\u0CB5-\u0CB9] 1205 | | '\u0CBD' 1206 | | '\u0CDE' 1207 | | [\u0CE0-\u0CE1] 1208 | | [\u0D05-\u0D0C] 1209 | | [\u0D0E-\u0D10] 1210 | | [\u0D12-\u0D28] 1211 | | [\u0D2A-\u0D39] 1212 | | [\u0D60-\u0D61] 1213 | | [\u0D85-\u0D96] 1214 | | [\u0D9A-\u0DB1] 1215 | | [\u0DB3-\u0DBB] 1216 | | '\u0DBD' 1217 | | [\u0DC0-\u0DC6] 1218 | | [\u0E01-\u0E30] 1219 | | [\u0E32-\u0E33] 1220 | | [\u0E40-\u0E45] 1221 | | '\u0E46' 1222 | | [\u0E81-\u0E82] 1223 | | '\u0E84' 1224 | | [\u0E87-\u0E88] 1225 | | '\u0E8A' 1226 | | '\u0E8D' 1227 | | [\u0E94-\u0E97] 1228 | | [\u0E99-\u0E9F] 1229 | | [\u0EA1-\u0EA3] 1230 | | '\u0EA5' 1231 | | '\u0EA7' 1232 | | [\u0EAA-\u0EAB] 1233 | | [\u0EAD-\u0EB0] 1234 | | [\u0EB2-\u0EB3] 1235 | | '\u0EBD' 1236 | | [\u0EC0-\u0EC4] 1237 | | '\u0EC6' 1238 | | [\u0EDC-\u0EDD] 1239 | | '\u0F00' 1240 | | [\u0F40-\u0F47] 1241 | | [\u0F49-\u0F6A] 1242 | | [\u0F88-\u0F8B] 1243 | | [\u1000-\u1021] 1244 | | [\u1023-\u1027] 1245 | | [\u1029-\u102A] 1246 | | [\u1050-\u1055] 1247 | | [\u10A0-\u10C5] 1248 | | [\u10D0-\u10FA] 1249 | | '\u10FC' 1250 | | [\u1100-\u1159] 1251 | | [\u115F-\u11A2] 1252 | | [\u11A8-\u11F9] 1253 | | [\u1200-\u1248] 1254 | | [\u124A-\u124D] 1255 | | [\u1250-\u1256] 1256 | | '\u1258' 1257 | | [\u125A-\u125D] 1258 | | [\u1260-\u1288] 1259 | | [\u128A-\u128D] 1260 | | [\u1290-\u12B0] 1261 | | [\u12B2-\u12B5] 1262 | | [\u12B8-\u12BE] 1263 | | '\u12C0' 1264 | | [\u12C2-\u12C5] 1265 | | [\u12C8-\u12D6] 1266 | | [\u12D8-\u1310] 1267 | | [\u1312-\u1315] 1268 | | [\u1318-\u135A] 1269 | | [\u1380-\u138F] 1270 | | [\u13A0-\u13F4] 1271 | | [\u1401-\u166C] 1272 | | [\u166F-\u1676] 1273 | | [\u1681-\u169A] 1274 | | [\u16A0-\u16EA] 1275 | | [\u16EE-\u16F0] 1276 | | [\u1700-\u170C] 1277 | | [\u170E-\u1711] 1278 | | [\u1720-\u1731] 1279 | | [\u1740-\u1751] 1280 | | [\u1760-\u176C] 1281 | | [\u176E-\u1770] 1282 | | [\u1780-\u17B3] 1283 | | '\u17D7' 1284 | | '\u17DC' 1285 | | [\u1820-\u1842] 1286 | | '\u1843' 1287 | | [\u1844-\u1877] 1288 | | [\u1880-\u18A8] 1289 | | [\u1900-\u191C] 1290 | | [\u1950-\u196D] 1291 | | [\u1970-\u1974] 1292 | | [\u1980-\u19A9] 1293 | | [\u19C1-\u19C7] 1294 | | [\u1A00-\u1A16] 1295 | | [\u1D00-\u1D2B] 1296 | | [\u1D2C-\u1D61] 1297 | | [\u1D62-\u1D77] 1298 | | '\u1D78' 1299 | | [\u1D79-\u1D9A] 1300 | | [\u1D9B-\u1DBF] 1301 | | [\u1E00-\u1E9B] 1302 | | [\u1EA0-\u1EF9] 1303 | | [\u1F00-\u1F15] 1304 | | [\u1F18-\u1F1D] 1305 | | [\u1F20-\u1F45] 1306 | | [\u1F48-\u1F4D] 1307 | | [\u1F50-\u1F57] 1308 | | '\u1F59' 1309 | | '\u1F5B' 1310 | | '\u1F5D' 1311 | | [\u1F5F-\u1F7D] 1312 | | [\u1F80-\u1FB4] 1313 | | [\u1FB6-\u1FBC] 1314 | | '\u1FBE' 1315 | | [\u1FC2-\u1FC4] 1316 | | [\u1FC6-\u1FCC] 1317 | | [\u1FD0-\u1FD3] 1318 | | [\u1FD6-\u1FDB] 1319 | | [\u1FE0-\u1FEC] 1320 | | [\u1FF2-\u1FF4] 1321 | | [\u1FF6-\u1FFC] 1322 | | '\u2071' 1323 | | '\u207F' 1324 | | [\u2090-\u2094] 1325 | | '\u2102' 1326 | | '\u2107' 1327 | | [\u210A-\u2113] 1328 | | '\u2115' 1329 | | '\u2118' 1330 | | [\u2119-\u211D] 1331 | | '\u2124' 1332 | | '\u2126' 1333 | | '\u2128' 1334 | | [\u212A-\u212D] 1335 | | '\u212E' 1336 | | [\u212F-\u2131] 1337 | | [\u2133-\u2134] 1338 | | [\u2135-\u2138] 1339 | | '\u2139' 1340 | | [\u213C-\u213F] 1341 | | [\u2145-\u2149] 1342 | | [\u2160-\u2183] 1343 | | [\u2C00-\u2C2E] 1344 | | [\u2C30-\u2C5E] 1345 | | [\u2C80-\u2CE4] 1346 | | [\u2D00-\u2D25] 1347 | | [\u2D30-\u2D65] 1348 | | '\u2D6F' 1349 | | [\u2D80-\u2D96] 1350 | | [\u2DA0-\u2DA6] 1351 | | [\u2DA8-\u2DAE] 1352 | | [\u2DB0-\u2DB6] 1353 | | [\u2DB8-\u2DBE] 1354 | | [\u2DC0-\u2DC6] 1355 | | [\u2DC8-\u2DCE] 1356 | | [\u2DD0-\u2DD6] 1357 | | [\u2DD8-\u2DDE] 1358 | | '\u3005' 1359 | | '\u3006' 1360 | | '\u3007' 1361 | | [\u3021-\u3029] 1362 | | [\u3031-\u3035] 1363 | | [\u3038-\u303A] 1364 | | '\u303B' 1365 | | '\u303C' 1366 | | [\u3041-\u3096] 1367 | | [\u309B-\u309C] 1368 | | [\u309D-\u309E] 1369 | | '\u309F' 1370 | | [\u30A1-\u30FA] 1371 | | [\u30FC-\u30FE] 1372 | | '\u30FF' 1373 | | [\u3105-\u312C] 1374 | | [\u3131-\u318E] 1375 | | [\u31A0-\u31B7] 1376 | | [\u31F0-\u31FF] 1377 | | [\u3400-\u4DB5] 1378 | | [\u4E00-\u9FBB] 1379 | | [\uA000-\uA014] 1380 | | '\uA015' 1381 | | [\uA016-\uA48C] 1382 | | [\uA800-\uA801] 1383 | | [\uA803-\uA805] 1384 | | [\uA807-\uA80A] 1385 | | [\uA80C-\uA822] 1386 | | [\uAC00-\uD7A3] 1387 | | [\uF900-\uFA2D] 1388 | | [\uFA30-\uFA6A] 1389 | | [\uFA70-\uFAD9] 1390 | | [\uFB00-\uFB06] 1391 | | [\uFB13-\uFB17] 1392 | | '\uFB1D' 1393 | | [\uFB1F-\uFB28] 1394 | | [\uFB2A-\uFB36] 1395 | | [\uFB38-\uFB3C] 1396 | | '\uFB3E' 1397 | | [\uFB40-\uFB41] 1398 | | [\uFB43-\uFB44] 1399 | | [\uFB46-\uFBB1] 1400 | | [\uFBD3-\uFD3D] 1401 | | [\uFD50-\uFD8F] 1402 | | [\uFD92-\uFDC7] 1403 | | [\uFDF0-\uFDFB] 1404 | | [\uFE70-\uFE74] 1405 | | [\uFE76-\uFEFC] 1406 | | [\uFF21-\uFF3A] 1407 | | [\uFF41-\uFF5A] 1408 | | [\uFF66-\uFF6F] 1409 | | '\uFF70' 1410 | | [\uFF71-\uFF9D] 1411 | | [\uFF9E-\uFF9F] 1412 | | [\uFFA0-\uFFBE] 1413 | | [\uFFC2-\uFFC7] 1414 | | [\uFFCA-\uFFCF] 1415 | | [\uFFD2-\uFFD7] 1416 | | [\uFFDA-\uFFDC] 1417 | ; 1418 | 1419 | /// id_continue ::= 1420 | fragment ID_CONTINUE 1421 | : ID_START 1422 | | [0-9] 1423 | | [\u0300-\u036F] 1424 | | [\u0483-\u0486] 1425 | | [\u0591-\u05B9] 1426 | | [\u05BB-\u05BD] 1427 | | '\u05BF' 1428 | | [\u05C1-\u05C2] 1429 | | [\u05C4-\u05C5] 1430 | | '\u05C7' 1431 | | [\u0610-\u0615] 1432 | | [\u064B-\u065E] 1433 | | [\u0660-\u0669] 1434 | | '\u0670' 1435 | | [\u06D6-\u06DC] 1436 | | [\u06DF-\u06E4] 1437 | | [\u06E7-\u06E8] 1438 | | [\u06EA-\u06ED] 1439 | | [\u06F0-\u06F9] 1440 | | '\u0711' 1441 | | [\u0730-\u074A] 1442 | | [\u07A6-\u07B0] 1443 | | [\u0901-\u0902] 1444 | | '\u0903' 1445 | | '\u093C' 1446 | | [\u093E-\u0940] 1447 | | [\u0941-\u0948] 1448 | | [\u0949-\u094C] 1449 | | '\u094D' 1450 | | [\u0951-\u0954] 1451 | | [\u0962-\u0963] 1452 | | [\u0966-\u096F] 1453 | | '\u0981' 1454 | | [\u0982-\u0983] 1455 | | '\u09BC' 1456 | | [\u09BE-\u09C0] 1457 | | [\u09C1-\u09C4] 1458 | | [\u09C7-\u09C8] 1459 | | [\u09CB-\u09CC] 1460 | | '\u09CD' 1461 | | '\u09D7' 1462 | | [\u09E2-\u09E3] 1463 | | [\u09E6-\u09EF] 1464 | | [\u0A01-\u0A02] 1465 | | '\u0A03' 1466 | | '\u0A3C' 1467 | | [\u0A3E-\u0A40] 1468 | | [\u0A41-\u0A42] 1469 | | [\u0A47-\u0A48] 1470 | | [\u0A4B-\u0A4D] 1471 | | [\u0A66-\u0A6F] 1472 | | [\u0A70-\u0A71] 1473 | | [\u0A81-\u0A82] 1474 | | '\u0A83' 1475 | | '\u0ABC' 1476 | | [\u0ABE-\u0AC0] 1477 | | [\u0AC1-\u0AC5] 1478 | | [\u0AC7-\u0AC8] 1479 | | '\u0AC9' 1480 | | [\u0ACB-\u0ACC] 1481 | | '\u0ACD' 1482 | | [\u0AE2-\u0AE3] 1483 | | [\u0AE6-\u0AEF] 1484 | | '\u0B01' 1485 | | [\u0B02-\u0B03] 1486 | | '\u0B3C' 1487 | | '\u0B3E' 1488 | | '\u0B3F' 1489 | | '\u0B40' 1490 | | [\u0B41-\u0B43] 1491 | | [\u0B47-\u0B48] 1492 | | [\u0B4B-\u0B4C] 1493 | | '\u0B4D' 1494 | | '\u0B56' 1495 | | '\u0B57' 1496 | | [\u0B66-\u0B6F] 1497 | | '\u0B82' 1498 | | [\u0BBE-\u0BBF] 1499 | | '\u0BC0' 1500 | | [\u0BC1-\u0BC2] 1501 | | [\u0BC6-\u0BC8] 1502 | | [\u0BCA-\u0BCC] 1503 | | '\u0BCD' 1504 | | '\u0BD7' 1505 | | [\u0BE6-\u0BEF] 1506 | | [\u0C01-\u0C03] 1507 | | [\u0C3E-\u0C40] 1508 | | [\u0C41-\u0C44] 1509 | | [\u0C46-\u0C48] 1510 | | [\u0C4A-\u0C4D] 1511 | | [\u0C55-\u0C56] 1512 | | [\u0C66-\u0C6F] 1513 | | [\u0C82-\u0C83] 1514 | | '\u0CBC' 1515 | | '\u0CBE' 1516 | | '\u0CBF' 1517 | | [\u0CC0-\u0CC4] 1518 | | '\u0CC6' 1519 | | [\u0CC7-\u0CC8] 1520 | | [\u0CCA-\u0CCB] 1521 | | [\u0CCC-\u0CCD] 1522 | | [\u0CD5-\u0CD6] 1523 | | [\u0CE6-\u0CEF] 1524 | | [\u0D02-\u0D03] 1525 | | [\u0D3E-\u0D40] 1526 | | [\u0D41-\u0D43] 1527 | | [\u0D46-\u0D48] 1528 | | [\u0D4A-\u0D4C] 1529 | | '\u0D4D' 1530 | | '\u0D57' 1531 | | [\u0D66-\u0D6F] 1532 | | [\u0D82-\u0D83] 1533 | | '\u0DCA' 1534 | | [\u0DCF-\u0DD1] 1535 | | [\u0DD2-\u0DD4] 1536 | | '\u0DD6' 1537 | | [\u0DD8-\u0DDF] 1538 | | [\u0DF2-\u0DF3] 1539 | | '\u0E31' 1540 | | [\u0E34-\u0E3A] 1541 | | [\u0E47-\u0E4E] 1542 | | [\u0E50-\u0E59] 1543 | | '\u0EB1' 1544 | | [\u0EB4-\u0EB9] 1545 | | [\u0EBB-\u0EBC] 1546 | | [\u0EC8-\u0ECD] 1547 | | [\u0ED0-\u0ED9] 1548 | | [\u0F18-\u0F19] 1549 | | [\u0F20-\u0F29] 1550 | | '\u0F35' 1551 | | '\u0F37' 1552 | | '\u0F39' 1553 | | [\u0F3E-\u0F3F] 1554 | | [\u0F71-\u0F7E] 1555 | | '\u0F7F' 1556 | | [\u0F80-\u0F84] 1557 | | [\u0F86-\u0F87] 1558 | | [\u0F90-\u0F97] 1559 | | [\u0F99-\u0FBC] 1560 | | '\u0FC6' 1561 | | '\u102C' 1562 | | [\u102D-\u1030] 1563 | | '\u1031' 1564 | | '\u1032' 1565 | | [\u1036-\u1037] 1566 | | '\u1038' 1567 | | '\u1039' 1568 | | [\u1040-\u1049] 1569 | | [\u1056-\u1057] 1570 | | [\u1058-\u1059] 1571 | | '\u135F' 1572 | | [\u1369-\u1371] 1573 | | [\u1712-\u1714] 1574 | | [\u1732-\u1734] 1575 | | [\u1752-\u1753] 1576 | | [\u1772-\u1773] 1577 | | '\u17B6' 1578 | | [\u17B7-\u17BD] 1579 | | [\u17BE-\u17C5] 1580 | | '\u17C6' 1581 | | [\u17C7-\u17C8] 1582 | | [\u17C9-\u17D3] 1583 | | '\u17DD' 1584 | | [\u17E0-\u17E9] 1585 | | [\u180B-\u180D] 1586 | | [\u1810-\u1819] 1587 | | '\u18A9' 1588 | | [\u1920-\u1922] 1589 | | [\u1923-\u1926] 1590 | | [\u1927-\u1928] 1591 | | [\u1929-\u192B] 1592 | | [\u1930-\u1931] 1593 | | '\u1932' 1594 | | [\u1933-\u1938] 1595 | | [\u1939-\u193B] 1596 | | [\u1946-\u194F] 1597 | | [\u19B0-\u19C0] 1598 | | [\u19C8-\u19C9] 1599 | | [\u19D0-\u19D9] 1600 | | [\u1A17-\u1A18] 1601 | | [\u1A19-\u1A1B] 1602 | | [\u1DC0-\u1DC3] 1603 | | [\u203F-\u2040] 1604 | | '\u2054' 1605 | | [\u20D0-\u20DC] 1606 | | '\u20E1' 1607 | | [\u20E5-\u20EB] 1608 | | [\u302A-\u302F] 1609 | | [\u3099-\u309A] 1610 | | '\uA802' 1611 | | '\uA806' 1612 | | '\uA80B' 1613 | | [\uA823-\uA824] 1614 | | [\uA825-\uA826] 1615 | | '\uA827' 1616 | | '\uFB1E' 1617 | | [\uFE00-\uFE0F] 1618 | | [\uFE20-\uFE23] 1619 | | [\uFE33-\uFE34] 1620 | | [\uFE4D-\uFE4F] 1621 | | [\uFF10-\uFF19] 1622 | | '\uFF3F' 1623 | ; 1624 | --------------------------------------------------------------------------------