small-bones

A pure python example language using the bones syntax


Keywords
bones, example, smalltalk
License
Apache-2.0
Install
pip install small-bones==0.1.2

Documentation

BONES

Bones is a minimalist functional language syntax - primarily influenced by Smalltalk, Python, q/kdb and Rebecca Wirfs-Brook's Responsibility Driven Design.

The syntax defines expressions, modules, functions, lists ((...) for immediate and [...] for deferred evaluation), basic literals (int, float, time, string, symbol, table, etc), names and name assignment, scopes (including access), struct (r/o) and repository (r/w) access, line and block comments, sections (for embedding) and types and type tagging. The only statements are python style imports.

The syntax is read left to right and is \n and ; separated with \ for line continuation.

Functions may be called using a piping syntax where " " (<space>) is used instead of the more familiar|> or >> etc.

Names may refer to structs or functions.

Names that refer to functions are called verbs and come in nullary, unary and binary flavours depending on how they interact with the piping syntax. Unary verbs - the most common - can take one piped argument, binaries two (most operators are binary) and nullaries can not be piped.

Partial functions may be created by passing less arguments than required for execution. Application of lists to verbs take precedence to piping - so piped argument are added to the end of the argument list. This style is slightly more uniform and flexible than the other common convertion of using piping to substitute the first argument. For example:

fred: {[a,b,c,d]...} <:unary:>       // define unary verb fred with arguments a, b, c & d
partialFred: fred("A", "B")          // partialFred is now fred["A", "B", c, d]
partialFred: partialFred(,"D")       // partialFred is now fred["A", "B", c, "D"]
result: "C" partialFred              // get the result

Implementations of verbs may be provided by external libraries - infact without at least some verbs being implemented externally bones can do very little other than return literals. AST fragments may be passed to and evaluated by external functions. This allows a library to implement flow control by selectively evaluating (or re-evaluating) those fragments.

Example, to implement the if verb,

answer: b > 5 if("greater than five", "not greater than five")

a Python library may export a function implementing if as a unary verb:

@export('if<:unary:>')
def myIfImp(trueAst, falseAst, conditionAst):
    # usual calling convention is:
    # <condition expr> if(<true expr>, <false expr>)
    if conditionAst.value:
        return trueAst.value
    else:
        return falseAst.value

Example, to implement ifTrue:ifFalse (i.e. Smalltalk keyword style which is implicitly unary),

 b > 5 ifTrue: "greater than five" ifFalse: "not greater than five" 

or

 b > 5 \
    ifTrue: "greater than five" \
    ifFalse: "not greater than five" 

in bones:

ifTrue:ifFalse::{[cond, trueExpr, falseExpr]
    cond value if[trueExpr, falseExpr]
}

or, in Python::

@bones.export('ifTrue:ifFalse:')
def _if(conditionAst, trueAst, falseAst):
    if conditionAst.value:
        return trueAst.value
    else:
        return falseAst.value

Fold, here,

(1,2,3) fold(0) {(prior, each) prior + each}

could be implemented:

@bones.export('fold<:binary:>')
def foldImpl(seed, iter, fn):
    answer = seee
    for e in iter:
        answer = fn(answer, e)
    retun answer

Sum then can be defined in bones as,

sum: fold(0,,{(prior, each) prior + each}) <:unary:>
(1,2,3) sum

Some basic pragmatics - type system, variable contexts and closures and symbol handling, etc - are provided, as well as an example standard library written in bones and Python.