Skip to content
This repository has been archived by the owner. It is now read-only.
Permalink
master
Switch branches/tags
Go to file
 
 
Cannot retrieve contributors at this time
#!/usr/bin/env python
#
#
# sqliteboy.py
# Simple web-based management tool for SQLite database
# (with form, report, website, and many other features)
# (c) Noprianto <nopri.anto@icloud.com>
# 2012-2019
# License: GPL
#
# SQLiteBoy is an independent product, developed separately from the
# SQLite core library, which is maintained by SQLite.org.
# Neither noprianto.com nor SQLite.org take any responsibility for the
# work of the other.
#
#
# Please read README.rst
#
# I apologize for:
# - the lack of documentation in source code
# - obsolete codes
# - non-descriptive variable/function names
# - using python builtins as variable (input, type, ...) :(
# - bare except:
# - PEP8 violations :)
#
#
#----------------------------------------------------------------------#
# APPLICATION #
#----------------------------------------------------------------------#
NAME = 'sqliteboy'
APP_DESC = 'Simple web-based management tool for SQLite database (with form, report, website, and many other features)'
VERSION = '1.83'
WSITE = 'https://github.com/nopri/sqliteboy'
TITLE = NAME + ' ' + VERSION
TITLE_DEFAULT = NAME
DBN = 'sqlite'
CHECK_SAME_THREAD = False
FORM_TBL = '_sqliteboy_'
FORM_URL_INIT = '/sqliteboy/init'
FORM_FIELDS = ['a', 'b', 'c', 'd', 'e', 'f', 'g']
FORM_FIELDS_R1 = ['rowid', 'rowtime']
FORM_FIELD_TYPE = 'text'
FORM_SPLIT = '.'
FORM_VALID = None
URL_VALID = None
PATH_VALID = None
PRECISION = 4
SORT = ('asc', 'desc')
VSORT = ('&#9650;', '&#9660;')
ROWID = 'ROWID'
DEFAULT_PORT = 11738
DEFAULT_ADDR = '0.0.0.0'
DEFAULT_LANG = 'default'
DEFAULT_TABLE = 'sqlite_master'
DEFAULT_LIMIT = 50
DEFAULT_T_BASE = '%s.html' %(NAME)
DEFAULT_T_BASE_HEADER = '$def with (data, content)'
DEFAULT_PY_HANDLER = '%s_user' %(NAME)
DEFAULT_SSL_CERTIFICATE = '%s.cert' %(NAME)
DEFAULT_SSL_PRIVATE_KEY = '%s.key' %(NAME)
DEFAULT_PY_FORM = 'form_'
DEFAULT_PY_REPORT = 'report_'
DEFAULT_PY_WEB = 'web_'
DEFAULT_PY_WEB_POST = 'post_'
DEFAULT_WEBSITE_POST = ''
DEFAULT_ADMIN_USER = 'admin'
DEFAULT_ADMIN_PASSWORD = DEFAULT_ADMIN_USER
DEFAULT_HOSTS_ALLOWED = ['127.0.0.1']
DEFAULT_TEXTAREA_COLS = 40
DEFAULT_TEXTAREA_ROWS = 15
DEFAULT_ERROR_INT = -1
DEFAULT_ERROR_STR = ''
DEFAULT_WIN_EXE = '%s.exe' %(NAME)
DEFAULT_WIN_EXE_VERSION = '%s.version' %(DEFAULT_WIN_EXE)
DEFAULT_WIN_MD5 = '%s.md5' %(DEFAULT_WIN_EXE)
DEFAULT_FAVICON = '%s.ico' %(NAME)
DEFAULT_SPEC = '%s.spec' %(NAME)
DEFAULT_VERSION = '%s.version' %(NAME)
DEFAULT_WEBPY_STATIC = ['static']
DEFAULT_QUERY_EXPORT = 'query.csv'
DEFAULT_VAR_MAX = 3
DEFAULT_INDEX = '/index'
DEFAULT_HOME = '/'
DEFAULT_CUSTOM = '/(.*)'
DEFAULT_LANG_WEB = 'en'
DEFAULT_CHARSET_WEB = 'utf-8'
APPLICATION_TITLE_MAX = 32
BROWSE_LIMIT_ALL = [10, 25, DEFAULT_LIMIT, 100, 250, 500, 1000]
SEQUENCE_TABLE = 'sqlite_sequence'
HOST_LOCAL = '0'
HOST_ALL = '1'
HOST_CUSTOM = '2'
DFLT_VALUE = 'dflt_value'
MARK_CURRENT = '&#9658' #unused
NAME_SELECT = 'select'
EXCLUDE_TABLE = []
SIZE_KB = 1024
SIZE_MB = 1024 * SIZE_KB
SIZE_GB = 1024 * SIZE_MB
BLOB_TYPE = ['blob']
TEXT_TYPE = ['text', 'clob']
NOQUOTE_TYPE = ['integer', 'real']
BLOB_VAR = 'rowid'
BLOB_COLUMN = 'column'
BLOB_CTYPE = 'application/octet-stream'
MODE_INSERT = 'insert'
SKT_ROWID = 'rowid'
SKT_M_INSERT = 'insert'
SKT_M_EDIT = 'edit'
SKT_P_EDIT = 'position'
SKT_M_COLUMN = 'column'
SKT_M_RENAME = 'rename'
SKT_M_DROP = 'drop'
SKT_M_COPY = 'copy'
SKT_M_EMPTY = 'empty'
SKT_M_IMPORT = 'import'
SKQ = 'query'
SKV = 'vacuum'
SK_CREATE = 'create'
SK_LOGIN = 'login'
SK_PASSWORD = 'password'
SK_NOTES = 'notes'
SK_FILES = 'files'
SK_PAGES = 'pages'
SK_CALCULATOR = 'calculator'
SK_INTERPRETER = 'interpreter'
SK_USERS = 'users'
SK_HOSTS = 'hosts'
SK_SYSTEM = 'system'
SK_SCRIPTS = 'scripts'
SK_SCRIPT = 'script'
SK_PROFILE = 'profile'
SK_SCHEMA = 'schema'
SK_WEBSITE = 'website'
SKF_CREATE = 'form.create'
SKF_RUN = 'form.run'
SKR_CREATE = 'report.create'
SKR_RUN = 'report.run'
COLUMN_TYPES = (
('integer primary key', 0),
('integer primary key autoincrement', 0),
('integer', 1),
('real', 1),
('char', 1),
('varchar', 1),
('text', 1),
('blob', 1),
('null', 1),
) #type, used in add column
COLUMN_CONVERT = {
'integer': 'sqliteboy_as_integer',
'real': 'sqliteboy_as_float',
'char': 'sqliteboy_strs',
'varchar': 'sqliteboy_strs',
'text': 'sqliteboy_strs',
'blob': 'sqliteboy_strs',
}
COLUMN_CONVERT_DEFAULT = 'sqliteboy_strs'
MAX_COLUMN_ADD = 3
CUSTOM_RT = {
'query': 4,
'calculator': 3,
'interpreter': 3,
} #command, rt
PK_SYM = '*'
URL_README = ('/sqliteboy/readme', 'sqliteboy_readme', 'README.rst')
URL_SOURCE = ('/sqliteboy/source', 'sqliteboy_source', 'sqliteboy.py')
PROTECTED_USERS = ['admin']
FORM_ALL = ''
FORM_KEY_TITLE = 'title'
FORM_KEY_INFO = 'info'
FORM_KEY_DATA = 'data'
FORM_KEY_DATA_TABLE = 'table'
FORM_KEY_DATA_COLUMN = 'column'
FORM_KEY_DATA_LABEL = 'label'
FORM_KEY_DATA_REFERENCE = 'reference'
FORM_KEY_DATA_DEFAULT = 'default'
FORM_KEY_DATA_REQUIRED = 'required'
FORM_KEY_DATA_READONLY = 'readonly'
FORM_KEY_DATA_CONSTRAINT = 'constraint'
FORM_KEY_SECURITY = 'security'
FORM_KEY_SECURITY_RUN = 'run'
FORM_KEY_DATA_ONSAVE = 'onsave'
FORM_KEY_SUB = 'sub'
FORM_KEY_MESSAGE = 'message'
FORM_KEY_SQL2 = 'sql2'
FORM_KEY_SQL0 = 'sql0'
FORM_KEY_INSERT = 'insert'
FORM_KEY_CONFIRM = 'confirm'
FORM_KEY_FOCUS = 'focus'
FORM_KEY_LINK = 'link'
FORM_KEY_RESULT = 'result'
FORM_REQ = (FORM_KEY_DATA,)
FORM_REQ_X = (2,) #parsed index
FORM_REQ_DATA = (FORM_KEY_DATA_TABLE,
FORM_KEY_DATA_COLUMN,
)
FORM_REFERENCE_SQL_0 = 'a'
FORM_REFERENCE_SQL_1 = 'b'
FORM_ONSAVE_SQL_VALUE = 'value'
FORM_ONSAVE_SQL_RET = 'onsave'
FORM_SUB_ROWS_DEFAULT = [5, 1]#rows, required rows
FORM_MESSAGE_LEN = 3
FORM_MESSAGE_VAR_RESULT = 'result'
FORM_MESSAGE_VAR_PYTHON_HANDLER = 'python_handler'
FORM_MESSAGE_RESULT_LEN = 2
FORM_INSERT_DEFAULT = 1
FORM_DEFAULT_SQL_RET = 'a'
#
REPORT_KEY_DATA_TYPES = ['integer']
REPORT_ALL = FORM_ALL
REPORT_KEY_TITLE = FORM_KEY_TITLE
REPORT_KEY_INFO = FORM_KEY_INFO
REPORT_KEY_DATA = FORM_KEY_DATA
REPORT_KEY_DATA_KEY = 'key'
REPORT_KEY_DATA_LABEL = FORM_KEY_DATA_LABEL
REPORT_KEY_DATA_REFERENCE = FORM_KEY_DATA_REFERENCE
REPORT_KEY_DATA_DEFAULT = FORM_KEY_DATA_DEFAULT
REPORT_KEY_DATA_REQUIRED = FORM_KEY_DATA_REQUIRED
REPORT_KEY_DATA_READONLY = FORM_KEY_DATA_READONLY
REPORT_KEY_DATA_CONSTRAINT = FORM_KEY_DATA_CONSTRAINT
REPORT_KEY_DATA_TYPE = 'type'
REPORT_KEY_ALIGN = 'align'
REPORT_KEY_MESSAGE = 'message'
REPORT_KEY_SECURITY = FORM_KEY_SECURITY
REPORT_KEY_SECURITY_RUN = FORM_KEY_SECURITY_RUN
REPORT_KEY_SQL = 'sql'
REPORT_KEY_HEADER = 'header'
REPORT_KEY_HEADERS = 'headers'
REPORT_KEY_FOOTERS = 'footers'
REPORT_KEY_PAPER = 'paper'
REPORT_KEY_MARGINS = 'margins'
REPORT_KEY_CONFIRM = 'confirm'
REPORT_KEY_FOCUS = 'focus'
REPORT_KEY_LINK = 'link'
REPORT_REQ = (REPORT_KEY_DATA,
REPORT_KEY_SQL,
)
REPORT_REQ_X = (3,) #parsed index
REPORT_REQ_DATA = (REPORT_KEY_DATA_KEY,)
REPORT_REFERENCE_SQL_0 = 'a'
REPORT_REFERENCE_SQL_1 = 'b'
REPORT_MESSAGE_LEN = 3
REPORT_MESSAGE_VAR_RESULT = 'result'
REPORT_HEADERS_CELL_LEN = 3
REPORT_FOOTERS_CELL_LEN = 3
REPORT_HEADERS_CELL_TYPES = [
(str, unicode, ),
(str, unicode, int, ),
(dict, ),
]
REPORT_FOOTERS_CELL_TYPES = REPORT_HEADERS_CELL_TYPES
REPORT_CELL_TYPE_TEXT = ''
REPORT_CELL_TYPE_FILES_IMAGE = 'files.image'
REPORT_CELL_TYPE_SQL = 'sql'
REPORT_CELL_TYPE_SQL_RESULT = REPORT_REFERENCE_SQL_0
REPORT_RESULT_ROW_COUNT = 'result_row_count'
REPORT_RESULT_MESSAGE = 'result_message'
REPORT_FORMAT_DEFAULT = ''
REPORT_FORMAT_CSV = 'csv'
REPORT_FORMAT_PDF = 'pdf'
REPORT_FORMAT_ALL = [
REPORT_FORMAT_DEFAULT,
REPORT_FORMAT_CSV,
REPORT_FORMAT_PDF,
]
REPORT_ALIGN_ALL = [
0, #left
1, #center
2, #right
3, #justify
]
REPORT_ALIGN_DEFAULT = 0
REFERENCE_FLAG_PASSWORD = 2
FAVICON_WIDTH = 16
FAVICON_HEIGHT = 16
PYTIME_FORMAT = '%Y-%m-%d %H:%M:%S'
PYTIME_DATE_FORMAT = '%Y-%m-%d'
PYTIME_TIME_FORMAT = '%H:%M:%S'
PYTIME_FORMAT_BACKUP = '%Y-%m-%d_%H-%M-%S'
REGEX_EMAIL = r'^[\w\.\+-]+@[\w\.-]+\.[a-zA-Z]+$'
DAYS_IN_YEAR = 365.2425
DAYS_IN_MONTH_AVERAGE = round( (DAYS_IN_YEAR / float(12)), 2)
DAYS_IN_MONTH_30 = 30
DAYS_IN_MONTH_31 = 31
SECONDS_IN_HOUR = 60 * 60
SECONDS_IN_DAY = 60 * 60 * 24
CSV_SUFFIX = '.csv'
CSV_CTYPE = 'text/csv'
BACKUP_BUFFER = 10 * SIZE_KB
FILES_MAX_NUMBER = 10
FILES_MAX_SIZE = 1 * SIZE_MB
SCRIPTS_MAX_SIZE = 32 * SIZE_KB
SYSTEM_CONFIG_MAXSPLIT = 3
SYSTEM_CONFIG = (
(
'x_application',
'x_application_title',
'application.title.',
'application.title..%s' %(''),
'',
'striphtml',
0,
),
(
'x_files',
'x_max_files_number',
'files.max_number.',
'files.max_number..%s' %(FILES_MAX_NUMBER),
FILES_MAX_NUMBER,
int,
0,
),
(
'x_files',
'x_max_file_size',
'files.max_size.',
'files.max_size..%s' %(FILES_MAX_SIZE),
FILES_MAX_SIZE,
int,
0,
),
(
'x_scripts',
'x_max_script_size',
'scripts.max_size.',
'scripts.max_size..%s' %(SCRIPTS_MAX_SIZE),
SCRIPTS_MAX_SIZE,
int,
0,
),
(
'x_log',
'x_log_access',
'log.access.',
'log.access..%s' %(''),
'',
'log_init',
0,
),
(
'x_log',
'x_log_override',
'log.override.',
'log.override..%s' %(''),
'',
str,
1,
),
(
'x_users',
'x_user_defined_profile_ref',
'users.profile.',
'users.profile..%s' %(''),
'',
str,
1,
),
(
'x_messages',
'x_messages_all',
'messages.all.',
'messages.all..%s' %(''),
'',
'striphtml',
1,
),
(
'x_link',
'x_link_login',
'link.login.',
'link.login..%s' %(''),
'',
str,
1,
),
(
'x_website',
'x_website_notfound',
'website.notfound.',
'website.notfound..%s' %(''),
'',
'notfound_url_check',
0,
),
)
NOTFOUND_CHECK = [
'/fs',
]
REGEX_PAGE = (
(
r'~([^~]+)~',
r'<em>\1</em>',
True,
'd_page_emphasis',
'',
),
(
r'\*([^\*]+)\*',
r'<strong>\1</strong>',
True,
'd_page_strong',
'',
),
(
r'_([^_]+)_',
r'<u>\1</u>',
True,
'd_page_underline',
'',
),
(
r'\[([^\|]+)\|(\S+)\]',
r'<a href="\2">\1</a>',
True,
'd_page_link',
'',
),
(
r'\[form:(\S+)\]',
r'<a href="/form/run/\1">\1</a>',
True,
'd_page_form',
'canformrun',
),
(
r'\[FORM:(\S+)\]',
r'<a href="/form/run/\1">\1</a><br>',
True,
'd_page_form_new_line',
'canformrun',
),
(
r'\[report:(\S+)\]',
r'<a href="/report/run/\1">\1</a>',
True,
'd_page_report',
'canreportrun',
),
(
r'\[REPORT:(\S+)\]',
r'<a href="/report/run/\1">\1</a><br>',
True,
'd_page_report_new_line',
'canreportrun',
),
(
r'\[-\]',
r'<hr>',
True,
'd_page_hr',
'',
),
)
SAMPLE_PAGE = [x[3] for x in REGEX_PAGE]
CALCULATOR_MAX_INPUT = 36
CALCULATOR_ALLOWED = ''
URL_MAX_INPUT = CALCULATOR_MAX_INPUT
PATH_MAX_INPUT = 128
PLAIN_CTYPE = 'text/plain'
SCRIPT_KEY_NAME = 'name'
SCRIPT_KEY_INFO = 'info'
SCRIPT_KEY_AUTHOR = 'author'
SCRIPT_KEY_LICENSE = 'license'
SCRIPT_KEY_TABLES = 'tables'
SCRIPT_KEY_FORMS = 'forms'
SCRIPT_KEY_REPORTS = 'reports'
SCRIPT_KEY_PROFILES = 'profiles'
SCRIPT_REQ = (
SCRIPT_KEY_NAME,
SCRIPT_KEY_TABLES,
SCRIPT_KEY_FORMS,
SCRIPT_KEY_REPORTS,
)
SCRIPT_TABLE_COLUMN_LEN = 3
SCRIPT_TABLE_ERROR = -1
SCRIPT_TABLE_COLUMN_CONFLICT = -2
SCRIPT_TABLE_OK = 0
SCRIPT_TABLE_EXISTS = 1
SCRIPT_FORM_ERROR = -1
SCRIPT_FORM_OK = 0
SCRIPT_FORM_EXISTS = -2
SCRIPT_REPORT_ERROR = -1
SCRIPT_REPORT_OK = 0
SCRIPT_REPORT_EXISTS = -2
SCRIPT_VALID_COLUMN_FLAG = (
([], 0, ''), #none
([], 1, ''), #primary key
(['integer'], 2, 'primary key autoincrement'),
)
JSON_INDENT = 4
COPY_TARGET_EXCLUDE = [
FORM_TBL,
DEFAULT_TABLE,
SEQUENCE_TABLE,
]
EMPTY_EXCLUDE = [
FORM_TBL,
DEFAULT_TABLE,
SEQUENCE_TABLE,
]
IMPORT_EXCLUDE = [
FORM_TBL,
DEFAULT_TABLE,
SEQUENCE_TABLE,
]
TEMPLATE_SELECT_EXCLUDE = [
FORM_TBL,
DEFAULT_TABLE,
SEQUENCE_TABLE,
]
PRAGMA_FREELIST_COUNT = 'freelist_count'
SERVER_COMMAND_SEPARATOR = '-'
SERVER_COMMAND_ALL = {
'generate_favicon': 'scmd_favicon',
'generate_pyinstaller': 'scmd_pyinstaller',
'generate_build': 'scmd_build',
'generate_version': 'scmd_version',
'enable_extended': 'scmd_extended',
'enable_extended_allow_all': 'scmd_extended_allow_all',
'reset_admin_password': 'scmd_reset_admin_password',
'disable_log': 'scmd_disable_log',
'restore_log': 'scmd_restore_log',
}
SHORTCUT_TYPE_FORM = 'form'
SHORTCUT_TYPE_REPORT = 'report'
SHORTCUT_ALL = [
SHORTCUT_TYPE_FORM,
SHORTCUT_TYPE_REPORT,
]
PRINT_DATA_KEY = 'output_printer'
PRINT_DATA_VALUE = 1
IMPORT_ERROR_CODE = 255
GENERAL_ERROR_CODE = 254
PYINSTALLER_SPEC = '''
# $title $command
# $datetime
a = Analysis([r'$source_path'])
a.datas += [
('$source', r'$source_path', 'DATA'),
('$readme', r'$readme_path', 'DATA'),
]
pyz = PYZ(a.pure)
exe = EXE(
pyz,
a.scripts,
a.binaries,
a.datas,
name=r'$output',
icon=r'$icon',
console=True,
debug=False
)
#
import sys
import os
try:
from hashlib import md5
except ImportError:
from md5 import md5
#
content_version = """
$title $command
$datetime
"""
file_version = open(r'$output_version', 'w')
file_version.write(content_version)
file_version.close()
#
content = open(r'$output', 'rb').read()
content_md5 = md5(content).hexdigest()
content_lines = [
'%s %s' %(content_md5, r'$output'),
]
file_md5 = open(r'$output_md5', 'w')
for i in content_lines:
line = '%s' %(i)
file_md5.write(line)
file_md5.close()
#
try:
content_check = open(r'$output_md5', 'r').readlines()
content_check = [x.strip() for x in content_check if not x.startswith('#')]
content_check = [x for x in content_check if x]
content_check_md5 = content_check[0].split()[0].strip()
if content_check_md5 == content_md5:
sys.stdout.write('OK' + os.linesep)
except:
pass
'''
VERSION_SPEC = '''
$title $command
$datetime
'''
PROFILE_STYLE_ADD_ALIGN = '''
.pre
{
white-space : pre-wrap;
white-space : -moz-pre-wrap;
word-wrap : break-word;
}
.left
{
text-align : left;
}
.center
{
text-align : center;
}
.right
{
text-align : right;
}
.justify
{
text-align : justify;
}
'''
PROFILE_STYLE_PRINT = '''
*
{
font-family : Courier;
font-size : 12pt;
}
table
{
border-collapse : collapse;
width : 100%;
}
td
{
border : 1px solid #000000;
padding : 2px;
}
th
{
border : 1px solid #000000;
padding : 2px;
}
select
{
width : 95%;
}
.main_menu
{
display : none;
}
.messages
{
display : none;
}
''' + PROFILE_STYLE_ADD_ALIGN
PROFILE_ITEM_STYLE = [
[
PROFILE_STYLE_PRINT,
'''
*
{
font-family : Courier;
font-size : 12pt;
}
table
{
border-collapse : collapse;
width : 100%;
}
td
{
border : 1px solid #808080;
padding : 2px;
}
th
{
background-color: #406080;
border : 1px solid #808080;
padding : 2px;
color : #ffffff;
}
th a
{
color : #ffffff;
text-decoration : none;
}
tr:nth-child(odd)
{
background-color: #cccccc;
}
tr:nth-child(even)
{
background-color: #ffffff;
}
select
{
width : 95%;
}
.main_menu
{
}
a
{
color : #406080;
}
.messages
{
padding : 2px;
background-color: #cccccc;
border : 1px solid #808080;
}
table:not(.nohover) tr:hover
{
background-color: #ffffe0;
}
''' + PROFILE_STYLE_ADD_ALIGN
],
[
PROFILE_STYLE_PRINT,
'''
*
{
font-family : Courier;
font-size : 12pt;
}
table
{
border-collapse : collapse;
width : 100%;
}
td
{
border : 1px solid #ffcc66;
padding : 2px;
}
th
{
background-color: #996600;
border : 1px solid #ffcc66;
padding : 2px;
color : #ffffff;
}
th a
{
color : #ffffff;
text-decoration : none;
}
tr:nth-child(odd)
{
background-color: #f3e3c3;
}
tr:nth-child(even)
{
background-color: #ffffff;
}
select
{
width : 95%;
}
.main_menu
{
}
input, select, textarea
{
background-color: #ffffff;
color : #996600;
border : 1px solid #ffcc66;
margin : 2px;
}
a
{
color : #996600;
}
.messages
{
padding : 2px;
background-color: #f3e3c3;
border : 1px solid #ffcc66;
}
table:not(.nohover) tr:hover
{
background-color: #ffffe0;
}
''' + PROFILE_STYLE_ADD_ALIGN
],
[
PROFILE_STYLE_PRINT,
'''
*
{
font-family : Courier;
font-size : 12pt;
}
table
{
border-collapse : collapse;
width : 100%;
}
td
{
border : 1px solid #4169e1;
padding : 2px;
}
th
{
background-color: #00008b;
border : 1px solid #4169e1;
padding : 2px;
color : #ffffff;
}
th a
{
color : #ffffff;
text-decoration : none;
}
tr:nth-child(odd)
{
background-color: #b0e0e6;
}
tr:nth-child(even)
{
background-color: #ffffff;
}
select
{
width : 95%;
}
.main_menu
{
}
a
{
color : #00008b;
}
.messages
{
padding : 2px;
background-color: #b0e0e6;
border : 1px solid #4169e1;
}
table:not(.nohover) tr:hover
{
background-color: #ffffe0;
}
''' + PROFILE_STYLE_ADD_ALIGN
],
]
PROFILE_ALL = [
[
'style',
'x_style',
[
[0, 'A'],
[1, 'B'],
[2, 'C'],
],
2,
'pr_style',
int,
0,
],
[
'first_name',
'x_first_name',
[
],
'',
'pr_user',
str,
0,
],
[
'last_name',
'x_last_name',
[
],
'',
'pr_user',
str,
0,
],
[
'email',
'x_email',
[
],
'',
'pr_user',
str,
0,
],
[
'website',
'x_website',
[
],
'',
'pr_user',
str,
0,
],
]
PROFILE_USER_DEFINED_LEN = 4
PROFILE_USER_DEFINED_HANDLER = 'pr_user'
PROFILE_USER_DEFINED_TYPE = str
PROFILE_USER_DEFINED_LEVEL = 1
ENV_VAR_MAX = DEFAULT_VAR_MAX
QUERY_STRING_MAX = 1 * SIZE_KB
PDF_CTYPE = 'application/pdf'
PDF_SUFFIX = '.pdf'
RANDOM_SIMPLE_MIN = 10
RANDOM_SIMPLE_MAX = 100
LOG_TABLE = '%s_log' %(NAME)
LINKS_LOGIN_MAIN = 'login.main'
LINKS_LOGIN_EXTRA = 'login.extra'
HEADER_CONTENT = 'Content-Type'
DEFAULT_CONTENT = 'text/html'
PREFIX_REDIR_HTTP = 'http://'
PREFIX_REDIR_HTTPS = 'https://'
PREFIX_BLOG = 'blog:'
BLOG_SEPARATOR = ':'
WSGI_DEFAULT_DATABASE = 'data.db'
SESSION_DEFAULT_DIR = '%s-session' %(NAME)
#----------------------------------------------------------------------#
# MODULE #
#----------------------------------------------------------------------#
import sys
import os
if getattr(sys, 'frozen', None):
try:
CURDIR = sys._MEIPASS
except:
CURDIR = os.getcwd()
CWDIR = os.getcwd()
SCURDIR = CWDIR
else:
CURDIR = os.path.dirname(__file__)
CWDIR = CURDIR
SCURDIR = os.getcwd()
for i in [CWDIR, SCURDIR]:
if not i in sys.path:
sys.path.append(i)
sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 0)
sys.stderr = os.fdopen(sys.stderr.fileno(), 'w', 0)
import time
import datetime
import decimal
import random
import string
FORM_VALID = [x for x in string.ascii_lowercase] + [x for x in string.digits]
FORM_VALID.append('_')
CALCULATOR_ALLOWED = string.digits + '.-+*/()'
URL_VALID = [x for x in string.ascii_lowercase]
PATH_VALID = [x for x in string.ascii_lowercase] + [x for x in string.digits]
PATH_VALID.append('_')
PATH_VALID.append('.')
PATH_VALID.append('/')
PATH_VALID.append('-')
import socket
try:
DEFAULT_HOSTS_ALLOWED.append(socket.gethostbyname(socket.gethostname()))
except:
pass
try:
from hashlib import md5
except ImportError:
from md5 import md5
import urllib
import hashlib
import base64
import platform
import struct
import re
import csv
try:
import cStringIO
except ImportError:
import StringIO as cStringIO
from HTMLParser import HTMLParser
import calendar
import copy
import traceback
try:
import reportlab
from reportlab.lib.enums import TA_LEFT as PDF_TA_LEFT
from reportlab.lib.enums import TA_CENTER as PDF_TA_CENTER
from reportlab.lib.enums import TA_RIGHT as PDF_TA_RIGHT
from reportlab.lib.enums import TA_JUSTIFY as PDF_TA_JUSTIFY
from reportlab.lib.colors import black as PDF_DEFAULT_BORDER_COLOR
from reportlab.lib.styles import getSampleStyleSheet as PDF_STYLE_SHEET
from reportlab.platypus import SimpleDocTemplate as PDF_TEMPLATE
from reportlab.platypus import Table as PDF_TABLE
from reportlab.platypus import Image as PDF_IMAGE
from reportlab.platypus import Spacer as PDF_SPACER
from reportlab.platypus import Paragraph as PDF_PARAGRAPH
from reportlab.lib.pagesizes import A4 as PDF_DEFAULT_PAGE_SIZE
from reportlab.lib.units import inch as PDF_UNIT_INCH
#
PDF_DEFAULT_BORDER_STYLE = [
(
'GRID',
(0, 0),
(-1, -1),
1,
PDF_DEFAULT_BORDER_COLOR,
),
]
PDF_DEFAULT_SPACER_WIDTH = 1
PDF_DEFAULT_SPACER_HEIGHT = 36
PDF_DEFAULT_PARAGRAPH_STYLE = PDF_STYLE_SHEET()['BodyText']
except ImportError:
PDF_TA_LEFT = 0
PDF_TA_CENTER = 0
PDF_TA_RIGHT = 0
PDF_TA_JUSTIFY = 0
PDF_TEMPLATE = None
PDF_TABLE = None
PDF_IMAGE = None
PDF_SPACER = None
PDF_PARAGRAPH = None
PDF_DEFAULT_PAGE_SIZE = ()
PDF_UNIT_INCH = 0
reportlab = None
if reportlab:
try:
from reportlab.pdfbase import _fontdata_enc_winansi
from reportlab.pdfbase import _fontdata_enc_macroman
from reportlab.pdfbase import _fontdata_enc_standard
from reportlab.pdfbase import _fontdata_enc_symbol
from reportlab.pdfbase import _fontdata_enc_zapfdingbats
from reportlab.pdfbase import _fontdata_enc_pdfdoc
from reportlab.pdfbase import _fontdata_enc_macexpert
from reportlab.pdfbase import _fontdata_widths_courier
from reportlab.pdfbase import _fontdata_widths_courierbold
from reportlab.pdfbase import _fontdata_widths_courieroblique
from reportlab.pdfbase import _fontdata_widths_courierboldoblique
from reportlab.pdfbase import _fontdata_widths_helvetica
from reportlab.pdfbase import _fontdata_widths_helveticabold
from reportlab.pdfbase import _fontdata_widths_helveticaoblique
from reportlab.pdfbase import _fontdata_widths_helveticaboldoblique
from reportlab.pdfbase import _fontdata_widths_timesroman
from reportlab.pdfbase import _fontdata_widths_timesbold
from reportlab.pdfbase import _fontdata_widths_timesitalic
from reportlab.pdfbase import _fontdata_widths_timesbolditalic
from reportlab.pdfbase import _fontdata_widths_symbol
from reportlab.pdfbase import _fontdata_widths_zapfdingbats
except:
pass
try:
import sqlite3
import json
import web
web.config.debug = False
ssl_cert = os.path.join(SCURDIR, DEFAULT_SSL_CERTIFICATE)
ssl_pkey = os.path.join(SCURDIR, DEFAULT_SSL_PRIVATE_KEY)
ssl_cert = os.path.abspath(ssl_cert)
ssl_pkey = os.path.abspath(ssl_pkey)
if os.path.exists(ssl_cert) and os.path.exists(ssl_pkey):
import OpenSSL
from web.wsgiserver import CherryPyWSGIServer
CherryPyWSGIServer.ssl_certificate = ssl_cert
CherryPyWSGIServer.ssl_private_key = ssl_pkey
except Exception, e:
lsep = os.linesep
emsg = '%s%s%s%s%s%s%s' %(
TITLE,
lsep,
APP_DESC,
lsep,
lsep,
str(e),
lsep
)
sys.stderr.write(emsg)
sys.exit(IMPORT_ERROR_CODE)
#----------------------------------------------------------------------#
# WEB #
#----------------------------------------------------------------------#
URLS = (
DEFAULT_HOME, 'home',
DEFAULT_INDEX, 'index',
'/favicon.ico', 'favicon_ico',
'/table/action', 'table_action',
'/table/browse/(.*)', 'table_browse',
'/table/column', 'table_column',
'/table/rename', 'table_rename',
'/table/empty', 'table_empty',
'/table/drop', 'table_drop',
'/table/export/csv', 'table_export_csv',
'/table/import/csv', 'table_import_csv',
'/table/schema', 'table_schema',
'/table/copy', 'table_copy',
'/table/create', 'table_create',
'/query', 'query',
'/vacuum', 'vacuum',
'/table/row/(.*)', 'table_row',
'/table/blob/(.*)', 'table_blob',
'/table/save', 'table_save',
FORM_URL_INIT, 'sqliteboy_init',
URL_README[0], URL_README[1],
URL_SOURCE[0], URL_SOURCE[1],
'/login', 'login',
'/logout', 'logout',
'/password', 'password',
'/admin/users', 'admin_users',
'/admin/hosts', 'admin_hosts',
'/admin/system', 'admin_system',
'/admin/backup', 'admin_backup',
'/admin/website', 'admin_website',
'/form/action', 'form_action',
'/form/run/(.*)', 'form_run',
'/form/shortcut/(.*)', 'form_shortcut',
'/form/edit', 'form_edit',
'/report/action', 'report_action',
'/report/run/(.*)', 'report_run',
'/report/shortcut/(.*)', 'report_shortcut',
'/report/edit', 'report_edit',
'/notes', 'notes',
'/files', 'files',
'/fs', 'fs',
'/pages', 'pages',
'/page/(.*)', 'page',
'/calculator', 'calculator',
'/interpreter', 'interpreter',
'/admin/scripts', 'admin_scripts',
'/admin/script/(.*)', 'admin_script',
'/profile', 'profile',
'/info', 'info',
DEFAULT_CUSTOM, 'website',
)
URLS_RESERVED_DEFAULT = (
DEFAULT_HOME,
DEFAULT_CUSTOM,
)
URLS_RESERVED_REAL = [x for x in URLS if x.startswith('/') and x not in URLS_RESERVED_DEFAULT]
URLS_RESERVED = [x.replace('/(.*)', '') for x in URLS_RESERVED_REAL]
URLS_RESERVED.sort()
app = None
db = None
dbfile = ''
dbfile0 = ''
rendertime = [0, 0]
rowid = '_%s___%s___%s___%s_' %(
NAME,
ROWID,
random.randrange(0, 999999999),
int(time.time()),
)
#
sess = None
sess_type = 0 #memory
sess_init = {
'var': {},
'table': {},
'user': '',
'admin': 0,
'login_redirect': '',
}
#
style_align_default = {
0: ' class="left"',
1: ' class="center"',
2: ' class="right"',
3: ' class="justify"',
}
style_align_pdf = {
0: PDF_TA_LEFT,
1: PDF_TA_CENTER,
2: PDF_TA_RIGHT,
3: PDF_TA_JUSTIFY,
}
#----------------------------------------------------------------------#
# STRIPHTMLPARSER #
#----------------------------------------------------------------------#
class StripHTMLParser(HTMLParser):
def __init__(self):
self.reset()
self.text = []
def handle_data(self, data):
self.text.append(data)
#----------------------------------------------------------------------#
# MEMSESSION #
#----------------------------------------------------------------------#
class MemSession(web.session.Store):
data = {}
def __init__(self):
self.data = {}
def __len__(self):
return len(self.data.keys())
def __contains__(self, key):
return self.data.has_key(key)
def __getitem__(self, key):
if not self.data.has_key(key):
raise KeyError, key
#
v = self.data[key]
v[0] = time.time()
self.data[key] = v
return v[1]
def __setitem__(self, key, value):
v = [time.time(), value]
self.data[key] = v
def __delitem__(self, key):
if self.data.has_key(key):
del self.data[key]
def cleanup(self, timeout):
kdel = []
now = time.time()
#
for k in self.data.keys():
v = self.data[k]
if now - v[0] > timeout:
kdel.append(k)
#
for k in kdel:
if self.data.has_key(k):
del self.data[k]
#----------------------------------------------------------------------#
# DISKSESSION #
#----------------------------------------------------------------------#
class DiskSession(web.session.DiskStore):
def __len__(self):
return len(os.listdir(self.root))
#----------------------------------------------------------------------#
# MONKEY PROGRAMMING LANGUAGE INTERPRETER #
#----------------------------------------------------------------------#
#
# Simple implementation of The Monkey Programming Language
# interpreter in Python
# Monkey.py
# (c) Noprianto <nopri.anto@icloud.com>, 2019
# Website: nopri.github.io
# License: MIT
# Version: 0.9
#
# Compatible with Python 2 and Python 3
# Minimum Python version: 2.3
#
# Based on code (in Go programming language) in book:
# WRITING AN INTERPRETER IN GO
#
# For Monkey Programming Language interpreter in Java, please
# download Monkey.java or Monkey.jar (minimum Java version: 5.0).
#
# How to use monkey.py:
# - Standalone
# - No command line argument: interactive
# python monkey.py
# Monkey.py 0.9
# Press ENTER to quit
# >> let hello = "Hello World"
# >> hello
# "Hello World"
# >>
# - Command line argument: try to interpret as file
# python monkey.py test.monkey
# If exception occurred: interpret the argument as monkey code
# python monkey.py "puts(1,2,3)"
# 1
# 2
# 3
# null
# - Library
# Please see the example below
#
# In monkey.py, it is possible to set initial environment
# when the interpreter is started. This allows integration
# with external applications. For example:
# code:
#
# try:
# from StringIO import StringIO
# except:
# from io import StringIO
#
# from monkey import *
#
# d = {'hello': 'Hello, World', 'test': True}
# e = MonkeyEnvironment.from_dictionary(d)
# o = StringIO()
# Monkey.evaluator_string('puts(hello); puts(test); ERROR;', e, output=o)
# r = o.getvalue()
# print(r)
#
# output:
#
# Hello, World
# true
# ERROR: identifier not found: ERROR
#
#
#import sys
#import os
MONKEYPY_VERSION = '0.9'
MONKEYPY_TITLE = 'Monkey.py %s' %(MONKEYPY_VERSION)
MONKEYPY_MESSAGE = 'Press ENTER to quit'
MONKEYPY_LINESEP = os.linesep
class MonkeyToken:
ILLEGAL = 'ILLEGAL'
EOF = 'EOF'
IDENT = 'IDENT'
INT = 'INT'
ASSIGN = '='
PLUS = '+'
MINUS = '-'
BANG = '!'
ASTERISK = '*'
SLASH = '/'
LT = '<'
GT = '>'
COMMA = ','
SEMICOLON = ';'
LPAREN = '('
RPAREN = ')'
LBRACE = '{'
RBRACE = '}'
FUNCTION = 'FUNCTION'
LET = 'LET'
TRUE = 'true'
FALSE = 'false'
IF = 'if'
ELSE = 'else'
RETURN = 'return'
EQ = '=='
NOT_EQ = '!='
STRING = 'STRING'
LBRACKET = '['
RBRACKET = ']'
COLON = ':'
def __init__(self, type_='', literal=''):
self.type = type_
self.literal = literal
class MonkeyLexer:
KEYWORDS = {
'fn': MonkeyToken.FUNCTION,
'let': MonkeyToken.LET,
'true': MonkeyToken.TRUE,
'false': MonkeyToken.FALSE,
'if': MonkeyToken.IF,
'else': MonkeyToken.ELSE,
'return': MonkeyToken.RETURN,
}
VALID_IDENTS = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_'
VALID_NUMBERS = '0123456789'
WHITESPACES = [' ', '\t', '\r', '\n']
def __init__(self, input_='', position=0, read=0, ch=''):
self.input = input_
self.position = position
self.read = read
self.ch = ch
def read_char(self):
if self.read >= len(self.input):
self.ch = ''
else:
self.ch = self.input[self.read]
self.position = self.read
self.read += 1
def peek_char(self):
if self.read >= len(self.input):
return ''
else:
return self.input[self.read]
def new_token(self, token, t, ch):
token.type = t
token.literal = ch
return token
def next_token(self):
t = MonkeyToken()
self.skip_whitespace()
if self.ch == '=':
if self.peek_char() == '=':
ch = self.ch
self.read_char()
t = self.new_token(t, MonkeyToken.EQ, ch + self.ch)
else:
t = self.new_token(t, MonkeyToken.ASSIGN, self.ch)
elif self.ch == '+':
t = self.new_token(t, MonkeyToken.PLUS, self.ch)
elif self.ch == '-':
t = self.new_token(t, MonkeyToken.MINUS, self.ch)
elif self.ch == '!':
if self.peek_char() == '=':
ch = self.ch
self.read_char()
t = self.new_token(t, MonkeyToken.NOT_EQ, ch + self.ch)
else:
t = self.new_token(t, MonkeyToken.BANG, self.ch)
elif self.ch == '/':
t = self.new_token(t, MonkeyToken.SLASH, self.ch)
elif self.ch == '*':
t = self.new_token(t, MonkeyToken.ASTERISK, self.ch)
elif self.ch == '<':
t = self.new_token(t, MonkeyToken.LT, self.ch)
elif self.ch == '>':
t = self.new_token(t, MonkeyToken.GT, self.ch)
elif self.ch == ';':
t = self.new_token(t, MonkeyToken.SEMICOLON, self.ch)
elif self.ch == '(':
t = self.new_token(t, MonkeyToken.LPAREN, self.ch)
elif self.ch == ')':
t = self.new_token(t, MonkeyToken.RPAREN, self.ch)
elif self.ch == ',':
t = self.new_token(t, MonkeyToken.COMMA, self.ch)
elif self.ch == '+':
t = self.new_token(t, MonkeyToken.PLUS, self.ch)
elif self.ch == '{':
t = self.new_token(t, MonkeyToken.LBRACE, self.ch)
elif self.ch == '}':
t = self.new_token(t, MonkeyToken.RBRACE, self.ch)
elif self.ch == '':
t.literal = ''
t.type = MonkeyToken.EOF
elif self.ch == '"':
t.literal = self.read_string()
t.type = MonkeyToken.STRING
elif self.ch == '[':
t = self.new_token(t, MonkeyToken.LBRACKET, self.ch)
elif self.ch == ']':
t = self.new_token(t, MonkeyToken.RBRACKET, self.ch)
elif self.ch == ':':
t = self.new_token(t, MonkeyToken.COLON, self.ch)
else:
if self.is_letter(self.ch):
t.literal = self.read_ident()
t.type = self.lookup_ident(t.literal)
return t
elif self.is_digit(self.ch):
t.literal = self.read_number()
t.type = MonkeyToken.INT
return t
else:
t = self.new_token(t, MonkeyToken.ILLEGAL, self.ch)
self.read_char()
return t
def read_ident(self):
pos = self.position
while True:
if not self.ch:
break
test = self.is_letter(self.ch)
if not test:
break
self.read_char()
ret = self.input[pos:self.position]
return ret
def read_number(self):
pos = self.position
while True:
if not self.ch:
break
test = self.is_digit(self.ch)
if not test:
break
self.read_char()
ret = self.input[pos:self.position]
return ret
def read_string(self):
pos = self.position + 1
while True:
self.read_char()
if self.ch == '"' or self.ch == '':
break
#
ret = self.input[pos:self.position]
return ret
def lookup_ident(self, s):
ret = MonkeyLexer.KEYWORDS.get(s)
if ret:
return ret
return MonkeyToken.IDENT
def is_letter(self, ch):
return ch in MonkeyLexer.VALID_IDENTS
def is_digit(self, ch):
return ch in MonkeyLexer.VALID_NUMBERS
def skip_whitespace(self):
while (self.ch in MonkeyLexer.WHITESPACES):
self.read_char()
def new(s):
l = MonkeyLexer()
l.input = s
l.read_char()
return l
new = staticmethod(new)
class MonkeyNode:
def __init__(self):
pass
def token_literal(self):
return ''
def string(self):
return ''
class MonkeyStatement(MonkeyNode):
def __init__(self):
pass
def statement_node(self):
pass
class MonkeyExpression(MonkeyNode):
def __init__(self):
pass
def expression_node(self):
pass
class MonkeyIdentifier(MonkeyExpression):
def __init__(self, value=''):
self.token = MonkeyToken()
self.value = value
def token_literal(self):
return self.token.literal
def string(self):
return self.value
class MonkeyLetStatement(MonkeyStatement):
def __init__(self):
self.token = MonkeyToken()
self.name = MonkeyIdentifier()
self.value = MonkeyExpression()
def token_literal(self):
return self.token.literal
def string(self):
ret = self.token_literal() + ' '
ret += self.name.string()
ret += ' = '
#
if self.value:
ret += self.value.string()
#
ret += ';'
return ret
class MonkeyReturnStatement(MonkeyStatement):
def __init__(self):
self.token = MonkeyToken()
self.return_value = MonkeyExpression()
def token_literal(self):
return self.token.literal
def string(self):
ret = self.token_literal() + ' '
if self.return_value:
ret += self.return_value.string()
#
ret += ';'
return ret
class MonkeyExpressionStatement(MonkeyStatement):
def __init__(self):
self.token = MonkeyToken()
self.expression = MonkeyExpression()
def token_literal(self):
return self.token.literal
def string(self):
if self.expression:
return self.expression.string()
#
return ''
class MonkeyBlockStatement(MonkeyStatement):
def __init__(self):
self.token = MonkeyToken()
self.statements = []
def is_empty(self):
return len(self.statements) == 0
def token_literal(self):
return self.token.literal
def string(self):
ret = '%s{%s' %(MONKEYPY_LINESEP, MONKEYPY_LINESEP)
#
for s in self.statements:
ret += '%s;%s' %(s.string(), MONKEYPY_LINESEP)
#
ret += '}%s' %(MONKEYPY_LINESEP)
return ret
class MonkeyIntegerLiteral(MonkeyExpression):
def __init__(self, value=None):
self.token = MonkeyToken()
self.value = value
def token_literal(self):
return self.token.literal
def string(self):
return self.token.literal
class MonkeyStringLiteral(MonkeyExpression):
def __init__(self, value=''):
self.token = MonkeyToken()
self.value = value
def token_literal(self):
return self.token.literal
def string(self):
return self.token.literal
class MonkeyFunctionLiteral(MonkeyExpression):
def __init__(self):
self.token = MonkeyToken()
self.parameters = []
self.body = MonkeyBlockStatement()
def token_literal(self):
return self.token.literal
def string(self):
params = []
for p in self.parameters:
params.append(p.string())
#
ret = self.token_literal()
ret += '('
ret += ', '.join(params)
ret += ')'
ret += self.body.string()
#
return ret
class MonkeyCallExpression(MonkeyExpression):
def __init__(self):
self.token = MonkeyToken()
self.function = MonkeyExpression()
self.arguments = []
def token_literal(self):
return self.token.literal
def string(self):
args = []
for a in self.arguments:
args.append(a.string())
#
ret = self.function.string()
ret += '('
ret += ', '.join(args)
ret += ')'
#
return ret
class MonkeyBoolean(MonkeyExpression):
def __init__(self, value=None):
self.token = MonkeyToken()
self.value = value
def token_literal(self):
return self.token.literal
def string(self):
return self.token.literal
class MonkeyPrefixExpression(MonkeyExpression):
def __init__(self):
self.token = MonkeyToken()
self.operator = ''
self.right = MonkeyExpression()
def token_literal(self):
return self.token.literal
def string(self):
ret = '('
ret += self.operator
ret += self.right.string()
ret += ')'
#
return ret
class MonkeyInfixExpression(MonkeyExpression):
def __init__(self):
self.token = MonkeyToken()
self.left = MonkeyExpression()
self.operator = ''
self.right = MonkeyExpression()
def token_literal(self):
return self.token.literal
def string(self):
ret = '('
ret += self.left.string()
ret += ' ' + self.operator + ' '
ret += self.right.string()
ret += ')'
#
return ret
class MonkeyIfExpression(MonkeyExpression):
def __init__(self):
self.token = MonkeyToken()
self.condition = MonkeyExpression()
self.consequence = MonkeyBlockStatement()
self.alternative = MonkeyBlockStatement()
def token_literal(self):
return self.token.literal
def string(self):
ret = 'if'
ret += self.condition.string()
ret += ' '
ret += self.consequence.string()
#
if not self.alternative.is_empty():
ret += ' else '
ret += self.alternative.string()
#
return ret
class MonkeyArrayLiteral(MonkeyExpression):
def __init__(self):
self.token = MonkeyToken()
self.elements = []
def token_literal(self):
return self.token.literal
def string(self):
elements = []
for e in self.elements:
elements.append(e.string())
#
ret = '['
ret += ', '.join(elements)
ret += ']'
#
return ret
class MonkeyIndexExpression(MonkeyExpression):
def __init__(self):
self.token = MonkeyToken()
self.left = MonkeyExpression()
self.index = MonkeyExpression()
def token_literal(self):
return self.token.literal
def string(self):
ret = '('
ret += self.left.string()
ret += '['
ret += self.index.string()
ret += '])'
#
return ret
class MonkeyHashLiteral(MonkeyExpression):
def __init__(self):
self.token = MonkeyToken()
self.pairs = {}
def token_literal(self):
return self.token.literal
def string(self):
pairs = []
for k in self.pairs.keys():
v = self.pairs.get(k)
pairs.append('%s:%s' %(k.string(), v.string()))
#
ret = '{'
ret += ', '.join(pairs)
ret += '}'
#
return ret
class MonkeyParser:
LOWEST = 1
EQUALS = 2
LESSGREATER = 3
SUM = 4
PRODUCT = 5
PREFIX = 6
CALL = 7
INDEX = 8
PRECEDENCES = {
MonkeyToken.LPAREN: CALL,
MonkeyToken.EQ: EQUALS,
MonkeyToken.NOT_EQ: EQUALS,
MonkeyToken.LT: LESSGREATER,
MonkeyToken.GT: LESSGREATER,
MonkeyToken.PLUS: SUM,
MonkeyToken.MINUS: SUM,
MonkeyToken.SLASH: PRODUCT,
MonkeyToken.ASTERISK: PRODUCT,
MonkeyToken.LBRACKET: INDEX,
}
def __init__(self):
self.lexer = None
self.cur_token = None
self.peek_token = None
self.errors = []
self.prefix_parse_fns = {}
self.infix_parse_fns = {}
#
self.register_prefix(MonkeyToken.IDENT, self.parse_identifier)
self.register_prefix(MonkeyToken.INT, self.parse_integer_literal)
self.register_prefix(MonkeyToken.BANG, self.parse_prefix_expression)
self.register_prefix(MonkeyToken.MINUS, self.parse_prefix_expression)
self.register_prefix(MonkeyToken.TRUE, self.parse_boolean)
self.register_prefix(MonkeyToken.FALSE, self.parse_boolean)
self.register_prefix(MonkeyToken.LPAREN, self.parse_grouped_expression)
self.register_prefix(MonkeyToken.IF, self.parse_if_expression)
self.register_prefix(MonkeyToken.FUNCTION, self.parse_function_literal)
self.register_prefix(MonkeyToken.STRING, self.parse_string_literal)
self.register_prefix(MonkeyToken.LBRACKET, self.parse_array_literal)
self.register_prefix(MonkeyToken.LBRACE, self.parse_hash_literal)
#
self.register_infix(MonkeyToken.PLUS, self.parse_infix_expression)
self.register_infix(MonkeyToken.MINUS, self.parse_infix_expression)
self.register_infix(MonkeyToken.SLASH, self.parse_infix_expression)
self.register_infix(MonkeyToken.ASTERISK, self.parse_infix_expression)
self.register_infix(MonkeyToken.EQ, self.parse_infix_expression)
self.register_infix(MonkeyToken.NOT_EQ, self.parse_infix_expression)
self.register_infix(MonkeyToken.LT, self.parse_infix_expression)
self.register_infix(MonkeyToken.GT, self.parse_infix_expression)
self.register_infix(MonkeyToken.LPAREN, self.parse_call_expression)
self.register_infix(MonkeyToken.LBRACKET, self.parse_index_expression)
def next_token(self):
self.cur_token = self.peek_token
self.peek_token = self.lexer.next_token()
def parse_program(self):
program = MonkeyProgram()
while self.cur_token.type != MonkeyToken.EOF:
s = self.parse_statement()
if s:
program.statements.append(s)
self.next_token()
return program
def parse_statement(self):
if self.cur_token.type == MonkeyToken.LET:
return self.parse_let_statement()
elif self.cur_token.type == MonkeyToken.RETURN:
return self.parse_return_statement()
else:
return self.parse_expression_statement()
return None
def parse_let_statement(self):
s = MonkeyLetStatement()
s.token = self.cur_token
if not self.expect_peek(MonkeyToken.IDENT):
return None
#
s.name = MonkeyIdentifier()
s.name.token = self.cur_token
s.name.value = self.cur_token.literal
if not self.expect_peek(MonkeyToken.ASSIGN):
return None
#
self.next_token()
s.value = self.parse_expression(self.LOWEST)
if self.peek_token_is(MonkeyToken.SEMICOLON):
self.next_token()
#
return s
def parse_return_statement(self):
s = MonkeyReturnStatement()
s.token = self.cur_token
self.next_token()
s.return_value = self.parse_expression(self.LOWEST)
if self.peek_token_is(MonkeyToken.SEMICOLON):
self.next_token()
#
return s
def parse_expression_statement(self):
s = MonkeyExpressionStatement()
s.token = self.cur_token
s.expression = self.parse_expression(self.LOWEST)
#
if self.peek_token_is(MonkeyToken.SEMICOLON):
self.next_token()
#
return s
def parse_block_statement(self):
block = MonkeyBlockStatement()
block.token = self.cur_token
#
self.next_token()
while not self.cur_token_is(MonkeyToken.RBRACE) and \
not self.cur_token_is(MonkeyToken.EOF):
s = self.parse_statement()
if s is not None:
block.statements.append(s)
self.next_token()
#
return block
def parse_expression(self, precedence):
prefix = self.prefix_parse_fns.get(self.cur_token.type)
if prefix is None:
self.no_prefix_parse_fn_error(self.cur_token.type)
return None
left_exp = prefix()
#
while not self.peek_token_is(MonkeyToken.SEMICOLON) and \
precedence < self.peek_precedence():
infix = self.infix_parse_fns.get(self.peek_token.type)
if infix is None:
return left_exp
#
self.next_token()
left_exp = infix(left_exp)
#
return left_exp
def parse_identifier(self):
ret = MonkeyIdentifier()
ret.token = self.cur_token
ret.value = self.cur_token.literal
return ret
def parse_integer_literal(self):
lit = MonkeyIntegerLiteral()
lit.token = self.cur_token
try:
test = int(self.cur_token.literal)
except:
msg = 'could not parse %s as integer' %(self.cur_token.literal)
self.errors.append(msg)
return None
#
lit.value = int(self.cur_token.literal)
return lit
def parse_string_literal(self):
lit = MonkeyStringLiteral()
lit.token = self.cur_token
lit.value = self.cur_token.literal
return lit
def parse_array_literal(self):
array = MonkeyArrayLiteral()
array.token = self.cur_token
array.elements = self.parse_expression_list(MonkeyToken.RBRACKET)
return array
def parse_hash_literal(self):
h = MonkeyHashLiteral()
h.token = self.cur_token
#
while not self.peek_token_is(MonkeyToken.RBRACE):
self.next_token()
key = self.parse_expression(self.LOWEST)
#
if not self.expect_peek(MonkeyToken.COLON):
return None
#
self.next_token()
value = self.parse_expression(self.LOWEST)
#
h.pairs[key] = value
#
if not self.peek_token_is(MonkeyToken.RBRACE) and \
not self.expect_peek(MonkeyToken.COMMA):
return None
#
if not self.expect_peek(MonkeyToken.RBRACE):
return None
#
return h
def parse_boolean(self):
ret = MonkeyBoolean()
ret.token = self.cur_token
ret.value = self.cur_token_is(MonkeyToken.TRUE)
return ret
def parse_prefix_expression(self):
e = MonkeyPrefixExpression()
e.token = self.cur_token
e.operator = self.cur_token.literal
#
self.next_token()
e.right = self.parse_expression(self.PREFIX)
#
return e
def parse_infix_expression(self, left):
e = MonkeyInfixExpression()
e.token = self.cur_token
e.operator = self.cur_token.literal
e.left = left
#
precedence = self.cur_precedence()
self.next_token()
e.right = self.parse_expression(precedence)
#
return e
def parse_grouped_expression(self):
self.next_token()
e = self.parse_expression(self.LOWEST)
#
if not self.expect_peek(MonkeyToken.RPAREN):
return None
#
return e
def parse_if_expression(self):
e = MonkeyIfExpression()
e.token = self.cur_token
#
if not self.expect_peek(MonkeyToken.LPAREN):
return None
#
self.next_token()
e.condition = self.parse_expression(self.LOWEST)
#
if not self.expect_peek(MonkeyToken.RPAREN):
return None
#
if not self.expect_peek(MonkeyToken.LBRACE):
return None
#
e.consequence = self.parse_block_statement()
#
if self.peek_token_is(MonkeyToken.ELSE):
self.next_token()
#
if not self.expect_peek(MonkeyToken.LBRACE):
return None
e.alternative = self.parse_block_statement()
#
return e
def parse_function_literal(self):
lit = MonkeyFunctionLiteral()
lit.token = self.cur_token
#
if not self.expect_peek(MonkeyToken.LPAREN):
return None
#
lit.parameters = self.parse_function_parameters()
#
if not self.expect_peek(MonkeyToken.LBRACE):
return None
#
lit.body = self.parse_block_statement()
#
return lit
def parse_function_parameters(self):
identifiers = []
#
if self.peek_token_is(MonkeyToken.RPAREN):
self.next_token()
return identifiers
#
self.next_token()
ident = MonkeyIdentifier()
ident.token = self.cur_token
ident.value = self.cur_token.literal
identifiers.append(ident)
#
while self.peek_token_is(MonkeyToken.COMMA):
self.next_token()
self.next_token()
ident = MonkeyIdentifier()
ident.token = self.cur_token
ident.value = self.cur_token.literal
identifiers.append(ident)
#
if not self.expect_peek(MonkeyToken.RPAREN):
return None
#
return identifiers
def parse_call_expression(self, function):
exp = MonkeyCallExpression()
exp.token = self.cur_token
exp.function = function
exp.arguments = self.parse_expression_list(MonkeyToken.RPAREN)
return exp
def parse_expression_list(self, end):
ret = []
#
if self.peek_token_is(end):
self.next_token()
return ret
#
self.next_token()
ret.append(self.parse_expression(self.LOWEST))
#
while self.peek_token_is(MonkeyToken.COMMA):
self.next_token()
self.next_token()
ret.append(self.parse_expression(self.LOWEST))
#
if not self.expect_peek(end):
return None
#
return ret
def parse_index_expression(self, left):
exp = MonkeyIndexExpression()
exp.token = self.cur_token
exp.left = left
#
self.next_token()
exp.index = self.parse_expression(self.LOWEST)
#
if not self.expect_peek(MonkeyToken.RBRACKET):
return None
#
return exp
def cur_token_is(self, t):
return self.cur_token.type == t
def peek_token_is(self, t):
return self.peek_token.type == t
def expect_peek(self, t):
if self.peek_token_is(t):
self.next_token()
return True
else:
self.peek_error(t)
return False
def peek_error(self, t):
m = 'expected next token to be %s, got %s instead' %(
t, self.peek_token.type
)
self.errors.append(m)
def register_prefix(self, token_type, fn):
self.prefix_parse_fns[token_type] = fn
def register_infix(self, token_type, fn):
self.infix_parse_fns[token_type] = fn
def no_prefix_parse_fn_error(self, token_type):
m = 'no prefix parse function for %s found' %(token_type)
self.errors.append(m)
def peek_precedence(self):
p = self.PRECEDENCES.get(self.peek_token.type)
if p:
return p
#
return self.LOWEST
def cur_precedence(self):
p = self.PRECEDENCES.get(self.cur_token.type)
if p:
return p
#
return self.LOWEST
def new(l):
p = MonkeyParser()
p.lexer = l
p.next_token()
p.next_token()
return p
new = staticmethod(new)
class MonkeyProgram(MonkeyNode):
def __init__(self):
self.statements = []
def token_literal(self):
if len(self.statements) > 0:
return self.statements[0].token_literal()
else:
return ''
def string(self):
ret = ''
for s in self.statements:
ret += s.string()
return ret
class MonkeyHashable:
def hash_key(self):
pass
class MonkeyObject:
INTEGER_OBJ = 'INTEGER'
BOOLEAN_OBJ = 'BOOLEAN'
NULL_OBJ = 'NULL'
RETURN_VALUE_OBJ = 'RETURN_VALUE'
ERROR_OBJ = 'ERROR'
FUNCTION_OBJ = 'FUNCTION'
STRING_OBJ = 'STRING'
BUILTIN_OBJ = 'BUILTIN'
ARRAY_OBJ = 'ARRAY'
HASH_OBJ = 'HASH'
def __init__(self, value=None, type_=''):
self.value = value
self.object_type = type_
def type(self):
return self.object_type
def inspect(self):
return ''
def inspect_value(self):
return self.inspect()
class MonkeyObjectInteger(MonkeyObject, MonkeyHashable):
def type(self):
return self.INTEGER_OBJ
def inspect(self):
return '%s' %(self.value)
def hash_key(self):
o = MonkeyHashKey(type_=self.type(), value=self.value)
return o
class MonkeyObjectString(MonkeyObject, MonkeyHashable):
def type(self):
return self.STRING_OBJ
def inspect(self):
return '"%s"' %(self.value)
def hash_key(self):
o = MonkeyHashKey(type_=self.type(), value=hash(self.value))
return o
def inspect_value(self):
return self.value
class MonkeyObjectBoolean(MonkeyObject, MonkeyHashable):
def type(self):
return self.BOOLEAN_OBJ
def inspect(self):
ret = '%s' %(self.value)
ret = ret.lower()
return ret
def hash_key(self):
o = MonkeyHashKey(type_=self.type())
if self.value:
o.value = 1
else:
o.value = 0
return o
class MonkeyObjectNull(MonkeyObject):
def type(self):
return self.NULL_OBJ
def inspect(self):
return 'null'
class MonkeyObjectReturnValue(MonkeyObject):
def type(self):
return self.RETURN_VALUE_OBJ
def inspect(self):
return self.value.inspect()
class MonkeyObjectError(MonkeyObject):
def __init__(self, value=None, message=''):
self.message = message
self.value = value
def type(self):
return self.ERROR_OBJ
def inspect(self):
return 'ERROR: %s' %(self.message)
class MonkeyObjectFunction(MonkeyObject):
def __init__(self):
self.parameters = []
self.body = MonkeyBlockStatement()
self.env = MonkeyEnvironment.new()
def type(self):
return self.FUNCTION_OBJ
def inspect(self):
params = []
for p in self.parameters:
params.append(p.string())
#
ret = 'fn'
ret += '('
ret += ', '.join(params)
ret += ')'
ret += self.body.string()
#
return ret
class MonkeyObjectBuiltin(MonkeyObject):
def __init__(self, fn=None, value=None):
self.fn = fn
self.value = value
def type(self):
return self.BUILTIN_OBJ
def inspect(self):
return 'builtin function'
class MonkeyObjectArray(MonkeyObject):
def __init__(self):
self.elements = []
def type(self):
return self.ARRAY_OBJ
def inspect(self):
elements = []
for e in self.elements:
elements.append(e.inspect())
#
ret = '['
ret += ', '.join(elements)
ret += ']'
#
return ret
class MonkeyObjectHash(MonkeyObject):
def __init__(self):
self.pairs = {}
def type(self):
return self.HASH_OBJ
def inspect(self):
pairs = []
for k in self.pairs.keys():
v = self.pairs.get(k)
pair = '%s: %s' %(v.key.inspect(), v.value.inspect())
pairs.append(pair)
#
ret = '{'
ret += ', '.join(pairs)
ret += '}'
#
return ret
class MonkeyHashKey:
def __init__(self, type_='', value=None):
self.type = type_
self.value = value
def __eq__(self, other):
if isinstance(other, MonkeyHashKey):
if other.type == self.type and other.value == self.value:
return True
return False
def __ne__(self, other):
if isinstance(other, MonkeyHashKey):
if other.type == self.type and other.value == self.value:
return False
return True
def __hash__(self):
h = '%s-%s' %(self.type, self.value)
return hash(h)
class MonkeyHashPair:
def __init__(self):
self.key = MonkeyObject()
self.value = MonkeyObject()
class MonkeyEnvironment:
def __init__(self, outer=None):
self.store = {}
self.outer = outer
def get(self, name):
obj = self.store.get(name)
if obj is None and self.outer is not None:
obj = self.outer.get(name)
return obj
def set(self, name, value):
self.store[name] = value
return value
def debug(self):
for k in self.store.keys():
v = self.store.get(k)
if v is not None:
Monkey.output('%s: %s' %(
k,
v.inspect(),
)
)
def new():
e = MonkeyEnvironment()
return e
new = staticmethod(new)
def new_enclosed(outer):
e = MonkeyEnvironment()
e.outer = outer
return e
new_enclosed = staticmethod(new_enclosed)
def from_dictionary(d):
e = MonkeyEnvironment()
if not isinstance(d, dict):
return e
#
for k in d.keys():
v = d.get(k)
key = None
value = None
if type(k) == type(''):
key = k
else:
key = str(k)
#
if type(v) == type(''):
value = MonkeyObjectString(value=v)
elif type(v) == type(1):
value = MonkeyObjectInteger(value=v)
elif type(v) == type(True):
value = MonkeyObjectBoolean(value=v)
else:
value = MonkeyObjectString(value=str(v))
#
if key is not None and value is not None:
e.set(key, value)
return e
from_dictionary = staticmethod(from_dictionary)
class MonkeyBuiltinFunctions:
def len(evaluator, args):
if len(args) != 1:
return evaluator.new_error(
'wrong number of arguments, got=%s, want=1' %(
len(args),
)
)
#
a = args[0]
if isinstance(a, MonkeyObjectString):
o = MonkeyObjectInteger()
o.value = len(a.value)
return o
elif isinstance(a, MonkeyObjectArray):
o = MonkeyObjectInteger()
o.value = len(a.elements)
return o
else:
return evaluator.new_error(
'argument to "len" not supported, got %s' %(
a.type()
)
)
len = staticmethod(len)
def first(evaluator, args):
if len(args) != 1:
return evaluator.new_error(
'wrong number of arguments, got=%s, want=1' %(
len(args),
)
)
#
a = args[0]
if not isinstance(a, MonkeyObjectArray):
return evaluator.new_error(
'argument to "first" must be ARRAY, got %s' %(
a.type()
)
)
#
if len(a.elements) > 0:
return a.elements[0]
#
return evaluator.NULL
first = staticmethod(first)
def last(evaluator, args):
if len(args) != 1:
return evaluator.new_error(
'wrong number of arguments, got=%s, want=1' %(
len(args),
)
)
#
a = args[0]
if not isinstance(a, MonkeyObjectArray):
return evaluator.new_error(
'argument to "last" must be ARRAY, got %s' %(
a.type()
)
)
#
length = len(a.elements)
if length > 0:
return a.elements[length-1]
#
return evaluator.NULL
last = staticmethod(last)
def rest(evaluator, args):
if len(args) != 1:
return evaluator.new_error(
'wrong number of arguments, got=%s, want=1' %(
len(args),
)
)
#
a = args[0]
if not isinstance(a, MonkeyObjectArray):
return evaluator.new_error(
'argument to "rest" must be ARRAY, got %s' %(
a.type()
)
)
#
length = len(a.elements)
if length > 0:
o = MonkeyObjectArray()
o.elements = a.elements[1:]
return o
#
return evaluator.NULL
rest = staticmethod(rest)
def push(evaluator, args):
if len(args) != 2:
return evaluator.new_error(
'wrong number of arguments, got=%s, want=2' %(
len(args),
)
)
#
a = args[0]
if not isinstance(a, MonkeyObjectArray):
return evaluator.new_error(
'argument to "push" must be ARRAY, got %s' %(
a.type()
)
)
#
o = MonkeyObjectArray()
o.elements = a.elements[:]
o.elements.append(args[1])
return o
push = staticmethod(push)
def puts(evaluator, args):
for a in args:
Monkey.output(a.inspect_value(), evaluator.output)
#
return evaluator.NULL
puts = staticmethod(puts)
class MonkeyBuiltins:
BUILTINS = {
'len': MonkeyObjectBuiltin(MonkeyBuiltinFunctions.len),
'first': MonkeyObjectBuiltin(MonkeyBuiltinFunctions.first),
'last': MonkeyObjectBuiltin(MonkeyBuiltinFunctions.last),
'rest': MonkeyObjectBuiltin(MonkeyBuiltinFunctions.rest),
'push': MonkeyObjectBuiltin(MonkeyBuiltinFunctions.push),
'puts': MonkeyObjectBuiltin(MonkeyBuiltinFunctions.puts),
}
def get(f):
return MonkeyBuiltins.BUILTINS.get(f)
get = staticmethod(get)
class MonkeyEvaluator:
NULL = MonkeyObjectNull()
TRUE = MonkeyObjectBoolean(True)
FALSE = MonkeyObjectBoolean(False)
def __init__(self):
self.output = sys.stdout
def eval(self, node, env):
if isinstance(node, MonkeyProgram):
return self.eval_program(node, env)
elif isinstance(node, MonkeyExpressionStatement):
return self.eval(node.expression, env)
elif isinstance(node, MonkeyIntegerLiteral):
o = MonkeyObjectInteger()
o.value = node.value
return o
elif isinstance(node, MonkeyBoolean):
return self.get_boolean(node.value)
elif isinstance(node, MonkeyPrefixExpression):
right = self.eval(node.right, env)
if self.is_error(right):
return right
#
return self.eval_prefix_expression(node.operator, right)
elif isinstance(node, MonkeyInfixExpression):
left = self.eval(node.left, env)
if self.is_error(left):
return left
#
right = self.eval(node.right, env)
if self.is_error(right):
return right
#
return self.eval_infix_expression(node.operator, left, right)
elif isinstance(node, MonkeyBlockStatement):
return self.eval_block_statement(node, env)
elif isinstance(node, MonkeyIfExpression):
return self.eval_if_expression(node, env)
elif isinstance(node, MonkeyReturnStatement):
val = self.eval(node.return_value, env)
if self.is_error(val):
return val
#
o = MonkeyObjectReturnValue()
o.value = val
return o
elif isinstance(node, MonkeyLetStatement):
val = self.eval(node.value, env)
if self.is_error(val):
return val
#
env.set(node.name.value, val)
elif isinstance(node, MonkeyIdentifier):
return self.eval_identifier(node, env)
elif isinstance(node, MonkeyFunctionLiteral):
params = node.parameters
body = node.body
#
o = MonkeyObjectFunction()
o.parameters = params
o.body = body
o.env = env
return o
elif isinstance(node, MonkeyCallExpression):
function = self.eval(node.function, env)
if self.is_error(function):
return function
#
args = self.eval_expressions(node.arguments, env)
if len(args) == 1 and self.is_error(args[0]):
return args[0]
#
return self.apply_function(function, args)
elif isinstance(node, MonkeyStringLiteral):
o = MonkeyObjectString()
o.value = node.value
return o
elif isinstance(node, MonkeyArrayLiteral):
elements = self.eval_expressions(node.elements, env)
if len(elements) == 1 and self.is_error(elements[0]):
return elements[0]
#
o = MonkeyObjectArray()
o.elements = elements
return o
elif isinstance(node, MonkeyIndexExpression):
left = self.eval(node.left, env)
if self.is_error(left):
return left
#
index = self.eval(node.index, env)
if self.is_error(index):
return index
#
return self.eval_index_expression(left, index)
elif isinstance(node, MonkeyHashLiteral):
return self.eval_hash_literal(node, env)
#
return None
def eval_program(self, program, env):
ret = MonkeyObject()
for s in program.statements:
ret = self.eval(s, env)
#
if isinstance(ret, MonkeyObjectReturnValue):
return ret.value
elif isinstance(ret, MonkeyObjectError):
return ret
#
return ret
def eval_block_statement(self, block, env):
ret = MonkeyObject()
for s in block.statements:
ret = self.eval(s, env)
#
if ret:
rt = ret.type()
if rt == MonkeyObject.RETURN_VALUE_OBJ or \
rt == MonkeyObject.ERROR_OBJ:
return ret
#
return ret
def get_boolean(self, val):
if val:
return self.TRUE
#
return self.FALSE
def eval_prefix_expression(self, operator, right):
if operator == '!':
return self.eval_bang_operator_expression(right)
elif operator == '-':
return self.eval_minus_prefix_operator_expression(right)
return self.new_error('unknown operator: %s%s' %(
operator, right.type()))
def eval_infix_expression(self, operator, left, right):
if left.type() == MonkeyObject.INTEGER_OBJ and \
right.type() == MonkeyObject.INTEGER_OBJ:
return self.eval_integer_infix_expression(operator, left, right)
elif left.type() == MonkeyObject.STRING_OBJ and \
right.type() == MonkeyObject.STRING_OBJ:
return self.eval_string_infix_expression(operator, left, right)
elif operator == '==':
return self.get_boolean(left == right)
elif operator == '!=':
return self.get_boolean(left != right)
elif left.type() != right.type():
return self.new_error('type mismatch: %s %s %s' %(
left.type(), operator, right.type()))
return self.new_error('unknown operator: %s %s %s' %(
left.type(), operator, right.type()))
def eval_integer_infix_expression(self, operator, left, right):
left_val = left.value
right_val = right.value
#
o = MonkeyObjectInteger()
if operator == '+':
o.value = left_val + right_val
return o
elif operator == '-':
o.value = left_val - right_val
return o
elif operator == '*':
o.value = left_val * right_val
return o
elif operator == '/':
try:
o.value = left_val // right_val
return o
except:
return self.NULL
elif operator == '<':
return self.get_boolean(left_val < right_val)
elif operator == '>':
return self.get_boolean(left_val > right_val)
elif operator == '==':
return self.get_boolean(left_val == right_val)
elif operator == '!=':
return self.get_boolean(left_val != right_val)
return self.new_error('unknown operator: %s %s %s' %(
left.type(), operator, right.type()))
def eval_string_infix_expression(self, operator, left, right):
left_val = left.value
right_val = right.value
#
o = MonkeyObjectString()
if operator != '+':
return self.new_error('unknown operator: %s %s %s' %(
left.type(), operator, right.type()))
#
o.value = left_val + right_val
return o
def eval_bang_operator_expression(self, right):
if right == self.TRUE:
return self.FALSE
elif right == self.FALSE:
return self.TRUE
elif right == self.NULL:
return self.TRUE
return self.FALSE
def eval_minus_prefix_operator_expression(self, right):
if right.type() != MonkeyObject.INTEGER_OBJ:
return self.new_error('unknown operator: -%s' %(right.type()))
#
val = right.value
ret = MonkeyObjectInteger()
ret.value = -val
#
return ret
def eval_if_expression(self, expression, env):
condition = self.eval(expression.condition, env)
if self.is_error(condition):
return condition
#
if self.is_truthy(condition):
return self.eval(expression.consequence, env)
elif not expression.alternative.is_empty():
return self.eval(expression.alternative, env)
else:
return self.NULL
def eval_identifier(self, node, env):
val = env.get(node.value)
if val:
return val
#
builtin = MonkeyBuiltins.get(node.value)
if builtin:
return builtin
#
return self.new_error('identifier not found: %s' %(node.value))
def eval_expressions(self, exp, env):
result = []
#
for e in exp:
evaluated = self.eval(e, env)
if self.is_error(evaluated):
result.append(evaluated)
return result
result.append(evaluated)
#
return result
def eval_index_expression(self, left, index):
if left.type() == MonkeyObject.ARRAY_OBJ and \
index.type() == MonkeyObject.INTEGER_OBJ:
return self.eval_array_index_expression(left, index)
elif left.type() == MonkeyObject.HASH_OBJ:
return self.eval_hash_index_expression(left, index)
return self.new_error('index operator not supported: %s' %(
left.type()))
def eval_array_index_expression(self, array, index):
idx = index.value
max_index = len(array.elements) - 1
#
if idx < 0 or idx > max_index:
return MonkeyEvaluator.NULL
#
return array.elements[idx]
def eval_hash_literal(self, node, env):
pairs = {}
#
for k in node.pairs.keys():
key = self.eval(k, env)
if self.is_error(key):
return key
#
if not isinstance(key, MonkeyHashable):
return self.new_error('unusable as hash key: %s' %(
key.type()
)
)
#
v = node.pairs.get(k)
val = self.eval(v, env)
if self.is_error(val):
return val
#
hashed = key.hash_key()
p = MonkeyHashPair()
p.key = key
p.value = val
pairs[hashed] = p
#
o = MonkeyObjectHash()
o.pairs = pairs
#
return o
def eval_hash_index_expression(self, hashtable, index):
if not isinstance(index, MonkeyHashable):
return self.new_error('unusable as hash key: %s' %(
index.type()
)
)
#
pair = hashtable.pairs.get(index.hash_key())
if pair is None:
return self.NULL
#
return pair.value
def apply_function(self, fn, args):
if isinstance(fn, MonkeyObjectFunction):
extended_env = self.extend_function_env(fn, args)
evaluated = self.eval(fn.body, extended_env)
return self.unwrap_return_value(evaluated)
elif isinstance(fn, MonkeyObjectBuiltin):
return fn.fn(self, args)
#
return self.new_error('not a function: %s' %(fn.type()))
def extend_function_env(self, fn, args):
env = MonkeyEnvironment.new_enclosed(fn.env)
for p in range(len(fn.parameters)):
param = fn.parameters[p]
env.set(param.value, args[p])
#
return env
def unwrap_return_value(self, obj):
if isinstance(obj, MonkeyObjectReturnValue):
return obj.value
#
return obj
def is_truthy(self, obj):
if obj == self.NULL:
return False
elif obj == self.TRUE:
return True
elif obj == self.FALSE:
return False
else:
return True
def new_error(self, message):
ret = MonkeyObjectError()
ret.message = message
return ret
def is_error(self, obj):
if obj:
return obj.type() == MonkeyObject.ERROR_OBJ
#
return False
def new():
e = MonkeyEvaluator()
return e
new = staticmethod(new)
class Monkey:
PROMPT = '>> '
def input(s):
try:
return raw_input(s)
except:
return input(s)
input = staticmethod(input)
def output(s, f=sys.stdout):
try:
f.write('%s%s' %(s, MONKEYPY_LINESEP))
except:
pass
output = staticmethod(output)
def lexer():
Monkey.output(MONKEYPY_TITLE)
Monkey.output(MONKEYPY_MESSAGE)
while True:
inp = Monkey.input(Monkey.PROMPT).strip()
if not inp:
break
l = MonkeyLexer.new(inp)
while True:
t = l.next_token()
if t.type == MonkeyToken.EOF:
break
Monkey.output(
'Type: %s, Literal: %s' %(t.type, t.literal))
lexer = staticmethod(lexer)
def parser():
Monkey.output(MONKEYPY_TITLE)
Monkey.output(MONKEYPY_MESSAGE)
while True:
inp = Monkey.input(Monkey.PROMPT).strip()
if not inp:
break
l = MonkeyLexer.new(inp)
p = MonkeyParser.new(l)
program = p.parse_program()
#
if p.errors:
Monkey.print_parse_errors(p.errors)
continue
#
Monkey.output(program.string())
parser = staticmethod(parser)
def print_parse_errors(e, output=sys.stdout):
for i in e:
Monkey.output('PARSER ERROR: %s' %(i), output)
print_parse_errors = staticmethod(print_parse_errors)
def evaluator():
Monkey.output(MONKEYPY_TITLE)
Monkey.output(MONKEYPY_MESSAGE)
env = MonkeyEnvironment.new()
while True:
inp = Monkey.input(Monkey.PROMPT).strip()
if not inp:
break
l = MonkeyLexer.new(inp)
p = MonkeyParser.new(l)
program = p.parse_program()
#
if p.errors:
Monkey.print_parse_errors(p.errors)
continue
#
evaluator = MonkeyEvaluator.new()
evaluated = evaluator.eval(program, env)
if evaluated:
Monkey.output(evaluated.inspect())
evaluator = staticmethod(evaluator)
def evaluator_string(s, environ=None, output=sys.stdout):
if environ is None or not isinstance(environ, MonkeyEnvironment):
env = MonkeyEnvironment.new()
else:
env = environ
l = MonkeyLexer.new(s)
p = MonkeyParser.new(l)
program = p.parse_program()
#
if p.errors:
Monkey.print_parse_errors(p.errors, output)
return
#
evaluator = MonkeyEvaluator.new()
evaluator.output = output
evaluated = evaluator.eval(program, env)
if evaluated:
Monkey.output(evaluated.inspect(), output)
evaluator_string = staticmethod(evaluator_string)
def main(argv):
if len(argv) < 2:
Monkey.evaluator()
else:
t = argv[1]
s = t
if os.path.exists(t):
try:
s = open(t).read()
except:
pass
#
if s:
Monkey.evaluator_string(s)
main = staticmethod(main)
#if __name__ == '__main__':
# Monkey.main(sys.argv)
#----------------------------------------------------------------------#
# NUMBER TO WORDS #
#----------------------------------------------------------------------#
NUMBER_TO_WORDS = {}
class NumberToWords:
def __init__(self):
self.word = {}
self.name1 = {}
self.name2 = {}
self.sign = {}
self.replace = {}
self.style = {}
self.data = {}
#
self.chunk_size = 3
def maxlength(self):
ret = 0
#
ln1 = len(self.name1.keys())
#
if ln1:
ret = ( ( 2 * ln1 ) - 1) * self.chunk_size
#
return ret
def separator(self):
return self.style.get('separator', ' ')
def decimal_separator(self):
return self.style.get('decimal_separator', ' ')
def is_number(self, s, check_length=True):
s = str(s)
#
ret = False
#
if check_length:
if len(s) > self.maxlength():
return False
#
try:
test = float(s)
ret = True
except:
pass
#
return ret
def is_negative(self, s):
ret = False
#
try:
test = float(s)
if test < 0:
ret = True
except:
pass
#
return ret
def split(self, s):
s = str(s).lower()
#
ret = ()
#
if not self.is_number(s, False):
return ret
#
if 'e' in s:
return ret
#
P1 = ''
P2 = ''
if '.' in s:
P1, P2 = s.split('.')
else:
P1 = s
P1 = P1.strip()
P2 = P2.strip()
#
P1 = P1.replace('+', '')
P1 = P1.replace('-', '')
#
if not self.is_number(P1):
return ret
#
ret = (P1, P2)
return ret
def chunk(self, s):
s = str(s)
#
n = self.chunk_size
#
mod = len(s) % n
if mod:
ln = len(s) + (n - mod)
s = s.rjust(ln, ' ')
#
ret = [s[i:i+n] for i in range(0, len(s), n)]
ret = [s.strip() for s in ret]
#
return ret
def get_single(self, s):
s = str(s)
#
res = []
#
for i in s:
x = self.word.get(i, '')
if x and i == '0':
x = x[0]
res.append(x)
#
ret = self.separator().join(res)
return ret
def get_1d(self, s):
s = str(s)
#
ret = ''
#
if not len(s) == 1:
return ret
#
r = self.word.get(s, '')
if r and s == '0':
r = r[1]
#
ret = r
return ret
def get_2d(self, s):
'''
override this
'''
s = str(s)
#
ret = ''
#
if not len(s) == 2:
return ret
#
return ret
def get_3d(self, s):
'''
override this
'''
s = str(s)
#
ret = ''
#
if not len(s) == 3:
return ret
#
return ret
def get_d(self, s):
s = str(s)
#
ls = len(s)
#
ret = ''
#
if ls == 1:
ret = self.get_1d(s)
elif ls == 2:
ret = self.get_2d(s)
elif ls == 3:
ret = self.get_3d(s)
#
return ret
def get_x1(self, c