aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSebastian <s_rath@xpresso24.com>2019-10-03 00:04:51 -0400
committerAustin Morton <austinpmorton@gmail.com>2019-10-30 19:51:38 -0400
commit78b3381c84a238cc8be48e5685d74ceec5b36765 (patch)
treeec05ec7a8b33b653b7ac289da401d3eb636e10c1
parent568f906322377817c400f09f0e7212e8ba800a11 (diff)
downloadcompiler-explorer-78b3381c84a238cc8be48e5685d74ceec5b36765.tar.gz
compiler-explorer-78b3381c84a238cc8be48e5685d74ceec5b36765.zip
* Add Python support (#1612)
-rw-r--r--CONTRIBUTORS.md2
-rw-r--r--etc/config/python.defaults.properties21
-rw-r--r--etc/scripts/dis_all.py132
-rw-r--r--examples/python/default.py2
-rw-r--r--lib/compilers/python.js86
-rw-r--r--lib/languages.js6
6 files changed, 248 insertions, 1 deletions
diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md
index 59e18e265..7ad5cae63 100644
--- a/CONTRIBUTORS.md
+++ b/CONTRIBUTORS.md
@@ -78,4 +78,4 @@ From oldest to newest contributor, we would like to thank:
- [Matt Hammerly](https://github.com/mhammerly)
- [Christian Vonrüti](https://github.com/alshain)
- [Alessandro Vergani](https://github.com/Loghorn)
-
+- [Sebastian Rath](https://github.com/seb-mtl)
diff --git a/etc/config/python.defaults.properties b/etc/config/python.defaults.properties
new file mode 100644
index 000000000..90511e100
--- /dev/null
+++ b/etc/config/python.defaults.properties
@@ -0,0 +1,21 @@
+compilers=&python3
+
+group.python3.compilers=python36:python37:python38
+compiler.python36.name=Python 3.6
+compiler.python36.exe=python3.6
+compiler.python37.name=Python 3.7
+compiler.python37.exe=python3.7
+compiler.python38.name=Python 3.8
+compiler.python38.exe=python3.8
+
+supportsBinary=false
+compilerType=python
+objdumper=
+demangler=
+postProcess=
+options=
+supportsBinary=false
+needsMulti=false
+supportsExecute=false
+stubText=def main():
+disasmScript=
diff --git a/etc/scripts/dis_all.py b/etc/scripts/dis_all.py
new file mode 100644
index 000000000..65472ad18
--- /dev/null
+++ b/etc/scripts/dis_all.py
@@ -0,0 +1,132 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2019, Sebastian Rath
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+# * Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+
+import os
+import sys
+import dis
+import argparse
+import traceback
+
+from dis import dis, disassemble, distb, _have_code, _disassemble_bytes, _try_compile
+
+parser = argparse.ArgumentParser(description='Disassembles Python source code given by an input file and writes the output to a file')
+parser.add_argument('-i', '--inputfile', type=str,
+ help='Input source code file (*.py)')
+parser.add_argument('-o', '--outputfile', type=str,
+ help='Optional output file to write output (or error message if syntax error).',
+ default='')
+
+
+def _disassemble_recursive(co, depth=None):
+ disassemble(co)
+ if depth is None or depth > 0:
+ if depth is not None:
+ depth = depth - 1
+ for x in co.co_consts:
+ if hasattr(x, 'co_code'):
+ print()
+ print("Disassembly of %r:" % (x,))
+ _disassemble_recursive(x, depth=depth)
+
+
+def _disassemble_str(source, **kwargs):
+ """Compile the source string, then disassemble the code object."""
+ _disassemble_recursive(_try_compile(source, '<dis>'), **kwargs)
+
+
+# This function is copied from Py 3.7 and compatible with Py 3.3, 3.4, 3.5 to support recursive diassemble
+def dis37(x=None, depth=None):
+ """Disassemble classes, methods, functions, and other compiled objects.
+
+ With no argument, disassemble the last traceback.
+
+ Compiled objects currently include generator objects, async generator
+ objects, and coroutine objects, all of which store their code object
+ in a special attribute.
+ """
+ if x is None:
+ distb()
+ return
+ # Extract functions from methods.
+ if hasattr(x, '__func__'):
+ x = x.__func__
+ # Extract compiled code objects from...
+ if hasattr(x, '__code__'): # ...a function, or
+ x = x.__code__
+ elif hasattr(x, 'gi_code'): #...a generator object, or (added in Py 3.5)
+ x = x.gi_code
+ elif hasattr(x, 'ag_code'): #...an asynchronous generator object, or (added in Py 3.7)
+ x = x.ag_code
+ elif hasattr(x, 'cr_code'): #...a coroutine. (added in Py 3.7)
+ x = x.cr_code
+ # Perform the disassembly.
+ if hasattr(x, '__dict__'): # Class or module (added in Py 3.7)
+ items = sorted(x.__dict__.items())
+ for name, x1 in items:
+ if isinstance(x1, _have_code):
+ print("Disassembly of %s:" % name)
+ try:
+ dis(x1, depth=depth)
+ except TypeError as msg:
+ print("Sorry:", msg)
+ print()
+ elif hasattr(x, 'co_code'): # Code object
+ _disassemble_recursive(x, depth=depth)
+ elif isinstance(x, (bytes, bytearray)): # Raw bytecode
+ _disassemble_bytes(x)
+ elif isinstance(x, str): # Source code
+ _disassemble_str(x, depth=depth)
+ else:
+ raise TypeError("don't know how to disassemble %s objects" %
+ type(x).__name__)
+
+
+if __name__ == '__main__':
+ args = parser.parse_args()
+
+ if not args.inputfile:
+ parser.print_help(sys.stderr)
+ sys.exit(1)
+
+ with open(args.inputfile, 'r', encoding='utf8') as fp:
+ source = fp.read()
+
+ name = os.path.basename(args.inputfile)
+ try:
+ code = compile(source, name, 'exec')
+ except Exception as e:
+ # redirect any other by compile(..) to stderr in order to hide traceback of this script
+ sys.stderr.write(''.join(traceback.format_exception_only(type(e), e)))
+ sys.exit(255)
+
+ if args.outputfile:
+ sys.stdout = open(args.outputfile, 'w', encoding='utf8')
+
+ if sys.version_info < (3, 7):
+ # Any Python version older than 3.7 doesn't support recursive diassembly,
+ # so we call our own function
+ dis37(code)
+ else:
+ dis(code)
diff --git a/examples/python/default.py b/examples/python/default.py
new file mode 100644
index 000000000..4f023ea04
--- /dev/null
+++ b/examples/python/default.py
@@ -0,0 +1,2 @@
+def main():
+ pass \ No newline at end of file
diff --git a/lib/compilers/python.js b/lib/compilers/python.js
new file mode 100644
index 000000000..15f42d0b8
--- /dev/null
+++ b/lib/compilers/python.js
@@ -0,0 +1,86 @@
+// Copyright (c) 2019, Sebastian Rath
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+
+const BaseCompiler = require('../base-compiler'),
+ argumentParsers = require("./argument-parsers"),
+ path = require('path');
+
+class PythonCompiler extends BaseCompiler {
+
+ constructor(compilerInfo, env) {
+ super(compilerInfo, env);
+ this.compiler.demangler = null;
+ this.demanglerClass = null;
+ }
+
+ // eslint-disable-next-line no-unused-vars
+ processAsm(result, filters) {
+ const lineRe = /^\s{0,4}([0-9]+)(.*)/;
+
+ const bytecodeLines = result.asm.split("\n");
+
+ var bytecodeResult = [];
+ var lastLineNo = null;
+ var sourceLoc = null;
+
+ bytecodeLines.forEach(line => {
+ var match = line.match(lineRe);
+
+ if (match) {
+ const lineno = parseInt(match[1]);
+ sourceLoc = {line: lineno, file: null};
+ lastLineNo = lineno;
+ } else if (!line) {
+ sourceLoc = {line: null, file: null};
+ lastLineNo = null;
+ } else {
+ sourceLoc = {line: lastLineNo, file: null};
+ }
+
+ bytecodeResult.push({text: line, source: sourceLoc});
+ });
+
+ return bytecodeResult;
+ }
+
+ getDisasmScriptPath() {
+ const script = this.compilerProps('disasmScript');
+
+ return script || path.resolve(__dirname, '..', '..', 'etc', 'scripts', 'dis_all.py');
+ }
+
+ optionsForFilter(filters, outputFilename) {
+ return ['-I',
+ this.getDisasmScriptPath(),
+ '--outputfile',
+ outputFilename,
+ '--inputfile'];
+ }
+
+ getArgumentParser() {
+ return argumentParsers.Base;
+ }
+}
+
+module.exports = PythonCompiler;
diff --git a/lib/languages.js b/lib/languages.js
index e77b337e7..164b5055a 100644
--- a/lib/languages.js
+++ b/lib/languages.js
@@ -114,6 +114,12 @@ const languages = {
extensions: ['.ml', '.mli'],
alias: []
},
+ python: {
+ name: 'Python',
+ monaco: 'python',
+ extensions: ['.py'],
+ alias: []
+ },
swift: {
name: 'Swift',
monaco: 'swift',