# Copyright (C) 2015 Sam Parkinson
|
|
#
|
|
# This program is free software; you can redistribute it and/or modify
|
|
# it under the terms of the GNU General Public License as published by
|
|
# the Free Software Foundation; either version 3 of the License, or
|
|
# (at your option) any later version.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
import re
|
|
import sys
|
|
import json
|
|
|
|
HELP = '''Usage:
|
|
|
|
python pluginify.py (file)
|
|
|
|
or
|
|
|
|
python pluginify.py (file) > plugin.json
|
|
|
|
Converts an RTP (readable TurtleBlocks plugin) file into a JSON file
|
|
to load into Turtle Blocks JS. For more information, run
|
|
`python pluginify.py syntax`
|
|
'''
|
|
|
|
SYNTAX = '''
|
|
|
|
In an RST file, new blocks are not defined with braces (as is typical of
|
|
Javascript); rather they start whenever you add //* *// and their scope is
|
|
until the next block starts (or the end of file in the case that it is
|
|
the last block).
|
|
|
|
To add a comment, just type //* comment *// followed by your multi-
|
|
line comment. pluginify ignores comments, i.e., they are not added in
|
|
the JSON plugin.
|
|
|
|
Example:
|
|
//* comment *// Your single or multi line comment here...
|
|
|
|
Global variables are defined in a section //* globals *//.
|
|
|
|
Example:
|
|
//* globals *//
|
|
var calories = 0;
|
|
var protein = 0;
|
|
var carbohydrate = 0;
|
|
var fiber = 0;
|
|
var fat = 0;
|
|
|
|
Define all the global variables under the section
|
|
//* block-globals *//.
|
|
|
|
These definitions will be added to all the code blocks in the created
|
|
JSON output. Note that these "globals" included in each block but are
|
|
local in context to each block.
|
|
|
|
Example: You can define a common API Key to be used by all blocks.
|
|
//* block-globals *//
|
|
var mashapeKey = '(keycode)'; in globals.
|
|
|
|
You can also declare global variables specific to argument blocks that
|
|
get applied to a set of similar blocks, e.g., the variables under
|
|
//* arg-globals *// will be added to all the arg blocks.
|
|
|
|
Example:
|
|
//* arg-globals *//
|
|
var block = blocks.blockList[blk];
|
|
var connections = block.connections;
|
|
|
|
You can declare functions for parameter blocks to be evaluated when
|
|
the block labels are updated with the //* parameter:(blockname) *//
|
|
tag.
|
|
|
|
Example:
|
|
//* parameter:loudness *//
|
|
if (mic == null) {errorMsg("The microphone is not available.");
|
|
value = 0;
|
|
} else {
|
|
value = Math.round(mic.getLevel() * 1000);
|
|
}
|
|
|
|
To define a block you need to type: //* block:(blockname) *//
|
|
|
|
Example:
|
|
//* block:power *//
|
|
var block = new ProtoBlock('power');
|
|
block.palette = palettes.dict['maths'];
|
|
blocks.protoBlockDict['power'] = block;
|
|
block.twoArgMathBlock();
|
|
block.defaults.push(10, 2);
|
|
block.staticLabels.push('power', 'base', 'exp.');
|
|
|
|
You should also define a setter for a parameter block (if appropriate).
|
|
You can do this using the setter setion:
|
|
|
|
//* setter:myValue *//
|
|
myValue = value;
|
|
updateDisplayOfMyValue();
|
|
|
|
Graphical elements (icons, colors) are defined in the own sections:
|
|
|
|
Palette icons are defined as //* palette-icon:(palette name) *//
|
|
|
|
Example:
|
|
//* palette-icon:food *//
|
|
<svg ...> ... </svg>
|
|
|
|
Similarly for block colors:
|
|
|
|
Example:
|
|
//* palette-fill:food *// #FFFFFF
|
|
//* palette-stroke:food *// #A0A0A0
|
|
//* palette-highlight:food *// #D5D5D5
|
|
|
|
Plugins can specify code to be executed on load, on start, and on stop.
|
|
Example:
|
|
//* onload:foo *//
|
|
your code here...
|
|
|
|
NOTE: name of on load, on start, and on stop sections must match the
|
|
name of one of the plugin blocks.
|
|
'''
|
|
|
|
|
|
def clear():
|
|
global NAMES, JS_TYPES, IMAGES
|
|
NAMES = {
|
|
'flow': 'FLOWPLUGINS',
|
|
'arg': 'ARGPLUGINS',
|
|
'block': 'BLOCKPLUGINS',
|
|
'parameter': 'PARAMETERPLUGINS',
|
|
'setter': 'SETTERPLUGINS',
|
|
'onload': 'ONLOAD',
|
|
'onstart': 'ONSTART',
|
|
'onstop': 'ONSTOP',
|
|
'palette-icon': 'PALETTEPLUGINS',
|
|
'palette-fill': 'PALETTEFILLCOLORS',
|
|
'palette-stroke': 'PALETTESTROKECOLORS',
|
|
'palette-highlight': 'PALETTEHIGHLIGHTCOLORS',
|
|
'palette-stroke-highlight': 'HIGHLIGHTSTROKECOLORS'}
|
|
JS_TYPES = ('flow', 'arg', 'block', 'parameter', 'setter', 'onload', 'onstart', 'onstop')
|
|
# 'blkName': 'imageData',
|
|
IMAGES = []
|
|
|
|
|
|
def pluginify(data):
|
|
clear()
|
|
sections_list = data.split('//*')
|
|
sections_pairs = []
|
|
specific_globals = {x: '' for x in JS_TYPES}
|
|
globals_ = None
|
|
for section in sections_list:
|
|
match = re.match('(.*)\*\/\/([^\0]*)', section.strip())
|
|
if match:
|
|
if match.group(1).strip() == 'globals':
|
|
globals_ = match.group(2).strip()
|
|
elif match.group(1).strip().endswith('-globals'):
|
|
type_, _ = match.group(1).strip().split('-')
|
|
specific_globals[type_] = specific_globals[type_] + \
|
|
match.group(2).strip()
|
|
elif match.group(1).strip() == 'comment':
|
|
continue
|
|
else:
|
|
sections_pairs.append((match.group(1).strip(),
|
|
match.group(2).strip()))
|
|
|
|
outp = {}
|
|
if globals_ is not None:
|
|
outp['GLOBALS'] = globals_.replace('\n', '').replace('var ', '')
|
|
|
|
for key, value in sections_pairs:
|
|
if len(key.split(':')) != 2:
|
|
raise ValueError('Section names should have 1 colon (type:name)')
|
|
type_, name = key.split(':')
|
|
|
|
if type_ in JS_TYPES:
|
|
value = specific_globals[type_] + value
|
|
value = value.replace('\n', '')
|
|
|
|
if type_ in NAMES:
|
|
type_ = NAMES[type_]
|
|
if type_ not in outp:
|
|
outp[type_] = []
|
|
outp[type_].append([name,value])
|
|
|
|
if type_ == 'image':
|
|
# TODO: Detect if its png
|
|
IMAGES.append([name, 'data:image/svg+xml;utf8,' + value])
|
|
|
|
if IMAGES:
|
|
outp['IMAGES'] = IMAGES
|
|
|
|
return json.dumps(outp, indent=4)
|
|
|
|
if __name__ == '__main__':
|
|
if len(sys.argv) != 2:
|
|
print HELP
|
|
elif sys.argv[1] in ('help', '-h', '--help'):
|
|
print HELP
|
|
elif sys.argv[1] == 'syntax':
|
|
print SYNTAX
|
|
else:
|
|
with open(sys.argv[1]) as f:
|
|
data = f.read()
|
|
print pluginify(data)
|