Skip to main content

Parser and transpiler for the Craftr DSL.

Project description

craftr-dsl

The Craftr DSL is a transpiler for the Python language that introduces the concept of closures, function calls without parentheses and a few other syntactical sugar into the language. The language is a full superset of Python 3 code. The added syntax features should only be used where it makes the code more readable or where it is semantically more relevant.

Installation

From Pip:

$ pip install craftr-dsl[codegen,colors]

Latest from GitHub:

$ pip install git+https://github.com/craftr-build/craftr-dsl

Requirements: Python 3.8 or newer

Hello, World!

A convoluted Hello, World! example in Craftr DSL might look like this:

# hello.craftr
world = { self('World!') }
world { print('Hello,', self) }

This is transpiled to

# $ python -m craftr.dsl hello.craftr -E | grep -v -e '^$'
def _closure_1(self, *arguments, **kwarguments):
    self('World!')
world = _closure_1
def _closure_2(self, *arguments, **kwarguments):
    print('Hello,', self)
world(_closure_2)

And evaluates to

# $ python -m craftr.dsl hello.craftr
Hello, World!

Language features

The Craftr DSL grammar and code generator can be configured to an extend to turn some language features and semantics on and off. What will be shown below in most examples is compatible with the default configuration unless otherwise noted.

Closures

Closures are formed with the following syntax: [ arg -> | (arg1, arg2, ...) -> ] { body }. A closure without an argument list automatically has the signature (self, *argnames, **kwargnames).

Craftr DSLPython
filter({ self % 2 }, range(5))
def _closure_1(self, *argnames, **kwargnames):
    self % 2
filter(_closure_1, range(5))
filter(x -> x % 2, range(5))
def _closure_1(x):
    return x % 2
filter(_closure_1, range(5))
reduce((a, b) -> {
  a.append(b * 2)
  return a
}, [1, 2, 3], [])
def _closure_1(a, b):
    a.append(b * 2)
    return a
reduce(_closure_1, [1, 2, 3], [])

Function calls without parentheses

Such function calls are only supported at the statement level. A function can be called without parentheses by simply omitting them. Variadic and keyword arguments are supported as expected. Applying a closure on an object is basically the same as calling that object with the function, and arguments following the closure are still supported.

Craftr DSLPython
print 'Hello, World!', file=sys.stderr
print('Hello, World!', file=sys.stderr)
map {
  print('Hello,', self)
}, ['John', 'World']
def _closure_1(self, *arguments, **kwarguments):
    print('Hello,', self)
map(_closure_1, ['John', 'World'])

Unseparated arguments & colon keyword arguments

The Craftr DSL allows passing arguments to function calls without separation by commas. Keyword arguments may be specified using colons (:) instead of equal signs (=).

Craftr DSLPython
print 'Hello, World!' 42 * 1 + 10 file: sys.stdout
print('Hello, World!', 42 * 1 + 10, file=sys.stdout)
task "hello_world" do: {
  print "Hello, World!"
}
def _closure_1(self, *arguments, **kwarguments):
    print('Hello, World!')
task('hello_world', do=_closure_1)
list(map {
  print('Hello,', self)
}, ['John', 'World'])

Note: Pitfall, this actually passes three arguments to list().

def _closure_1(self, *arguments, **kwarguments):
    print('Hello,', self)
list(map, _closure_1, ['John', 'World'])

Dynamic name resolution (non-default)

For some purposes and applications, dynamic name resolution may be desirable, for example when writing self in front of every name to access a property of the closure target object is too cumbersome. For this, the Craftr DSL transpiler can generate code that looks up, sets and deletes keys using subscript syntax on a particular variable name.

Using the craftr.dsl.runtime package, you can configure the transpiler and runtime to use dynamic name resolution. Example usage:

from craftr.dsl.transpiler import transpile_to_ast
from craftr.dsl.runtime import Closure

class Project:
  def task(self, name: str, *, do: callable): ...

code = ...
filename = ...

# Long form:
module = transpile_to_ast(code, filename, Closure.get_options())
code = compile(module, filename, 'exec')
scope = {'__closure__': Closure(None, None, Project())}
exec(code, scope)

# Shorthand form:
Closure(None, None, Project()).run_code(code, filename)

The Closure.get_options() function returns TranspileOptions that instruct the transpiler to convert name lookups into subscripts on the __closure__ variable, add a @__closure__.child decoration before every closure function definition and to add a __closure__, argument to their arglist. The Closure object passed into the scope on execution deals with the rest.

Craftr DSLPython
task "foobar" do: {
  return n_times
}

task "belzebub" do: {
  def n_times = 1
  return n_times
}

task "cheeky" do: {
  def n_times = 1
  return (() -> n_times )()
}
@__closure__.child
def _closure_1(__closure__, self, *arguments, **kwarguments):
    return __closure__['n_times']
__closure__['task']('foobar', do=_closure_1)

@__closure__.child
def _closure_2(__closure__, self, *arguments, **kwarguments):
    n_times = 1
    return n_times
__closure__['task']('belzebub', do=_closure_2)

@__closure__.child
def _closure_3(__closure__, self, *arguments, **kwarguments):
    n_times = 1
    @__closure__.child
    def _closure_3_closure_3(__closure__):
        return n_times
    return _closure_3_closure_3()
__closure__['task']('cheeky', do=_closure_3)

Limitations

Craftr DSL is intended to behave as a complete syntactic superset of standard Python. However there are currently some limitations, namely:

  • Literal sets cannot be expressed due to the grammar conflict with parameter-less closures
  • Type annotations are not currently supported
  • The walrus operator is not currently supported
  • Function calls without parenthesis do not support passing *args as the first argument as that is interpreted as a multiplication expression.

Copyright © 2021 Niklas Rosenstein

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

craftr-dsl-0.6.0.tar.gz (19.6 kB view details)

Uploaded Source

Built Distribution

craftr_dsl-0.6.0-py3-none-any.whl (17.9 kB view details)

Uploaded Python 3

File details

Details for the file craftr-dsl-0.6.0.tar.gz.

File metadata

  • Download URL: craftr-dsl-0.6.0.tar.gz
  • Upload date:
  • Size: 19.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/3.4.2 importlib_metadata/4.6.3 pkginfo/1.7.1 requests/2.26.0 requests-toolbelt/0.9.1 tqdm/4.62.0 CPython/3.8.10

File hashes

Hashes for craftr-dsl-0.6.0.tar.gz
Algorithm Hash digest
SHA256 c51faafc938e7e1b1a874133d102851e537085e1aa75877a31bb16b39ed12cbe
MD5 bb025a2a62d9aaa567738a743b499263
BLAKE2b-256 49cef01c44a65a29bd85ae360472addd3a6b4ef748a5648eae1f5f9e0c458c30

See more details on using hashes here.

File details

Details for the file craftr_dsl-0.6.0-py3-none-any.whl.

File metadata

  • Download URL: craftr_dsl-0.6.0-py3-none-any.whl
  • Upload date:
  • Size: 17.9 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/3.4.2 importlib_metadata/4.6.3 pkginfo/1.7.1 requests/2.26.0 requests-toolbelt/0.9.1 tqdm/4.62.0 CPython/3.8.10

File hashes

Hashes for craftr_dsl-0.6.0-py3-none-any.whl
Algorithm Hash digest
SHA256 ee6ccba93790ca337bbcf5728114465c4c177e66ca4e4b1bde8ec1d0946f6dde
MD5 a7c7c87066ad560fcb78fec59c8b5618
BLAKE2b-256 68f4f67d7a49267992b9ab75eabe595dc91559720066ca334aed869c181c8223

See more details on using hashes here.

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page