sketchdance
Moonshine is a strongly typed text-based language intended to be represented well and completely by blocks, to be implemented easily in JavaScript, but compilable to native code efficiently. It is one piece of a project that includes Tardigrade (the block representation), Bootleg (the research OS), Moss Piglet (the UI), and Waterbear (that ties these all together).
The Scratch web forum uses "scratchblocks" to display "blocks" in forum messages.
say [Hello World!] for (2) seconds
Will create a step block with 2 inputs, one for text and one for numbers.
You can simulate drop-down lists by appending v
to an input:
replace item (42v) of [Numbersv] with [The answer!]
And putting a hexcode in square brackets will show a numeric input
set pen color to [#ff0000]
Predicates use angle brackets:
if <((2) + (2)) = [5]> then
say [Freedom is slavery.]
else
say [Peace is war.]
end
Reporters use (overload) parentheses
wait ([sin v] of (90)) secs
Variables also use parenthesis
change y by (gravity)
this technique has a lot of built-in knowledge about Scratch, so that inserting symbols has several different shortcuts, and it seems to know which names correspond to "C-blocks" (contexts and triggers in WB). The end
keyword marks the end of containment in a C-block, but there is no marker for the beginning of containment besides the name of the block and knowing whether or not that name is a container.
Up until now it has only been using existing block names and the block type and colour have been inferred. For custom blocks it gets more interesting.
Scratch has 11 normal categories of block, along with "grey", "extension", "custom-arg", and "obsolete". To colour a block, you append :: category
set [var v] to (1) // normal
set [var v] to (1) :: motion // as a motion block
You can use any of the scratch shapes (stack (step), hat (trigger), ring (N/A), reporter (value), boolean (value), cap (no slot for a following block)).
show :: hat
And C-blocks use curly braces to show containment
run script {
} without screen refresh :: control
You can show blocks inside other blocks
this is (a block :: sensing stack) :: custom-arg stack
So, what can I learn from this for Moonshine?
[Type Array(String[3])::Number]
would mean: There is a type here which takes Arrays of Strings, the arrays are of length 3, and it returns a number. I don't know, maybe some things could be typed by the first argument you drag to them (like I did for earlier Waterbear) or maybe there is a dropdown to customize it first. Explicit is better, although the dropdown may get pretty complicated and require a pop-up for specifying the type in all its gory details (especially if we want to apply constraints like "An integer between 1 and 6").returns
, i.e., a point can be used as a point, or you can grab just the x
from it and use that.What I don't want is for the syntax to be sub-optimal because I'm not good enough at writing parsers!
NOTE (2021-01-04): The syntax of Moonshine should be less influenced by Javascript and more by the structure of blocks. It should be as much as possible a literal text transcription of the blocks.
Right now I have enough of Moonshine working in the parser to generate steps (and blocks from steps) and make function calls. Having written a fair bit of Moonshine now to define blocks in Waterbear, I am not super happy with it so far. Below is an example.
sprite{
// Update parser to accept list notation Image[]
spriteFromImages:Sprite = (images:Image) => {
Sprite.spriteFromImages(images)
}
copy:Sprite = (sprite:Sprite) => {
sprite.copy()
}
accelerateBy:Sprite = (sprite:Sprite, vector:Vector) => {
sprite.accelerateBy(vector)
}
setVelocityTo:Sprite = (sprite:Sprite, vector:Vector) => {
sprite.setVelocityTo(vector)
}
getVelocity:Vector = (sprite:Sprite) => {
sprite.getVelocity()
}
}
As you can see, it is too limited (no way to specify an array of Sprites), too verbose (a lot of repetitive syntax just to call into a JavaScript library), and too crunchy (what purpose do the "=" and "=>" signs serve besides imitating JavaScript?). It also does not cover the requirements for other blocks.
Proposed syntax (updated 2020-12-22):
namespace{
defs{
/* values used by blocks, but not themselves exposed, mainly lists for
select arguments */
name:Type <= value
}
// step (most common block, least extra syntax)
name:returnType["optionalReturnTypeName"](param:Type[=optionalDefault],param:Type[=optionalDefault]){
body
}
// trigger, async, so no return type, no parameters
trigger name(){
body
}
// return type of a context is the return type of its last step as an array
// or can it vary more than that?
// or should context have a return type at all?
// for now, no return type, let's see how that works
// context
// was: name["optionReturnTypeName"](param:Type[=optionalDefault]){
context name(param:Type, param:Type){
locals{
/* Just like value blocks */
name:Type <= value
name:Type <= value
}
body
}
// value
name:Type <= value
}
I think this captures the direct needs of Moonshine. Some things will get bumped to the Waterbear level: Not allowing names to be shadowed, generating lists of types (all directly defined types, lists of all directly defined types, lists of lists, blocks of various signatures), mutating return types. Some of this may move back down into Moonshine, like having variables declared in the Setup or Preload blocks be available to all other blocks, but it's a Waterbear-level thing to automatically include URLs in the Preload block when referenced elsewhere in the script, unless we're using Scene blocks (then each Scene can have its own Preload).
I still need to address how repetitive & verbose the body is, but maybe I can actually write some WB scripts first.
There also may be a higher level of objects than Sprites. Something like the avatars in RenPy, or Storytelling Alice / Looking Glass with sprites that have built-in social interactions. Ken Perlin was doing something with this too. This is probably a "for later" thing.
Current development happening on Glitch: https://glitch.com/edit/#!/waterbear
{
namespace: {type: 'namespace', name, values: []},
literalValues: [Integer, Float#, Boolean, String, Array, Dict, Set, Vector],
function: {type: 'function', name, params: ['0 to 3 typed names'], returnType},
functionCall: {type: 'functionCall', namespace, name, args},
comment: {type: 'comment', value},
array: {type: 'array', value: []},
dict: {type: 'dict', value: {}},
string: {type: 'string', value: ''},
boolean: {type: 'boolean', value: true || false},
integer: {type: 'integer', value: 0},
float: {type: 'float', value: 0.0},
keyedValue: {type: 'keyedValue', object, key},
indexedValue: {type: 'indexedValue', object, index},
typedName: {name, type},
}
Yesterday I finally gave in and wrote a parser for the language that will become Moonshine. There are things I want (types, guards) that aren't supported by a pure subset of JS, and neither Lisp nor Forth felt like the right syntax for me, especially since neither carry type information.
Keeping it simple (and fairly verbose) for now, will work on shortcuts later, maybe.
Some notes from my journal
Somewhere between a MUD and a code editor: Garden of Waterbears.
I've been struggling with whether to define Moonshine as JSON, a subset of JavaScript, or it's own format that I would have to parse. Part of the problem may be that I'm conflating two very different things: the definition of blocks (needing to specify arguments, types, etc.) and the specification of scripts (which assemble the blocks, but can also create new blocks, so there is legitimate overlap).
Since a block needs a lot of context, it can get more verbose. I need to specify:
Whereas for most scripts what I need is
I have probably also been overthinking using immutable state to drive building the UI and how that will play with transient state (dragging, open/closed, scrolling, resizing). It is fine to keep transient state in the DOM, that's where it belongs. Some things (resizing workspace) can be saved to localStorage. It is also fine to have different immutable state roots for different things: the block menu is essentially a separate app from the script blocks, and that's OK.
Going to try putting this into practice RSN.
What this could look like for block definitions:
// Multimethod
// Commutative: order doesn't matter, only works with two arguments for now
Method('add').commut(isNumber, isNumber, (a,b) => a+b, 'number');
// Non-commutative, order is important
Method('add').when('vector', 'number', (a,b) => new Vector(a.x+b, a.y+b));
Method('add').when('array', 'array', (a,b) => [a…, b…]);
Method('add').when('array', 'any', (a,b) => [a…, b]);
OK, the more I think about it the more I dislike Multimethods in practice, are least for Waterbear. It is easy enough to name add_vector_number(a,b) to look like "Add" in the block (in a way that can be localized), but for writing scripts, and for human disambiguation it is better to have them namespaced or just named significantly.
i.e.,
Vector.prototype.addNumber(x)
or
Vector.add_Vector_Number(v,x)
are both preferable to
add(v:Vector, x:Number):Vector
because when code uses them you can see what is going on, and parsed code is easier to disambiguate. Of the two, I think I like the class syntax vs. the namespace syntax because it is shorter, keeping a balance between conciseness and clarity. But it does rely on this
and doesn't map to blocks well, so back to namespaced? Maybe
Vector.addNumber(v,x)
where Vector
is a namespace, not a class (or a class acting as a namespace), but like a Python class, all namespaced function expect the named type as first argument.
In general, if functions return different things, they should be different blocks.
Which opens up a can of worms around loopOver(). Do I keep it as-is and loop over integers, objects, and arrays, or do I create three loop blocks? I think for maximum clarity, three loop blocks is the way to go.
Forth might be a better model than LISP
(historical, I'm not 100% sure what I meant by this)
Just like JavaScript String Templates can be tagged to be run through the named function, I think something like tagged JSON would be useful to instantiate as an instance of the named object. The main question is: should it be valid JSON by incorporating the tag as part of the structure, or should it be external like the functions in String Templates?
While I would really like to move the tag outside of JSON for Moonshine, I think keeping JSON parseable by other tools and validators is probably more valuable than that nicety.
"in most Smalltalk environments, the execution stack is a first-class citizen, coroutines can be implemented without additional library or VM support."
AFAICT Scratch uses the Actor model for concurrency and limits nearly all actor interactions to message broadcasts.
How compatible are all of these mechanisms with immutable state? And how well will any of this work with WebWorkers?
All built-ins should have attributes:
So, for now let's keep it in JS (or TS).
fork()
Create a processexit()
Terminate the current processwait()
Wait for a child process to exitkill(pid)
Terminate process pidgetpid()
Return the current process’s pidsleep(n)
Sleep for n clock ticksexec(filename, *argv)
Load a file and execute itsbrk(n)
Grow process’s memory by n bytesopen(filename, flags)
Open a file; the flags indicate read/writeread(fd, buf, n)
Read n bytes from an open file into bufwrite(fd, buf, n)
Write n bytes to an open fileclose(fd)
Release open file fddup(fd)
Duplicate fdpipe(p)
Create a pipe and return fd’s in pchdir(dirname)
Change the current directorymkdir(dirname)
Create a new directorymknod(name, major, minor)
Create a device filefstat(fd)
Return info about an open filelink(f1, f2)
Create another name (f2) for the file f1unlink(filename)
Remove a fileCtrl-M
. Edit this file to create any kind of launcher
Ctrl-F1
to get a list of completable words including filenames, symbolnames, etc. All source code is indexed and you can jump to any function from anywhere, even in the shell
Uf('foo')
will disassemble a function and each symbol will be hyperlinked to source
Type()
displays files like cat
, respecting hypertext and displaying graphicsDir
gives directory listing, click on any folder to go to itCtrl-T
gives raw text source. Can contain text, images, 3D meshes, macros (system commands that run on click). Similar to Oberon. This replaces HTML, JSON, XML, shell scripts, sourcefiles, text filesmain()
. Anything at top-level scope is executed as it runs through compiler#exe
compiler directive to shell out to external commands and include their output in source code while compilinglast class
keyword as default argument. Provides name of previous argument's type as a stringF5
in editor to JIT-compile and runDbg()
or use Alt-Ctrl-D
to get a debugger which still runs as a full REPLMoonshine
A language based on Javascript
Moonshine is a subset of Javascript ES6 plus libraries, Sweet.js macros, and a coding style based on functional programming.
From ES6
Arrow functions
Block scope (let, const, functions)
Classes
Default parameters
Destructuring
For-of loops (iterators)
Generators
Modules
Rest parameters
Spread operators
Template strings
New methods on String and Object
Enhanced object literals
Comprehensions
Map, set, weakmap, weakset
Proxies
Symbols
Subclassable built-ins
Promises
New methods for Math and Number
Binary and octal literals
Tail call optimization
From Sweet.js
macros
syntax for other new features
Other libraries as “built-ins”
P5.js or similar
Mori.js for immutable structures
document-register-element.js / DOM4.js
localForage for local persistence
hoodie or firebase for remote persistence
csp.js for go-like channels
GA game library? https://github.com/kittykatattack/ga
Still need:
A better wrapper for canvas/SVG (MathBox2? Pixi.js?)
A low-level abstraction of CSS
A wrapper or alternative to Regex. Candidates: re-build (https://github.com/MaxArt2501/re-build), apg-exp (https://www.sitepoint.com/alternative-to-regular-expressions/), OMeta.
Modules, library loading
UI library
2016-08-07 Thinking
Keep it minimal. What can we take away from JavaScript?
Arrays
Objects?
Optional syntax
Assignment
Statements? (only expressions)
Keep
Number
Strings
Arrow function expressions
Some or all of standard library
Time to put some code down:
Define the starting syntax
Pull in some libraries
Write some examples, starting with syntax validation
Syntax:
list -> value [, list]
number -> (this is actually fairly complex)
" -> *characters + "
' -> *characters + ' // do we need both?
([val [,val]) => expression
tuple -> (val [,'val'])
name + tuple -> function call
APL Summary:
Everything is in arrays
Data can be strings or characters
A single item is a scalar
A one-dimensional array is a vector
A two-dimensional array is a matrix
Higher dimensional arrays are just arrays
Each data item in an array can be a character, a number, or an array
Arrays which contain both numbers and characters are “mixed arrays”
Arrays which contain other arrays are “nested arrays”
Some versions of APL support complex numbers
Some versions of APL support collectionss of data and/or functions called Overlays
In object-oriented APL a data item can be either a class reference or an object reference
Data items can be given names, in which case they are variables
There are about fifty built-in operators and function, each is one character. Most can be called with either one or two arguments, providing different (but related) operations.
System variables and functions are an extension to the built-in set and always start with the quad “⎕” character to distinguish them
System commands are also extensions, mostly for working with the workspace (REPL) and start with ")". While usually typed directly, they can be used in scripts as well.
Functions can take 0, 1, or 2 arguments. Functions always take data as arguments. Functions act on whole arrays without need for explicity looping.
Operators are like functions, but their arguments (operands) can themselves be functions.
Assignment operator ←
Names for variables: APL uses upper-case and lower-case characters. Many APLs also allow the symbols Delta (∆), underlined Delta (⍙), the underline (_), and the high minus (¯)
⍴ (rho) is the shape operator
⍴ x => returns shape of x (number of rows and columns)
2 3 ⍴ 1 2 3 4 5 6 => returns matrix of 2 rows and 3 columns: [[1,2,3],[4,5,6]]
6 ⍴ 9 => single row containing 6 columns, all filled with 9: [9,9,9,9,9,9,]
⍴ can be used to create single-value vectors 22 is a scalar but 1 ⍴ 22 is a vector containing a single data value
Square brackets are used for indexing
Multi-dimensional arrays are indexed with multiple numbers x[1;3;6]
Multiple indexes from a single axis can be selected: x[1;1 2 3]
For all values in an axis, leave it blank: x[1;] (row) or x[;2] (column)
Attributes of code (objects) generally take the following forms (from Doug Lea):
instance variable
direct
indirect
repository to query
property list
derived
Categories of attributes:
Value representation
Aquaintance
History
Logical state / Role variables
Execution state
Cached representation