diff --git a/carp/src/Pipfile b/carp/src/Pipfile new file mode 100644 index 0000000..ebea21b --- /dev/null +++ b/carp/src/Pipfile @@ -0,0 +1,14 @@ +[[source]] +name = "pypi" +url = "https://pypi.org/simple" +verify_ssl = true + +[dev-packages] + +[packages] +flask = "*" +requests = "*" +msgpack = "*" + +[requires] +python_version = "3" diff --git a/carp/src/bridge/__init__.py b/carp/src/bridge/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/carp/src/bridge/bridge.py b/carp/src/bridge/bridge.py new file mode 100644 index 0000000..0843a27 --- /dev/null +++ b/carp/src/bridge/bridge.py @@ -0,0 +1,178 @@ +import argparse +import threading +import sys +import hooks +from bridge.hooks import * +from bridge.object_registry import registry + +def pbbreak(): + print("Breaking now") + breakpoint() + print("Continuing") + + +class EvalCommand: + statements = "" + bindings = {} + commandId = 0 + + def __init__(self, commandId, statements, bindings): + self.statements = statements + self.commandId = commandId + self.bindings = bindings + + def execute_using_env(self, env): + try: + self.execute() + except Exception as err: + self.perform_proceed_action(notify_error(err,self)) + + def perform_proceed_action(self, actionDict): + actionSymbol = actionDict['action'] + if actionSymbol == "IGNORE": + pass + if actionSymbol == "DROP_QUEUE": + bridge.globals.globalCommandList.drop_queue() + if actionSymbol == "REPLACE_COMMAND": + commandDict = actionDict["command"] + bridge.globals.globalCommandList.push_command_at_first(EvalCommand( + commandDict["commandId"], + commandDict["statements"], + commandDict["bindings"])) + + def command_id(self): + return self.commandId + + def execute(self): + bridge.globals.proc.evaluate(self.statements) + +class Logger(): + def log(self, msg): + print(str(msg), file=sys.stderr, flush=True) + +class NoLogger(): + def log(self, msg): + pass + +# This List is thought to be multi-producer and single-consumer. For optimal results wait for push_command return value to push another command that depends on the previous one. +class CommandList: + currentCommandIndex = 0 + commandList = [] + listLock = threading.Lock() + consumeSemaphore = threading.Semaphore(value=0) + + # This method locks the thread until the command has been succesfully appended to the list. Even though that it has a lock inside, we do not expect long waiting time. + def push_command(self, aCommand): + self.listLock.acquire() + self.commandList.append(aCommand) + commandIndex = len(self.commandList) - 1 + self.listLock.release() + self.consumeSemaphore.release() + return commandIndex + + def push_command_at_first(self, aCommand): + self.listLock.acquire() + self.commandList.insert(self.currentCommandIndex, aCommand) + self.listLock.release() + self.consumeSemaphore.release() + return self.currentCommandIndex + + def drop_queue(self): + self.listLock.acquire() + self.consumeSemaphore = threading.Semaphore(value=0) + self.currentCommandIndex = len(self.commandList) + self.listLock.release() + + def consume_command(self): + repeatMonitorFlag = True + while repeatMonitorFlag: + self.consumeSemaphore.acquire() + self.listLock.acquire() + repeatMonitorFlag = False + if(self.currentCommandIndex >= len(self.commandList)): + repeatMonitorFlag = True + self.listLock.release() + command = self.commandList[self.currentCommandIndex] + self.currentCommandIndex += 1 + self.listLock.release() + return command + + def get_current_command(self): + if self.currentCommandIndex == 0: + return None + self.listLock.acquire() + command = self.commandList[self.currentCommandIndex-1] + self.listLock.release() + return command + + def get_command_list(self): + self.listLock.acquire() + listCopy = self.commandList.copy() + self.listLock.release() + return listCopy + +#### UTILS FUNCTIONS +def clean_locals_env(): + return locals() + +def deserialize(text): + result = bridge.globals.msg_service.serializer.deserialize(text) + bridge.globals.logger.log("DESERIALISE (bridge): " + str(result)) + if registry().isProxy(result): + result = registry().resolve(result['__pyid__']) + return result + +def enqueue_command(data): + bridge.globals.globalCommandList.push_command(EvalCommand( + data["commandId"], + data["statements"], + {k: deserialize(v) for k, v in data["bindings"].items()})) + +def run_bridge(): + ap = argparse.ArgumentParser() + ap.add_argument("-p", "--port", required=False, + help="port to be used for receiving instructions") + ap.add_argument("-o", "--pharo", required=True, + help="port to be used for sending notifications back to pharo") + ap.add_argument("-m", "--method", required=False, + help="identifier for communication protocol strategy http or msgpack") + ap.add_argument("--log", required=False, const=True, nargs="?", + help="enable logging") + args = vars(ap.parse_args()) + + bridge.globals.pharoPort = args["pharo"] + if args["log"]: + bridge.globals.logger = Logger() + else: + bridge.globals.logger = NoLogger() + bridge.globals.pyPort = args["port"] + bridge.globals.globalCommandList = CommandList() + globalCommandList = bridge.globals.globalCommandList + bridge.globals.proc = carp.start_carp_proc() + env = clean_locals_env() + msg_service = None + if args["port"] == None: + args["port"] = '0' + if args["method"] == None: + args["method"] = 'http' + if args["method"] == 'http': + from bridge import flask_platform + msg_service = flask_platform.build_service(int(args["port"]), int(args["pharo"]), enqueue_command) + elif args["method"] == 'msgpack': + from bridge import msgpack_socket_platform + msg_service = msgpack_socket_platform.build_service(int(args["port"]), int(args["pharo"]), enqueue_command) + else: + raise Exception("Invalid communication strategy.") + bridge.globals.msg_service = msg_service + msg_service.start() + bridge.globals.logger.log("PYTHON: Start consuming commands") + while True: + command = globalCommandList.consume_command() + bridge.globals.logger.log("PYTHON: Executing command " + command.command_id()) + bridge.globals.logger.log("PYTHON: bindings: " + str(command.bindings)) + bridge.globals.logger.log("PYTHON: " + command.statements) + command.execute_using_env(env) + bridge.globals.logger.log("PYTHON: Finished command execution") + +if __name__ == "__main__": + run_bridge() diff --git a/carp/src/bridge/carp.py b/carp/src/bridge/carp.py new file mode 100644 index 0000000..3bff86e --- /dev/null +++ b/carp/src/bridge/carp.py @@ -0,0 +1,13 @@ +import subprocess + + +class CarpProc: + def __init__(self): + self.proc = subprocess.Popen(['carp']) + + def evaluate(self, statements): + self.proc.stdin.send(statements) + + +def start_carp_proc(): + return CarpProc() diff --git a/carp/src/bridge/flask_platform.py b/carp/src/bridge/flask_platform.py new file mode 100644 index 0000000..45b29a1 --- /dev/null +++ b/carp/src/bridge/flask_platform.py @@ -0,0 +1,73 @@ +from flask import Flask, request +import http.client +import json +import threading +import bridge.globals +import bridge.utils +from bridge import json_encoder +import sys +import logging +import requests + +class FlaskMsgService: + + def __init__(self, port, pharo_port, feed_callback): + self.serializer = json_encoder.JsonSerializer() + log = logging.getLogger('werkzeug') + log.setLevel(logging.ERROR) + self.thread = None + self.port = port + self.pharo_port = pharo_port + self.feed_callback = feed_callback + self.app = Flask('carp_bridge') + self.app.use_reloader=False + self.session = requests.Session() + self.session.trust_env = True + + @self.app.route("/ENQUEUE", methods=["POST"]) + def eval_expression(): + data = request.get_json(force=True) + self.feed_callback(data) + return "{}" + + @self.app.route("/IS_ALIVE", methods=["POST"]) + def status_endpoint(): + return "{}" + + def addMapping(self, key_type, mapping_function): + json_encoder.addMapping(key_type, mapping_function) + + def _start(self): + try: + self.app.run(port=self.port) + except OSError as err: + bridge.globals.logger.log('Critical Error:' + str(err)) + exit(42) + + def start(self): + self.thread = threading.Thread(target=self._start, args=()) + self.thread.daemon = True + self.thread.start() + + def is_running(self): + return self.thread != None + + def stop(self): + pass + + def send_async_message(self, msg): + self.send_sync_message(msg) + + def send_sync_message(self, msg): + msg['__sync'] = bridge.utils.random_str() + bridge.globals.logger.log("SYNC_MSG: " + json.dumps(msg)) + response = self.session.post( + 'http://localhost:' + str(self.pharo_port) + '/' + msg['type'], + data=json.dumps(msg), + headers={'content-type': 'application/json'}, + allow_redirects=True).content.decode('utf-8') + bridge.globals.logger.log("SYNC_ANS: " + response) + return json.loads(response) + +def build_service(port, pharo_port, feed_callback): + return FlaskMsgService(port, pharo_port, feed_callback) diff --git a/carp/src/bridge/globals.py b/carp/src/bridge/globals.py new file mode 100644 index 0000000..e69de29 diff --git a/carp/src/bridge/hooks.py b/carp/src/bridge/hooks.py new file mode 100644 index 0000000..5663709 --- /dev/null +++ b/carp/src/bridge/hooks.py @@ -0,0 +1,50 @@ +from flask import Flask, request +import http.client +import argparse +import sys +import traceback +import bridge.globals + +def serialize(obj): + return bridge.globals.msg_service.serializer.serialize(obj) + +def deserialize(text): + return bridge.globals.msg_service.serializer.deserialize(text) + +def observer(commandId, observerId): + return lambda obj: notify_observer(obj, commandId, observerId) + +#### NOTIFICATION FUNCTIONS +def notify(obj, notificationId): + bridge.globals.logger.log("PYTHON: Notify " + str(notificationId)) + data = {} + data["type"] = "EVAL" + data["id"] = notificationId + data["value"] = serialize(obj) + bridge.globals.msg_service.send_async_message(data) + +def notify_observer(obj, commandId, observerId): + bridge.globals.logger.log("PYTHON: Notify observer " + str(commandId) + " " + str(observerId)) + data = {} + data["type"] = "CALLBACK" + data["commandId"] = commandId + data["observerId"] = observerId + data["value"] = serialize(obj) + rawValue = bridge.globals.msg_service.send_sync_message(data)['value'] + return deserialize(rawValue) + +def notify_error(ex, command): + bridge.globals.logger.log("Error on command: " + str(command.command_id())) + bridge.globals.logger.log("Exception: " + str(ex)) + data = {} + data["type"] = "ERR" + data["errMsg"] = str(ex) + data["trace"] = traceback.format_exc(100) + data["commandId"] = command.command_id() + return bridge.globals.msg_service.send_sync_message(data) + +def bridge_inspect(obj): + if hasattr(obj,'__dict__'): + return obj.__dict__ + else: + return {} diff --git a/carp/src/bridge/json_encoder.py b/carp/src/bridge/json_encoder.py new file mode 100644 index 0000000..18abd8f --- /dev/null +++ b/carp/src/bridge/json_encoder.py @@ -0,0 +1,28 @@ +import json +import io +from bridge.object_registry import registry + +mapper = {} + +def addMapping(key_type, mapping_function): + mapper[key_type] = mapping_function + +class JsonEncoder(json.JSONEncoder): + def __init__(self, *args, **kwargs): + json.JSONEncoder.__init__(self, *args, **kwargs) + self.mapper = mapper + + def default(self, obj): + if type(obj) in self.mapper: + return self.mapper[type(obj)](obj) + return { + '__pyclass__': type(obj).__name__, + '__pyid__': registry().register(obj) + } + +class JsonSerializer: + def serialize(self, obj): + return json.dumps(obj, cls=JsonEncoder) + + def deserialize(self, text): + return json.loads(text) diff --git a/carp/src/bridge/msgpack_serializer.py b/carp/src/bridge/msgpack_serializer.py new file mode 100644 index 0000000..11bea9f --- /dev/null +++ b/carp/src/bridge/msgpack_serializer.py @@ -0,0 +1,41 @@ +import msgpack +from bridge.object_registry import registry + +primitive_types = [ + type(None), + bool, + int, + bytes, + str, + dict, + list, + memoryview, + bytearray] + +mapper = {} + +def addMapping(key_type, mapping_function): + mapper[key_type] = mapping_function + +class MsgPackSerializer: + def __init__(self): + self.primitive_types = primitive_types + + def mapper(self): + return mapper + + def default(self, obj): + if type(obj) in self.primitive_types: + return obj + if type(obj) in self.mapper(): + return self.mapper()[type(obj)](obj) + return { + '__pyclass__': type(obj).__name__, + '__pyid__': registry().register(obj) + } + + def serialize(self, obj): + return msgpack.packb(obj, default=self.default, use_bin_type=True) + + def deserialize(self, binary): + return msgpack.unpackb(binary, raw=False) diff --git a/carp/src/bridge/msgpack_socket_platform.py b/carp/src/bridge/msgpack_socket_platform.py new file mode 100644 index 0000000..aa9f942 --- /dev/null +++ b/carp/src/bridge/msgpack_socket_platform.py @@ -0,0 +1,120 @@ +import msgpack +import socket +import _thread +import threading +import time +import sys +import bridge.globals +from bridge import stoppable_thread, msgpack_serializer +from uuid import uuid1 + +# Messages supported by this sockets must be Dictionaries. This is because we use special key __sync to know if it is +# a synchronized message or not. If it is we hook a semaphore to that id under the __sync key and after we receive the +# value we store there the return message and signal the semaphore. +class MsgPackSocketPlatform: + + def __init__(self, port): + self.port = port + self.client = None + self.serializer = msgpack_serializer.MsgPackSerializer() + self.unpacker = msgpack.Unpacker(raw=False) + self.packer = msgpack.Packer(use_bin_type=True) + self.sync_table = {} + self.async_handlers = {} + + def addMapping(self, key_type, mapping_function): + msgpack_serializer.addMapping(key_type, mapping_function) + + def set_handler(self, msg_type, async_handler): + self.async_handlers[msg_type] = async_handler + + def prim_handle(self): + try: + bridge.globals.logger.log("loop func") + data = self.client.recv(2048) + if len(data) == 0: + time.sleep(0.005) + else: + self.unpacker.feed(data) + for msg in self.unpacker: + bridge.globals.logger.log("prim handle message") + self.prim_handle_msg(msg) + except OSError: + bridge.globals.logger.log("OSError: " + str(err)) + self.stop() + sys.exit() + exit(-1) + except Exception as err: + bridge.globals.logger.log("ERROR message: " + str(err)) + + def setup_func(self): + self.client = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + self.client.connect(('localhost', self.port)) + + def stop(self): + if self.thread is not None: + self.thread.stop() + if self.client is not None: + self.client.close() + self.client = None + + def send_answer(self, msg, answer): + if answer['type'] != msg['type']: + raise Exception('Type mismatch') + answer['__sync'] = msg['__sync'] + self.send_async_message(answer) + + def is_running(self): + return self.client != None + + def prim_handle_msg(self, raw_msg): + msg = raw_msg + msg_type = msg['type'] + if msg_type in self.async_handlers: + self.async_handlers[msg['type']](msg) + elif is_sync_msg(msg): + sync_id = message_sync_id(msg) + semaphore = self.sync_table[sync_id] + self.sync_table[sync_id] = msg + semaphore.release() + else: + bridge.globals.logger.log("Error! Msg couldnt be handled") + raise Exception('Message couldn''t be handled') + + + def start(self): + self.thread = stoppable_thread.StoppableThread( + loop_func= self.prim_handle, + setup_func= self.setup_func) + self.thread.start() + time.sleep(.1) + + def send_async_message(self, msg): + self.client.send(self.packer.pack(msg)) + + def send_sync_message(self, msg): + sync_id = mark_message_as_sync(msg) + semaphore = threading.Semaphore(value=0) + self.sync_table[sync_id] = semaphore + self.send_async_message(msg) + semaphore.acquire() + ans = self.sync_table[sync_id] + del self.sync_table[sync_id] + return ans + +def is_sync_msg(msg): + return '__sync' in msg + +def message_sync_id(msg): + return msg['__sync'] + +def mark_message_as_sync(msg): + sync_id = uuid1().hex + msg['__sync'] = sync_id + return sync_id + +def build_service(port, pharo_port, feed_callback): + service = MsgPackSocketPlatform(pharo_port) + service.set_handler('ENQUEUE',feed_callback) + service.set_handler('IS_ALIVE', lambda msg: service.send_answer(msg, {'type': 'IS_ALIVE'})) + return service diff --git a/carp/src/bridge/object_registry.py b/carp/src/bridge/object_registry.py new file mode 100644 index 0000000..8734fee --- /dev/null +++ b/carp/src/bridge/object_registry.py @@ -0,0 +1,88 @@ +import bridge.globals +from uuid import uuid1 + +def ensure_global_registry(): + if not hasattr(bridge.globals, 'ObjectRegistry'): + bridge.globals.ObjectRegistry = Registry() + +def registry(): + return bridge.globals.ObjectRegistry + +primitive = (int, str, bool) + +def is_primitive(obj): + return isinstance(obj, primitive) + +class Registry(): + + def __init__(self): + self.idToObjMap = {} + self.objToIdMap = {} + + def hasId(self, anId): + return anId in self.idToObjMap + + def createNewObjId(self): + return uuid1().hex + + def register(self, obj): + if obj is None or is_primitive(obj): + return 0 + if id(obj) in self.objToIdMap: + return self.objToIdMap[id(obj)] + else: + return self._register(obj, self.createNewObjId()) + + def register_with_id(self, obj, newObjId): + if obj is None or is_primitive(obj): + return RegisterForbiddenObject(obj) + if id(obj) in self.objToIdMap: + objId = self.objToIdMap[id(obj)] + if objId == newObjId: + return newObjId + else: + raise RegisterWithDifferentIdError(obj, newObjId) + else: + return self._register(obj, newObjId) + + def resolve(self, objId): + if objId in self.idToObjMap: + return self.idToObjMap[objId] + else: + raise ResolveUnknownObject(objId) + + def _register(self, obj, newObjId): + self.idToObjMap[newObjId] = obj + self.objToIdMap[id(obj)] = newObjId + return newObjId + + def clean(self, objId): + obj = self.idToObjMap[objId] + del self.idToObjMap[objId] + del self.objToIdMap[id(obj)] + + def isProxy(self, anObject): + is_proxy = False + + if isinstance(anObject, dict): + if len(anObject.keys()) == 2 and ('__pyclass__' in anObject) and ('__pyid__' in anObject): + is_proxy = True + + return is_proxy + +class RegistryError(Exception): + pass + +class RegisterWithDifferentIdError(RegistryError): + def __init__(self, obj, newId): + RegistryError.__init__(self,"Attempting to register object {0} with ID {1} with different ID {2}.".format(type(obj).__name__, registry().register(obj), newId)) + +class ResolveUnknownObject(RegistryError): + def __init__(self, objId): + RegistryError.__init__(self,"Attempting to resolve unknown object with id {0}.".format(objId)) + +class RegisterForbiddenObject(RegistryError): + def __init__(self, obj): + RegistryError.__init__(self,"Attempting to register forbidden object of type {0}.".format(type(obj).__name__)) + +ensure_global_registry() diff --git a/carp/src/bridge/stoppable_thread.py b/carp/src/bridge/stoppable_thread.py new file mode 100644 index 0000000..325cf96 --- /dev/null +++ b/carp/src/bridge/stoppable_thread.py @@ -0,0 +1,24 @@ +import threading + +class StoppableThread(threading.Thread): + def __init__(self, loop_func, setup_func): + threading.Thread.__init__(self) + self._stop_event = threading.Event() + self.daemon = True + self.loop_func = loop_func + self.setup_func = setup_func + + # function using _stop function + def stop(self): + self._stop_event.set() + + def stopped(self): + return self._stop_event.isSet() + + def run(self): + self.setup_func() + while True: + if self.stopped(): + return + self.loop_func() + diff --git a/carp/src/bridge/tfactorial.py b/carp/src/bridge/tfactorial.py new file mode 100644 index 0000000..01c8f01 --- /dev/null +++ b/carp/src/bridge/tfactorial.py @@ -0,0 +1,36 @@ +import argparse +import threading +import time + + +# Calculate the factorial +def factorial(n, t): + time.sleep(n*t/4) + print("Start: " + str(t) + ": " + str(n)) + if n == 1: + res = 1 + if t == 1: + print("Feel free to break here") + else: + res = n * factorial(n-1, t) + return res + + +# Calculate the factorial and print the result +def factorial_thread(n, t): + time.sleep(2) + result = factorial(n, t) + print("Thread " + str(t) + " = "+str(result)) + + +def launch_factorials(n): + threads = [] + print("Calculate: "+str(n)) + breakpoint() + for i in range(n): + threads.append(threading.Thread(target=factorial_thread, args=(n+i, i+1))) + threads[-1].start() + print("Wait for the results") + for thread in threads: + thread.join() + print("Done") diff --git a/carp/src/bridge/tmp.py b/carp/src/bridge/tmp.py new file mode 100644 index 0000000..8183ee5 --- /dev/null +++ b/carp/src/bridge/tmp.py @@ -0,0 +1,2 @@ +import time +time.sleep(3) \ No newline at end of file diff --git a/carp/src/bridge/utils.py b/carp/src/bridge/utils.py new file mode 100644 index 0000000..b14138d --- /dev/null +++ b/carp/src/bridge/utils.py @@ -0,0 +1,5 @@ +from uuid import uuid1 + +def random_str(): + return uuid1().hex + diff --git a/carp/src/languagelink.carp b/carp/src/languagelink.carp new file mode 100644 index 0000000..ee6e3a4 --- /dev/null +++ b/carp/src/languagelink.carp @@ -0,0 +1,17 @@ +(load "/Users/veitheller/Documents/Code/Github/carp/archive/socket/sockets.carp") + +(use Socket) + +(defn main [] + (let-do [port (Maybe.from (from-string &(Maybe.from (nth &System.args 1) @"")) 0)] + (IO.println &(fmt "Starting server on localhost, port %d" port)) + (Socket.with-server sock "127.0.0.1" port + (if (Socket.valid? &sock) + (Socket.while-connection &sock client + (IO.println &(read &client)) + (send &client "hi") + (IO.println "yay")) + (IO.errorln "Server couldn’t be started."))))) + +(build) +(run) diff --git a/carp/src/languagelink.py b/carp/src/languagelink.py new file mode 100644 index 0000000..f80f199 --- /dev/null +++ b/carp/src/languagelink.py @@ -0,0 +1,4 @@ +from bridge import bridge +from bridge.bridge_hooks import * + +bridge.run_bridge() diff --git a/lepiter/98eic2d1mangtosf26n3aq420.bak b/lepiter/98eic2d1mangtosf26n3aq420.bak index 059a4ce..a4b19e1 100644 --- a/lepiter/98eic2d1mangtosf26n3aq420.bak +++ b/lepiter/98eic2d1mangtosf26n3aq420.bak @@ -8,7 +8,45 @@ "__type" : "textSnippet", "children" : { "__type" : "snippets", - "items" : [ ] + "items" : [ + { + "__type" : "textSnippet", + "children" : { + "__type" : "snippets", + "items" : [ ] + }, + "createEmail" : { + "__type" : "email", + "emailString" : "" + }, + "createTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-04-17T22:22:31.801924+02:00" + } + }, + "editEmail" : { + "__type" : "email", + "emailString" : "" + }, + "editTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-04-17T22:22:40.406467+02:00" + } + }, + "uid" : { + "__type" : "uid", + "uidString" : "3kLYI0GZDQCKnLdXBAU6hQ==" + }, + "paragraphStyle" : { + "__type" : "textStyle" + }, + "string" : "[[Interlude: An Introduction to Carp]]" + } + ] }, "createEmail" : { "__type" : "email", @@ -41,43 +79,6 @@ }, "string" : "[[Introduction]]" }, - { - "__type" : "textSnippet", - "children" : { - "__type" : "snippets", - "items" : [ ] - }, - "createEmail" : { - "__type" : "email", - "emailString" : "" - }, - "createTime" : { - "__type" : "time", - "time" : { - "__type" : "dateAndTime", - "dateAndTimeString" : "2022-04-17T22:22:30.537898+02:00" - } - }, - "editEmail" : { - "__type" : "email", - "emailString" : "" - }, - "editTime" : { - "__type" : "time", - "time" : { - "__type" : "dateAndTime", - "dateAndTimeString" : "2022-04-17T22:22:30.537898+02:00" - } - }, - "uid" : { - "__type" : "uid", - "uidString" : "3kLYI0GZDQCKnLdXBAU6hQ==" - }, - "paragraphStyle" : { - "__type" : "textStyle" - }, - "string" : "" - }, { "__type" : "textSnippet", "children" : { @@ -231,7 +232,119 @@ "__type" : "textSnippet", "children" : { "__type" : "snippets", - "items" : [ ] + "items" : [ + { + "__type" : "textSnippet", + "children" : { + "__type" : "snippets", + "items" : [ ] + }, + "createEmail" : { + "__type" : "email", + "emailString" : "" + }, + "createTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T17:49:20.565513+02:00" + } + }, + "editEmail" : { + "__type" : "email", + "emailString" : "" + }, + "editTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T17:49:20.565513+02:00" + } + }, + "uid" : { + "__type" : "uid", + "uidString" : "UXDVu4+dDQC/fqHFA8ExpA==" + }, + "paragraphStyle" : { + "__type" : "textStyle" + }, + "string" : "[[Building a LanguageLink client]]" + }, + { + "__type" : "textSnippet", + "children" : { + "__type" : "snippets", + "items" : [ ] + }, + "createEmail" : { + "__type" : "email", + "emailString" : "" + }, + "createTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T17:49:33.223032+02:00" + } + }, + "editEmail" : { + "__type" : "email", + "emailString" : "" + }, + "editTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T17:49:33.223032+02:00" + } + }, + "uid" : { + "__type" : "uid", + "uidString" : "6dWWvI+dDQC/gM7QA8ExpA==" + }, + "paragraphStyle" : { + "__type" : "textStyle" + }, + "string" : "[[Building a LanguageLink server]]" + }, + { + "__type" : "textSnippet", + "children" : { + "__type" : "snippets", + "items" : [ ] + }, + "createEmail" : { + "__type" : "email", + "emailString" : "" + }, + "createTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T18:08:45.88618+02:00" + } + }, + "editEmail" : { + "__type" : "email", + "emailString" : "" + }, + "editTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T18:08:48.608001+02:00" + } + }, + "uid" : { + "__type" : "uid", + "uidString" : "rYmTAZCdDQC/qIzPA8ExpA==" + }, + "paragraphStyle" : { + "__type" : "textStyle" + }, + "string" : "[[What now?]]" + } + ] }, "createEmail" : { "__type" : "email", @@ -264,6 +377,81 @@ }, "string" : "[[Executing code]]" }, + { + "__type" : "textSnippet", + "children" : { + "__type" : "snippets", + "items" : [ + { + "__type" : "textSnippet", + "children" : { + "__type" : "snippets", + "items" : [ ] + }, + "createEmail" : { + "__type" : "email", + "emailString" : "" + }, + "createTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T18:07:50.665361+02:00" + } + }, + "editEmail" : { + "__type" : "email", + "emailString" : "" + }, + "editTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T18:07:59.692968+02:00" + } + }, + "uid" : { + "__type" : "uid", + "uidString" : "3NNC/o+dDQC/oLtxA8ExpA==" + }, + "paragraphStyle" : { + "__type" : "textStyle" + }, + "string" : "[[Hooking up your LanguageLink server]]" + } + ] + }, + "createEmail" : { + "__type" : "email", + "emailString" : "" + }, + "createTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T18:08:54.91122+02:00" + } + }, + "editEmail" : { + "__type" : "email", + "emailString" : "" + }, + "editTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T18:09:11.691776+02:00" + } + }, + "uid" : { + "__type" : "uid", + "uidString" : "uAjiAZCdDQC/qqUDA8ExpA==" + }, + "paragraphStyle" : { + "__type" : "textStyle" + }, + "string" : "[[A custom snippet type]]" + }, { "__type" : "textSnippet", "children" : { diff --git a/lepiter/98eic2d1mangtosf26n3aq420.lepiter b/lepiter/98eic2d1mangtosf26n3aq420.lepiter index bd9e872..ce14640 100644 --- a/lepiter/98eic2d1mangtosf26n3aq420.lepiter +++ b/lepiter/98eic2d1mangtosf26n3aq420.lepiter @@ -232,7 +232,119 @@ "__type" : "textSnippet", "children" : { "__type" : "snippets", - "items" : [ ] + "items" : [ + { + "__type" : "textSnippet", + "children" : { + "__type" : "snippets", + "items" : [ ] + }, + "createEmail" : { + "__type" : "email", + "emailString" : "" + }, + "createTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T17:49:20.565513+02:00" + } + }, + "editEmail" : { + "__type" : "email", + "emailString" : "" + }, + "editTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T17:49:20.565513+02:00" + } + }, + "uid" : { + "__type" : "uid", + "uidString" : "UXDVu4+dDQC/fqHFA8ExpA==" + }, + "paragraphStyle" : { + "__type" : "textStyle" + }, + "string" : "[[Building a LanguageLink client]]" + }, + { + "__type" : "textSnippet", + "children" : { + "__type" : "snippets", + "items" : [ ] + }, + "createEmail" : { + "__type" : "email", + "emailString" : "" + }, + "createTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T17:49:33.223032+02:00" + } + }, + "editEmail" : { + "__type" : "email", + "emailString" : "" + }, + "editTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T17:49:33.223032+02:00" + } + }, + "uid" : { + "__type" : "uid", + "uidString" : "6dWWvI+dDQC/gM7QA8ExpA==" + }, + "paragraphStyle" : { + "__type" : "textStyle" + }, + "string" : "[[Building a LanguageLink server]]" + }, + { + "__type" : "textSnippet", + "children" : { + "__type" : "snippets", + "items" : [ ] + }, + "createEmail" : { + "__type" : "email", + "emailString" : "" + }, + "createTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T18:08:45.88618+02:00" + } + }, + "editEmail" : { + "__type" : "email", + "emailString" : "" + }, + "editTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T18:08:48.608001+02:00" + } + }, + "uid" : { + "__type" : "uid", + "uidString" : "rYmTAZCdDQC/qIzPA8ExpA==" + }, + "paragraphStyle" : { + "__type" : "textStyle" + }, + "string" : "[[What now?]]" + } + ] }, "createEmail" : { "__type" : "email", @@ -265,6 +377,118 @@ }, "string" : "[[Executing code]]" }, + { + "__type" : "textSnippet", + "children" : { + "__type" : "snippets", + "items" : [ + { + "__type" : "textSnippet", + "children" : { + "__type" : "snippets", + "items" : [ ] + }, + "createEmail" : { + "__type" : "email", + "emailString" : "" + }, + "createTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T18:07:50.665361+02:00" + } + }, + "editEmail" : { + "__type" : "email", + "emailString" : "" + }, + "editTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T18:07:59.692968+02:00" + } + }, + "uid" : { + "__type" : "uid", + "uidString" : "3NNC/o+dDQC/oLtxA8ExpA==" + }, + "paragraphStyle" : { + "__type" : "textStyle" + }, + "string" : "[[Hooking up your LanguageLink server]]" + } + ] + }, + "createEmail" : { + "__type" : "email", + "emailString" : "" + }, + "createTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T18:08:54.91122+02:00" + } + }, + "editEmail" : { + "__type" : "email", + "emailString" : "" + }, + "editTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T18:09:11.691776+02:00" + } + }, + "uid" : { + "__type" : "uid", + "uidString" : "uAjiAZCdDQC/qqUDA8ExpA==" + }, + "paragraphStyle" : { + "__type" : "textStyle" + }, + "string" : "[[A custom snippet type]]" + }, + { + "__type" : "textSnippet", + "children" : { + "__type" : "snippets", + "items" : [ ] + }, + "createEmail" : { + "__type" : "email", + "emailString" : "" + }, + "createTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T18:38:03.097269+02:00" + } + }, + "editEmail" : { + "__type" : "email", + "emailString" : "" + }, + "editTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T18:38:05.781133+02:00" + } + }, + "uid" : { + "__type" : "uid", + "uidString" : "j+IkapCdDQCBVgDCA8ExpA==" + }, + "paragraphStyle" : { + "__type" : "textStyle" + }, + "string" : "[[Fin]]" + }, { "__type" : "textSnippet", "children" : { diff --git a/lepiter/98eic2dywhmaij4eonyqv7aqu.bak b/lepiter/98eic2dywhmaij4eonyqv7aqu.bak index c197b40..78b0413 100644 --- a/lepiter/98eic2dywhmaij4eonyqv7aqu.bak +++ b/lepiter/98eic2dywhmaij4eonyqv7aqu.bak @@ -325,14 +325,14 @@ "__type" : "time", "time" : { "__type" : "dateAndTime", - "dateAndTimeString" : "2022-04-17T17:07:38.104346+02:00" + "dateAndTimeString" : "2022-06-11T16:35:43.289314+02:00" } }, "uid" : { "__type" : "uid", "uidString" : "Ddc1ljyZDQCZq2mSDqn2mw==" }, - "code" : "; this is what we will be able to do\r(IO.defn hello-world []\r (IO.println \"Hello world!\"))" + "code" : "; this is what we will be able to do\r(defn hello-world []\r (IO.println \"Hello world!\"))" }, { "__type" : "textSnippet", diff --git a/lepiter/98eic2dywhmaij4eonyqv7aqu.lepiter b/lepiter/98eic2dywhmaij4eonyqv7aqu.lepiter index dacd65c..9955146 100644 --- a/lepiter/98eic2dywhmaij4eonyqv7aqu.lepiter +++ b/lepiter/98eic2dywhmaij4eonyqv7aqu.lepiter @@ -325,7 +325,7 @@ "__type" : "time", "time" : { "__type" : "dateAndTime", - "dateAndTimeString" : "2022-04-17T17:08:04.346367+02:00" + "dateAndTimeString" : "2022-06-11T16:35:43.289314+02:00" } }, "uid" : { @@ -359,7 +359,7 @@ "__type" : "time", "time" : { "__type" : "dateAndTime", - "dateAndTimeString" : "2022-04-17T16:54:20.656267+02:00" + "dateAndTimeString" : "2022-06-11T17:10:18.677811+02:00" } }, "uid" : { diff --git a/lepiter/98eic2g8xrl1zoo0c1rpxqe78.bak b/lepiter/98eic2g8xrl1zoo0c1rpxqe78.bak new file mode 100644 index 0000000..b7fa264 --- /dev/null +++ b/lepiter/98eic2g8xrl1zoo0c1rpxqe78.bak @@ -0,0 +1,113 @@ +{ + "__schema" : "4.1", + "__type" : "page", + "children" : { + "__type" : "snippets", + "items" : [ + { + "__type" : "textSnippet", + "children" : { + "__type" : "snippets", + "items" : [ ] + }, + "createEmail" : { + "__type" : "email", + "emailString" : "" + }, + "createTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T11:30:43.257961+02:00" + } + }, + "editEmail" : { + "__type" : "email", + "emailString" : "" + }, + "editTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T17:48:16.142343+02:00" + } + }, + "uid" : { + "__type" : "uid", + "uidString" : "JUkMyTyZDQCZxrrZDqn2mw==" + }, + "paragraphStyle" : { + "__type" : "textStyle" + }, + "string" : "Getting code to execute is most likely the most tricky part of adding a language to GT. Most of the code in the Carp IDE deals with handling that part of the IDE experience. Additionally, LanguageLink requires a custom server setup, which needed to be written outside of GT." + }, + { + "__type" : "textSnippet", + "children" : { + "__type" : "snippets", + "items" : [ ] + }, + "createEmail" : { + "__type" : "email", + "emailString" : "" + }, + "createTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T17:48:20.869905+02:00" + } + }, + "editEmail" : { + "__type" : "email", + "emailString" : "" + }, + "editTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T17:48:46.359725+02:00" + } + }, + "uid" : { + "__type" : "uid", + "uidString" : "dwEUdoqdDQC+EJoFA3UANg==" + }, + "paragraphStyle" : { + "__type" : "textStyle" + }, + "string" : "Because both parts of this process make a cohesive whole, I split this chapter into two sub-chapters, [[B" + } + ] + }, + "createEmail" : { + "__type" : "email", + "emailString" : "" + }, + "createTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-04-17T17:10:47.300048+02:00" + } + }, + "editEmail" : { + "__type" : "email", + "emailString" : "" + }, + "editTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-04-17T17:10:47.300048+02:00" + } + }, + "pageType" : { + "__type" : "namedPage", + "title" : "Executing code" + }, + "uid" : { + "__type" : "uuid", + "uuid" : "647f0bc9-3c99-0d00-99c5-5d710ea9f69b" + } +} \ No newline at end of file diff --git a/lepiter/98eic2g8xrl1zoo0c1rpxqe78.lepiter b/lepiter/98eic2g8xrl1zoo0c1rpxqe78.lepiter index 1a3f638..0b4e2f2 100644 --- a/lepiter/98eic2g8xrl1zoo0c1rpxqe78.lepiter +++ b/lepiter/98eic2g8xrl1zoo0c1rpxqe78.lepiter @@ -18,7 +18,7 @@ "__type" : "time", "time" : { "__type" : "dateAndTime", - "dateAndTimeString" : "2022-04-17T17:10:53.313378+02:00" + "dateAndTimeString" : "2022-06-11T11:30:43.257961+02:00" } }, "editEmail" : { @@ -29,7 +29,7 @@ "__type" : "time", "time" : { "__type" : "dateAndTime", - "dateAndTimeString" : "2022-04-17T17:10:53.833743+02:00" + "dateAndTimeString" : "2022-06-11T17:48:16.142343+02:00" } }, "uid" : { @@ -39,7 +39,44 @@ "paragraphStyle" : { "__type" : "textStyle" }, - "string" : "TODO" + "string" : "Getting code to execute is most likely the most tricky part of adding a language to GT. Most of the code in the Carp IDE deals with handling that part of the IDE experience. Additionally, LanguageLink requires a custom server setup, which needed to be written outside of GT." + }, + { + "__type" : "textSnippet", + "children" : { + "__type" : "snippets", + "items" : [ ] + }, + "createEmail" : { + "__type" : "email", + "emailString" : "" + }, + "createTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T17:48:20.869905+02:00" + } + }, + "editEmail" : { + "__type" : "email", + "emailString" : "" + }, + "editTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T17:49:02.268331+02:00" + } + }, + "uid" : { + "__type" : "uid", + "uidString" : "dwEUdoqdDQC+EJoFA3UANg==" + }, + "paragraphStyle" : { + "__type" : "textStyle" + }, + "string" : "Because both parts of this process make a cohesive whole, I split this chapter into two sub-chapters, [[Building a LanguageLink client]] and [[Building a LanguageLink server]]." } ] }, diff --git a/lepiter/9py149rplidcvk0jjdlnxeufv.bak b/lepiter/9py149rplidcvk0jjdlnxeufv.bak new file mode 100644 index 0000000..33d89ee --- /dev/null +++ b/lepiter/9py149rplidcvk0jjdlnxeufv.bak @@ -0,0 +1,258 @@ +{ + "__schema" : "4.1", + "__type" : "page", + "children" : { + "__type" : "snippets", + "items" : [ + { + "__type" : "textSnippet", + "children" : { + "__type" : "snippets", + "items" : [ ] + }, + "createEmail" : { + "__type" : "email", + "emailString" : "" + }, + "createTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T18:09:27.48233+02:00" + } + }, + "editEmail" : { + "__type" : "email", + "emailString" : "" + }, + "editTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T18:09:42.790324+02:00" + } + }, + "uid" : { + "__type" : "uid", + "uidString" : "n0WyA5CdDQC/sEIUA8ExpA==" + }, + "paragraphStyle" : { + "__type" : "textStyle" + }, + "string" : "Now that we have a LanguageLink server, we are all set, right? Well, not quite." + }, + { + "__type" : "textSnippet", + "children" : { + "__type" : "snippets", + "items" : [ ] + }, + "createEmail" : { + "__type" : "email", + "emailString" : "" + }, + "createTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T18:09:49.137549+02:00" + } + }, + "editEmail" : { + "__type" : "email", + "emailString" : "" + }, + "editTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T18:10:53.048149+02:00" + } + }, + "uid" : { + "__type" : "uid", + "uidString" : "YxldBZCdDQC/s15wA8ExpA==" + }, + "paragraphStyle" : { + "__type" : "textStyle" + }, + "string" : "Usually, a LanguageLink server is used within snippets as a driver for code execution. While you can use it yourself to execute some custom code and find some pleasure in that, we will first need an interface to type code *into* to make it truly worth our while." + }, + { + "__type" : "textSnippet", + "children" : { + "__type" : "snippets", + "items" : [ ] + }, + "createEmail" : { + "__type" : "email", + "emailString" : "" + }, + "createTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T18:10:55.085183+02:00" + } + }, + "editEmail" : { + "__type" : "email", + "emailString" : "" + }, + "editTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T18:11:56.629038+02:00" + } + }, + "uid" : { + "__type" : "uid", + "uidString" : "ChpOCZCdDQC/tYwmA8ExpA==" + }, + "paragraphStyle" : { + "__type" : "textStyle" + }, + "string" : "But I understand that after all of the effort of building a LanguageLink implementation for your target you’re itching to try it out, so I’ll give you a little teaser of how it’s going to be used in the snippet:" + }, + { + "__type" : "textSnippet", + "children" : { + "__type" : "snippets", + "items" : [ ] + }, + "createEmail" : { + "__type" : "email", + "emailString" : "" + }, + "createTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T18:11:58.378929+02:00" + } + }, + "editEmail" : { + "__type" : "email", + "emailString" : "" + }, + "editTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T18:12:53.87069+02:00" + } + }, + "uid" : { + "__type" : "uid", + "uidString" : "BW4QDZCdDQC/tz6wA8ExpA==" + }, + "paragraphStyle" : { + "__type" : "textStyle" + }, + "string" : "{{gtMethod:GtCarpCoderModel>>#bindAndExecute:|expanded}}" + }, + { + "__type" : "textSnippet", + "children" : { + "__type" : "snippets", + "items" : [ ] + }, + "createEmail" : { + "__type" : "email", + "emailString" : "" + }, + "createTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T18:13:00.938416+02:00" + } + }, + "editEmail" : { + "__type" : "email", + "emailString" : "" + }, + "editTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T18:13:26.816542+02:00" + } + }, + "uid" : { + "__type" : "uid", + "uidString" : "LBHBEJCdDQC/9sJRA8ExpA==" + }, + "paragraphStyle" : { + "__type" : "textStyle" + }, + "string" : "This means that a simple snippet to test your LanguageLink implementation might look like this:" + }, + { + "__type" : "pharoSnippet", + "children" : { + "__type" : "snippets", + "items" : [ ] + }, + "createEmail" : { + "__type" : "email", + "emailString" : "" + }, + "createTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T18:13:37.920717+02:00" + } + }, + "editEmail" : { + "__type" : "email", + "emailString" : "" + }, + "editTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T18:18:19.754957+02:00" + } + }, + "uid" : { + "__type" : "uid", + "uidString" : "EjsQE5CdDQC/+NLCA8ExpA==" + }, + "code" : "sourceString := '(IO.println \"Hello World!\")'.\t\"change this to your language\"\r\rapplication := CarpApplication uniqueInstance. \"change this to your application, might need to initialize the unique instance\"\rapplication isRunning ifFalse: [ application start ].\rcommandFactory := application newCommandFactory.\r\rcommandFactory\r\t<< sourceString;\r\tsendAndWait" + } + ] + }, + "createEmail" : { + "__type" : "email", + "emailString" : "" + }, + "createTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T18:09:20.062163+02:00" + } + }, + "editEmail" : { + "__type" : "email", + "emailString" : "" + }, + "editTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T18:09:20.062163+02:00" + } + }, + "pageType" : { + "__type" : "namedPage", + "title" : "What now?" + }, + "uid" : { + "__type" : "uuid", + "uuid" : "9b9eb103-909d-0d00-bfaf-1b1203c131a4" + } +} \ No newline at end of file diff --git a/lepiter/9py149rplidcvk0jjdlnxeufv.lepiter b/lepiter/9py149rplidcvk0jjdlnxeufv.lepiter new file mode 100644 index 0000000..31e3200 --- /dev/null +++ b/lepiter/9py149rplidcvk0jjdlnxeufv.lepiter @@ -0,0 +1,295 @@ +{ + "__schema" : "4.1", + "__type" : "page", + "children" : { + "__type" : "snippets", + "items" : [ + { + "__type" : "textSnippet", + "children" : { + "__type" : "snippets", + "items" : [ ] + }, + "createEmail" : { + "__type" : "email", + "emailString" : "" + }, + "createTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T18:09:27.48233+02:00" + } + }, + "editEmail" : { + "__type" : "email", + "emailString" : "" + }, + "editTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T18:09:42.790324+02:00" + } + }, + "uid" : { + "__type" : "uid", + "uidString" : "n0WyA5CdDQC/sEIUA8ExpA==" + }, + "paragraphStyle" : { + "__type" : "textStyle" + }, + "string" : "Now that we have a LanguageLink server, we are all set, right? Well, not quite." + }, + { + "__type" : "textSnippet", + "children" : { + "__type" : "snippets", + "items" : [ ] + }, + "createEmail" : { + "__type" : "email", + "emailString" : "" + }, + "createTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T18:09:49.137549+02:00" + } + }, + "editEmail" : { + "__type" : "email", + "emailString" : "" + }, + "editTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T18:10:53.048149+02:00" + } + }, + "uid" : { + "__type" : "uid", + "uidString" : "YxldBZCdDQC/s15wA8ExpA==" + }, + "paragraphStyle" : { + "__type" : "textStyle" + }, + "string" : "Usually, a LanguageLink server is used within snippets as a driver for code execution. While you can use it yourself to execute some custom code and find some pleasure in that, we will first need an interface to type code *into* to make it truly worth our while." + }, + { + "__type" : "textSnippet", + "children" : { + "__type" : "snippets", + "items" : [ ] + }, + "createEmail" : { + "__type" : "email", + "emailString" : "" + }, + "createTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T18:10:55.085183+02:00" + } + }, + "editEmail" : { + "__type" : "email", + "emailString" : "" + }, + "editTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T18:11:56.629038+02:00" + } + }, + "uid" : { + "__type" : "uid", + "uidString" : "ChpOCZCdDQC/tYwmA8ExpA==" + }, + "paragraphStyle" : { + "__type" : "textStyle" + }, + "string" : "But I understand that after all of the effort of building a LanguageLink implementation for your target you’re itching to try it out, so I’ll give you a little teaser of how it’s going to be used in the snippet:" + }, + { + "__type" : "textSnippet", + "children" : { + "__type" : "snippets", + "items" : [ ] + }, + "createEmail" : { + "__type" : "email", + "emailString" : "" + }, + "createTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T18:11:58.378929+02:00" + } + }, + "editEmail" : { + "__type" : "email", + "emailString" : "" + }, + "editTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T18:12:53.87069+02:00" + } + }, + "uid" : { + "__type" : "uid", + "uidString" : "BW4QDZCdDQC/tz6wA8ExpA==" + }, + "paragraphStyle" : { + "__type" : "textStyle" + }, + "string" : "{{gtMethod:GtCarpCoderModel>>#bindAndExecute:|expanded}}" + }, + { + "__type" : "textSnippet", + "children" : { + "__type" : "snippets", + "items" : [ ] + }, + "createEmail" : { + "__type" : "email", + "emailString" : "" + }, + "createTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T18:13:00.938416+02:00" + } + }, + "editEmail" : { + "__type" : "email", + "emailString" : "" + }, + "editTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T18:13:26.816542+02:00" + } + }, + "uid" : { + "__type" : "uid", + "uidString" : "LBHBEJCdDQC/9sJRA8ExpA==" + }, + "paragraphStyle" : { + "__type" : "textStyle" + }, + "string" : "This means that a simple snippet to test your LanguageLink implementation might look like this:" + }, + { + "__type" : "pharoSnippet", + "children" : { + "__type" : "snippets", + "items" : [ ] + }, + "createEmail" : { + "__type" : "email", + "emailString" : "" + }, + "createTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T18:13:37.920717+02:00" + } + }, + "editEmail" : { + "__type" : "email", + "emailString" : "" + }, + "editTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T18:18:32.957333+02:00" + } + }, + "uid" : { + "__type" : "uid", + "uidString" : "EjsQE5CdDQC/+NLCA8ExpA==" + }, + "code" : "sourceString := '(IO.println \"Hello World!\")'.\t\"change this to your language\"\r\rapplication := CarpApplication uniqueInstance.\t\"change this to your application, might need to initialize the unique instance\"\rapplication isRunning ifFalse: [ application start ].\rcommandFactory := application newCommandFactory.\r\rcommandFactory\r\t<< sourceString;\r\tsendAndWait" + }, + { + "__type" : "textSnippet", + "children" : { + "__type" : "snippets", + "items" : [ ] + }, + "createEmail" : { + "__type" : "email", + "emailString" : "" + }, + "createTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T18:18:42.650713+02:00" + } + }, + "editEmail" : { + "__type" : "email", + "emailString" : "" + }, + "editTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T18:18:48.915283+02:00" + } + }, + "uid" : { + "__type" : "uid", + "uidString" : "pPclJZCdDQCADqoCA8ExpA==" + }, + "paragraphStyle" : { + "__type" : "textStyle" + }, + "string" : "And that should be all you need to get going!" + } + ] + }, + "createEmail" : { + "__type" : "email", + "emailString" : "" + }, + "createTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T18:09:20.062163+02:00" + } + }, + "editEmail" : { + "__type" : "email", + "emailString" : "" + }, + "editTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T18:09:20.062163+02:00" + } + }, + "pageType" : { + "__type" : "namedPage", + "title" : "What now?" + }, + "uid" : { + "__type" : "uuid", + "uuid" : "9b9eb103-909d-0d00-bfaf-1b1203c131a4" + } +} \ No newline at end of file diff --git a/lepiter/9py149svgo06hri6l3ym0kvvu.bak b/lepiter/9py149svgo06hri6l3ym0kvvu.bak new file mode 100644 index 0000000..8fa965f --- /dev/null +++ b/lepiter/9py149svgo06hri6l3ym0kvvu.bak @@ -0,0 +1,187 @@ +{ + "__schema" : "4.1", + "__type" : "page", + "children" : { + "__type" : "snippets", + "items" : [ + { + "__type" : "textSnippet", + "children" : { + "__type" : "snippets", + "items" : [ ] + }, + "createEmail" : { + "__type" : "email", + "emailString" : "" + }, + "createTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T18:18:58.358497+02:00" + } + }, + "editEmail" : { + "__type" : "email", + "emailString" : "" + }, + "editTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T18:19:44.830424+02:00" + } + }, + "uid" : { + "__type" : "uid", + "uidString" : "n4phA5CdDQC/rbQ7A8ExpA==" + }, + "paragraphStyle" : { + "__type" : "textStyle" + }, + "string" : "After the doozy that is setting up LanguageLink, you might think that making a custom snippet type is going to be even more effort. I’ve got good news for you." + }, + { + "__type" : "textSnippet", + "children" : { + "__type" : "snippets", + "items" : [ ] + }, + "createEmail" : { + "__type" : "email", + "emailString" : "" + }, + "createTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T18:19:51.016989+02:00" + } + }, + "editEmail" : { + "__type" : "email", + "emailString" : "" + }, + "editTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T18:25:16.445558+02:00" + } + }, + "uid" : { + "__type" : "uid", + "uidString" : "Occ9KZCdDQCAEXGwA8ExpA==" + }, + "paragraphStyle" : { + "__type" : "textStyle" + }, + "string" : "Creating a snippet type is actually quite simple. Almost all of the code for Carp is in the Carp package, under the tag Lepiter (TODO: gtPackage annotation would be great). The most important class is likely {{gtClass:LeCarpSnippet}}, because like {{gtClass:CarpApplication}} for LanguageLink, this class is the central player around which the rest of the team organizes." + }, + { + "__type" : "textSnippet", + "children" : { + "__type" : "snippets", + "items" : [ ] + }, + "createEmail" : { + "__type" : "email", + "emailString" : "" + }, + "createTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T18:25:29.637968+02:00" + } + }, + "editEmail" : { + "__type" : "email", + "emailString" : "" + }, + "editTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T18:29:22.376041+02:00" + } + }, + "uid" : { + "__type" : "uid", + "uidString" : "pS1HPZCdDQCAOUdiA8ExpA==" + }, + "paragraphStyle" : { + "__type" : "textStyle" + }, + "string" : "Most of the code you will write will just be specialization over readily-available Lepiter classes. The only things that will be truly custom will most likely be your version of {{gtClass:LeExternalServerStrategy}} (in the case of Carp that is {{gtClass:LeCarpApplicationStrategy}}), which will bridge the gap between your LanguageLink application and the snippet itself, and your {{gtClass:GtSourceCoder}}, which" + }, + { + "__type" : "textSnippet", + "children" : { + "__type" : "snippets", + "items" : [ ] + }, + "createEmail" : { + "__type" : "email", + "emailString" : "" + }, + "createTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T18:28:08.711515+02:00" + } + }, + "editEmail" : { + "__type" : "email", + "emailString" : "" + }, + "editTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T18:28:15.191912+02:00" + } + }, + "uid" : { + "__type" : "uid", + "uidString" : "Dv7PRJCdDQCASGWFA8ExpA==" + }, + "paragraphStyle" : { + "__type" : "textStyle" + }, + "string" : "But more about that in the next section!" + } + ] + }, + "createEmail" : { + "__type" : "email", + "emailString" : "" + }, + "createTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T18:09:14.768068+02:00" + } + }, + "editEmail" : { + "__type" : "email", + "emailString" : "" + }, + "editTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T18:09:14.768068+02:00" + } + }, + "pageType" : { + "__type" : "namedPage", + "title" : "A custom snippet type" + }, + "uid" : { + "__type" : "uuid", + "uuid" : "6ad66003-909d-0d00-bfac-dd3803c131a4" + } +} \ No newline at end of file diff --git a/lepiter/9py149svgo06hri6l3ym0kvvu.lepiter b/lepiter/9py149svgo06hri6l3ym0kvvu.lepiter new file mode 100644 index 0000000..2d6b121 --- /dev/null +++ b/lepiter/9py149svgo06hri6l3ym0kvvu.lepiter @@ -0,0 +1,187 @@ +{ + "__schema" : "4.1", + "__type" : "page", + "children" : { + "__type" : "snippets", + "items" : [ + { + "__type" : "textSnippet", + "children" : { + "__type" : "snippets", + "items" : [ ] + }, + "createEmail" : { + "__type" : "email", + "emailString" : "" + }, + "createTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T18:18:58.358497+02:00" + } + }, + "editEmail" : { + "__type" : "email", + "emailString" : "" + }, + "editTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T18:19:44.830424+02:00" + } + }, + "uid" : { + "__type" : "uid", + "uidString" : "n4phA5CdDQC/rbQ7A8ExpA==" + }, + "paragraphStyle" : { + "__type" : "textStyle" + }, + "string" : "After the doozy that is setting up LanguageLink, you might think that making a custom snippet type is going to be even more effort. I’ve got good news for you." + }, + { + "__type" : "textSnippet", + "children" : { + "__type" : "snippets", + "items" : [ ] + }, + "createEmail" : { + "__type" : "email", + "emailString" : "" + }, + "createTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T18:19:51.016989+02:00" + } + }, + "editEmail" : { + "__type" : "email", + "emailString" : "" + }, + "editTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T18:25:16.445558+02:00" + } + }, + "uid" : { + "__type" : "uid", + "uidString" : "Occ9KZCdDQCAEXGwA8ExpA==" + }, + "paragraphStyle" : { + "__type" : "textStyle" + }, + "string" : "Creating a snippet type is actually quite simple. Almost all of the code for Carp is in the Carp package, under the tag Lepiter (TODO: gtPackage annotation would be great). The most important class is likely {{gtClass:LeCarpSnippet}}, because like {{gtClass:CarpApplication}} for LanguageLink, this class is the central player around which the rest of the team organizes." + }, + { + "__type" : "textSnippet", + "children" : { + "__type" : "snippets", + "items" : [ ] + }, + "createEmail" : { + "__type" : "email", + "emailString" : "" + }, + "createTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T18:25:29.637968+02:00" + } + }, + "editEmail" : { + "__type" : "email", + "emailString" : "" + }, + "editTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T18:29:42.230718+02:00" + } + }, + "uid" : { + "__type" : "uid", + "uidString" : "pS1HPZCdDQCAOUdiA8ExpA==" + }, + "paragraphStyle" : { + "__type" : "textStyle" + }, + "string" : "Most of the code you will write will just be specialization over readily-available Lepiter classes. The only things that will be truly custom will most likely be your version of {{gtClass:LeExternalServerStrategy}} (in the case of Carp that is {{gtClass:LeCarpApplicationStrategy}}), which will bridge the gap between your LanguageLink application and the snippet itself, and your {{gtClass:GtSourceCoder}} ({{gtClass:GtCarpCoderModel}})." + }, + { + "__type" : "textSnippet", + "children" : { + "__type" : "snippets", + "items" : [ ] + }, + "createEmail" : { + "__type" : "email", + "emailString" : "" + }, + "createTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T18:28:08.711515+02:00" + } + }, + "editEmail" : { + "__type" : "email", + "emailString" : "" + }, + "editTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T18:28:15.191912+02:00" + } + }, + "uid" : { + "__type" : "uid", + "uidString" : "Dv7PRJCdDQCASGWFA8ExpA==" + }, + "paragraphStyle" : { + "__type" : "textStyle" + }, + "string" : "But more about that in the next section!" + } + ] + }, + "createEmail" : { + "__type" : "email", + "emailString" : "" + }, + "createTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T18:09:14.768068+02:00" + } + }, + "editEmail" : { + "__type" : "email", + "emailString" : "" + }, + "editTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T18:09:14.768068+02:00" + } + }, + "pageType" : { + "__type" : "namedPage", + "title" : "A custom snippet type" + }, + "uid" : { + "__type" : "uuid", + "uuid" : "6ad66003-909d-0d00-bfac-dd3803c131a4" + } +} \ No newline at end of file diff --git a/lepiter/9py149sz34c10e429aqq1v7qc.bak b/lepiter/9py149sz34c10e429aqq1v7qc.bak new file mode 100644 index 0000000..828a6f2 --- /dev/null +++ b/lepiter/9py149sz34c10e429aqq1v7qc.bak @@ -0,0 +1,150 @@ +{ + "__schema" : "4.1", + "__type" : "page", + "children" : { + "__type" : "snippets", + "items" : [ + { + "__type" : "textSnippet", + "children" : { + "__type" : "snippets", + "items" : [ ] + }, + "createEmail" : { + "__type" : "email", + "emailString" : "" + }, + "createTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T17:59:53.327975+02:00" + } + }, + "editEmail" : { + "__type" : "email", + "emailString" : "" + }, + "editTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T18:01:42.598848+02:00" + } + }, + "uid" : { + "__type" : "uid", + "uidString" : "bUpMu4+dDQC/fPzrA8ExpA==" + }, + "paragraphStyle" : { + "__type" : "textStyle" + }, + "string" : "Theoretically, you can use any technology that supports HTTP, JSON, and possibly MessagePack to construct a LanguageLink server, and you might be tempted to just do it in the language you are targetting, especially since it is quite likely that you are comfortable in it." + }, + { + "__type" : "textSnippet", + "children" : { + "__type" : "snippets", + "items" : [ ] + }, + "createEmail" : { + "__type" : "email", + "emailString" : "" + }, + "createTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T18:01:44.277599+02:00" + } + }, + "editEmail" : { + "__type" : "email", + "emailString" : "" + }, + "editTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T18:06:06.353672+02:00" + } + }, + "uid" : { + "__type" : "uid", + "uidString" : "Yzt86I+dDQC/lywBA8ExpA==" + }, + "paragraphStyle" : { + "__type" : "textStyle" + }, + "string" : "I too was tempted to go down that route before realizing that that would mean understanding and implementing the LanguageLink protocol, a task I wasn’t particularly interested in. Instead, the Carp LanguageLink server uses the one provided by PythonBridge (which can be found [in the PythonBridge repository TODO: good link](github.com/feenkcom/pythonbridge)). It includes all the infrastructure needed, and the only thing that likely needs to be changed is how `EvalCommand`s are handled: in the case of Carp, a simple subprocess handler that feeds the statements into a Carp REPL and reads the result. You can inspect the custom code I wrote [in the gt4carp repository TODO: good link](https://git.veitheller.de/carpentry/gt4carp)." + }, + { + "__type" : "textSnippet", + "children" : { + "__type" : "snippets", + "items" : [ ] + }, + "createEmail" : { + "__type" : "email", + "emailString" : "" + }, + "createTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T18:06:15.752492+02:00" + } + }, + "editEmail" : { + "__type" : "email", + "emailString" : "" + }, + "editTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T18:07:19.529977+02:00" + } + }, + "uid" : { + "__type" : "uid", + "uidString" : "tJmV+I+dDQC/nrmHA8ExpA==" + }, + "paragraphStyle" : { + "__type" : "textStyle" + }, + "string" : "While I understand that this is a rather terse “figure it out yourself”, I don’t know the ins and outs of the LanguageLink protocol well enough to assist you any more—precisely because I took t" + } + ] + }, + "createEmail" : { + "__type" : "email", + "emailString" : "" + }, + "createTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T17:49:05.418793+02:00" + } + }, + "editEmail" : { + "__type" : "email", + "emailString" : "" + }, + "editTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T17:49:05.418793+02:00" + } + }, + "pageType" : { + "__type" : "namedPage", + "title" : "Building a LanguageLink server" + }, + "uid" : { + "__type" : "uuid", + "uuid" : "e4a14bbb-8f9d-0d00-bf7b-383c03c131a4" + } +} \ No newline at end of file diff --git a/lepiter/9py149sz34c10e429aqq1v7qc.lepiter b/lepiter/9py149sz34c10e429aqq1v7qc.lepiter new file mode 100644 index 0000000..88fca0c --- /dev/null +++ b/lepiter/9py149sz34c10e429aqq1v7qc.lepiter @@ -0,0 +1,150 @@ +{ + "__schema" : "4.1", + "__type" : "page", + "children" : { + "__type" : "snippets", + "items" : [ + { + "__type" : "textSnippet", + "children" : { + "__type" : "snippets", + "items" : [ ] + }, + "createEmail" : { + "__type" : "email", + "emailString" : "" + }, + "createTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T17:59:53.327975+02:00" + } + }, + "editEmail" : { + "__type" : "email", + "emailString" : "" + }, + "editTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T18:01:42.598848+02:00" + } + }, + "uid" : { + "__type" : "uid", + "uidString" : "bUpMu4+dDQC/fPzrA8ExpA==" + }, + "paragraphStyle" : { + "__type" : "textStyle" + }, + "string" : "Theoretically, you can use any technology that supports HTTP, JSON, and possibly MessagePack to construct a LanguageLink server, and you might be tempted to just do it in the language you are targetting, especially since it is quite likely that you are comfortable in it." + }, + { + "__type" : "textSnippet", + "children" : { + "__type" : "snippets", + "items" : [ ] + }, + "createEmail" : { + "__type" : "email", + "emailString" : "" + }, + "createTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T18:01:44.277599+02:00" + } + }, + "editEmail" : { + "__type" : "email", + "emailString" : "" + }, + "editTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T18:06:06.353672+02:00" + } + }, + "uid" : { + "__type" : "uid", + "uidString" : "Yzt86I+dDQC/lywBA8ExpA==" + }, + "paragraphStyle" : { + "__type" : "textStyle" + }, + "string" : "I too was tempted to go down that route before realizing that that would mean understanding and implementing the LanguageLink protocol, a task I wasn’t particularly interested in. Instead, the Carp LanguageLink server uses the one provided by PythonBridge (which can be found [in the PythonBridge repository TODO: good link](github.com/feenkcom/pythonbridge)). It includes all the infrastructure needed, and the only thing that likely needs to be changed is how `EvalCommand`s are handled: in the case of Carp, a simple subprocess handler that feeds the statements into a Carp REPL and reads the result. You can inspect the custom code I wrote [in the gt4carp repository TODO: good link](https://git.veitheller.de/carpentry/gt4carp)." + }, + { + "__type" : "textSnippet", + "children" : { + "__type" : "snippets", + "items" : [ ] + }, + "createEmail" : { + "__type" : "email", + "emailString" : "" + }, + "createTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T18:06:15.752492+02:00" + } + }, + "editEmail" : { + "__type" : "email", + "emailString" : "" + }, + "editTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T18:07:40.565555+02:00" + } + }, + "uid" : { + "__type" : "uid", + "uidString" : "tJmV+I+dDQC/nrmHA8ExpA==" + }, + "paragraphStyle" : { + "__type" : "textStyle" + }, + "string" : "While I understand that this is a rather terse “figure it out yourself”, I don’t know the ins and outs of the LanguageLink protocol well enough to assist you any more—precisely because I took the effort of side-stepping the issue entirely. Hopefully you can too!" + } + ] + }, + "createEmail" : { + "__type" : "email", + "emailString" : "" + }, + "createTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T17:49:05.418793+02:00" + } + }, + "editEmail" : { + "__type" : "email", + "emailString" : "" + }, + "editTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T17:49:05.418793+02:00" + } + }, + "pageType" : { + "__type" : "namedPage", + "title" : "Building a LanguageLink server" + }, + "uid" : { + "__type" : "uuid", + "uuid" : "e4a14bbb-8f9d-0d00-bf7b-383c03c131a4" + } +} \ No newline at end of file diff --git a/lepiter/9py149upn2jn86ntqk68tvld1.bak b/lepiter/9py149upn2jn86ntqk68tvld1.bak new file mode 100644 index 0000000..a611a4e --- /dev/null +++ b/lepiter/9py149upn2jn86ntqk68tvld1.bak @@ -0,0 +1,261 @@ +{ + "__schema" : "4.1", + "__type" : "page", + "children" : { + "__type" : "snippets", + "items" : [ + { + "__type" : "textSnippet", + "children" : { + "__type" : "snippets", + "items" : [ ] + }, + "createEmail" : { + "__type" : "email", + "emailString" : "" + }, + "createTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T18:28:18.00762+02:00" + } + }, + "editEmail" : { + "__type" : "email", + "emailString" : "" + }, + "editTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T18:30:10.327276+02:00" + } + }, + "uid" : { + "__type" : "uid", + "uidString" : "xNZI/4+dDQC/o+CfA8ExpA==" + }, + "paragraphStyle" : { + "__type" : "textStyle" + }, + "string" : "Finally, finally we get to have a snippet type that actually knows how to execute code!" + }, + { + "__type" : "textSnippet", + "children" : { + "__type" : "snippets", + "items" : [ ] + }, + "createEmail" : { + "__type" : "email", + "emailString" : "" + }, + "createTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T18:30:14.076728+02:00" + } + }, + "editEmail" : { + "__type" : "email", + "emailString" : "" + }, + "editTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T18:31:02.274958+02:00" + } + }, + "uid" : { + "__type" : "uid", + "uidString" : "t3BjTpCdDQCAUTe6A8ExpA==" + }, + "paragraphStyle" : { + "__type" : "textStyle" + }, + "string" : "To make that happen, we will need an application strategy. Building one should be fairly straightforward, since it mostly holds onto your LanguageLink application and builds new ones if needed." + }, + { + "__type" : "textSnippet", + "children" : { + "__type" : "snippets", + "items" : [ ] + }, + "createEmail" : { + "__type" : "email", + "emailString" : "" + }, + "createTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T18:31:19.494771+02:00" + } + }, + "editEmail" : { + "__type" : "email", + "emailString" : "" + }, + "editTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T18:32:06.868833+02:00" + } + }, + "uid" : { + "__type" : "uid", + "uidString" : "E6NbUZCdDQCAU8nsA8ExpA==" + }, + "paragraphStyle" : { + "__type" : "textStyle" + }, + "string" : "More interesting will be your coder model, which is responsible for taking source code from an application, handing it over to LanguageLink, and displaying the result. As such, it is the main link between the IDE and the execution engine." + }, + { + "__type" : "textSnippet", + "children" : { + "__type" : "snippets", + "items" : [ ] + }, + "createEmail" : { + "__type" : "email", + "emailString" : "" + }, + "createTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T18:32:07.688865+02:00" + } + }, + "editEmail" : { + "__type" : "email", + "emailString" : "" + }, + "editTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T18:34:29.49505+02:00" + } + }, + "uid" : { + "__type" : "uid", + "uidString" : "ypstVZCdDQCAYOo/A8ExpA==" + }, + "paragraphStyle" : { + "__type" : "textStyle" + }, + "string" : "In the section [[What now?]] I already showcased the method that is most central to that process: `bindAndExecute:`. In my case, {{gtMethod:GtCarpCoderModel>>#bindAndExecute:|expanded}} is the final result. A quick refresher:" + }, + { + "__type" : "textSnippet", + "children" : { + "__type" : "snippets", + "items" : [ ] + }, + "createEmail" : { + "__type" : "email", + "emailString" : "" + }, + "createTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T18:34:40.493148+02:00" + } + }, + "editEmail" : { + "__type" : "email", + "emailString" : "" + }, + "editTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T18:36:37.301362+02:00" + } + }, + "uid" : { + "__type" : "uid", + "uidString" : "WALsXZCdDQCBUGyRA8ExpA==" + }, + "paragraphStyle" : { + "__type" : "textStyle" + }, + "string" : "The method will take in a source string directly from coder and send it off to the evaluator handled by the LanguageLink application. Before that happens, though, it will most likely add some funky code around the snippet to capture the statement as a variable if possible, to allow for better interaction with GT." + }, + { + "__type" : "textSnippet", + "children" : { + "__type" : "snippets", + "items" : [ ] + }, + "createEmail" : { + "__type" : "email", + "emailString" : "" + }, + "createTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T18:36:42.952545+02:00" + } + }, + "editEmail" : { + "__type" : "email", + "emailString" : "" + }, + "editTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T18:37:23.855402+02:00" + } + }, + "uid" : { + "__type" : "uid", + "uidString" : "R7ljZZCdDQCBUlkLA8ExpA==" + }, + "paragraphStyle" : { + "__type" : "textStyle" + }, + "string" : "It might be more useful for you to start with a trimmed-down version of this method that doesn’t do anything with the bindings and just hands over the source string as-is. To harness the full power of interacting with G" + } + ] + }, + "createEmail" : { + "__type" : "email", + "emailString" : "" + }, + "createTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T18:08:06.046507+02:00" + } + }, + "editEmail" : { + "__type" : "email", + "emailString" : "" + }, + "editTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T18:08:06.046507+02:00" + } + }, + "pageType" : { + "__type" : "namedPage", + "title" : "Hooking up your LanguageLink server" + }, + "uid" : { + "__type" : "uuid", + "uuid" : "e53a48ff-8f9d-0d00-bfa2-217603c131a4" + } +} \ No newline at end of file diff --git a/lepiter/9py149upn2jn86ntqk68tvld1.lepiter b/lepiter/9py149upn2jn86ntqk68tvld1.lepiter new file mode 100644 index 0000000..bdb49bc --- /dev/null +++ b/lepiter/9py149upn2jn86ntqk68tvld1.lepiter @@ -0,0 +1,261 @@ +{ + "__schema" : "4.1", + "__type" : "page", + "children" : { + "__type" : "snippets", + "items" : [ + { + "__type" : "textSnippet", + "children" : { + "__type" : "snippets", + "items" : [ ] + }, + "createEmail" : { + "__type" : "email", + "emailString" : "" + }, + "createTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T18:28:18.00762+02:00" + } + }, + "editEmail" : { + "__type" : "email", + "emailString" : "" + }, + "editTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T18:30:10.327276+02:00" + } + }, + "uid" : { + "__type" : "uid", + "uidString" : "xNZI/4+dDQC/o+CfA8ExpA==" + }, + "paragraphStyle" : { + "__type" : "textStyle" + }, + "string" : "Finally, finally we get to have a snippet type that actually knows how to execute code!" + }, + { + "__type" : "textSnippet", + "children" : { + "__type" : "snippets", + "items" : [ ] + }, + "createEmail" : { + "__type" : "email", + "emailString" : "" + }, + "createTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T18:30:14.076728+02:00" + } + }, + "editEmail" : { + "__type" : "email", + "emailString" : "" + }, + "editTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T18:31:02.274958+02:00" + } + }, + "uid" : { + "__type" : "uid", + "uidString" : "t3BjTpCdDQCAUTe6A8ExpA==" + }, + "paragraphStyle" : { + "__type" : "textStyle" + }, + "string" : "To make that happen, we will need an application strategy. Building one should be fairly straightforward, since it mostly holds onto your LanguageLink application and builds new ones if needed." + }, + { + "__type" : "textSnippet", + "children" : { + "__type" : "snippets", + "items" : [ ] + }, + "createEmail" : { + "__type" : "email", + "emailString" : "" + }, + "createTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T18:31:19.494771+02:00" + } + }, + "editEmail" : { + "__type" : "email", + "emailString" : "" + }, + "editTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T18:32:06.868833+02:00" + } + }, + "uid" : { + "__type" : "uid", + "uidString" : "E6NbUZCdDQCAU8nsA8ExpA==" + }, + "paragraphStyle" : { + "__type" : "textStyle" + }, + "string" : "More interesting will be your coder model, which is responsible for taking source code from an application, handing it over to LanguageLink, and displaying the result. As such, it is the main link between the IDE and the execution engine." + }, + { + "__type" : "textSnippet", + "children" : { + "__type" : "snippets", + "items" : [ ] + }, + "createEmail" : { + "__type" : "email", + "emailString" : "" + }, + "createTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T18:32:07.688865+02:00" + } + }, + "editEmail" : { + "__type" : "email", + "emailString" : "" + }, + "editTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T18:34:29.49505+02:00" + } + }, + "uid" : { + "__type" : "uid", + "uidString" : "ypstVZCdDQCAYOo/A8ExpA==" + }, + "paragraphStyle" : { + "__type" : "textStyle" + }, + "string" : "In the section [[What now?]] I already showcased the method that is most central to that process: `bindAndExecute:`. In my case, {{gtMethod:GtCarpCoderModel>>#bindAndExecute:|expanded}} is the final result. A quick refresher:" + }, + { + "__type" : "textSnippet", + "children" : { + "__type" : "snippets", + "items" : [ ] + }, + "createEmail" : { + "__type" : "email", + "emailString" : "" + }, + "createTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T18:34:40.493148+02:00" + } + }, + "editEmail" : { + "__type" : "email", + "emailString" : "" + }, + "editTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T18:36:37.301362+02:00" + } + }, + "uid" : { + "__type" : "uid", + "uidString" : "WALsXZCdDQCBUGyRA8ExpA==" + }, + "paragraphStyle" : { + "__type" : "textStyle" + }, + "string" : "The method will take in a source string directly from coder and send it off to the evaluator handled by the LanguageLink application. Before that happens, though, it will most likely add some funky code around the snippet to capture the statement as a variable if possible, to allow for better interaction with GT." + }, + { + "__type" : "textSnippet", + "children" : { + "__type" : "snippets", + "items" : [ ] + }, + "createEmail" : { + "__type" : "email", + "emailString" : "" + }, + "createTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T18:36:42.952545+02:00" + } + }, + "editEmail" : { + "__type" : "email", + "emailString" : "" + }, + "editTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T18:37:38.760779+02:00" + } + }, + "uid" : { + "__type" : "uid", + "uidString" : "R7ljZZCdDQCBUlkLA8ExpA==" + }, + "paragraphStyle" : { + "__type" : "textStyle" + }, + "string" : "It might be more useful for you to start with a trimmed-down version of this method that doesn’t do anything with the bindings and just hands over the source string as-is. To harness the full power of interacting with GT, however, we will need to send variables and values back and forth sooner or later." + } + ] + }, + "createEmail" : { + "__type" : "email", + "emailString" : "" + }, + "createTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T18:08:06.046507+02:00" + } + }, + "editEmail" : { + "__type" : "email", + "emailString" : "" + }, + "editTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T18:08:06.046507+02:00" + } + }, + "pageType" : { + "__type" : "namedPage", + "title" : "Hooking up your LanguageLink server" + }, + "uid" : { + "__type" : "uuid", + "uuid" : "e53a48ff-8f9d-0d00-bfa2-217603c131a4" + } +} \ No newline at end of file diff --git a/lepiter/9py149uso8srjrtyq6opyrmzh.bak b/lepiter/9py149uso8srjrtyq6opyrmzh.bak new file mode 100644 index 0000000..ca71238 --- /dev/null +++ b/lepiter/9py149uso8srjrtyq6opyrmzh.bak @@ -0,0 +1,224 @@ +{ + "__schema" : "4.1", + "__type" : "page", + "children" : { + "__type" : "snippets", + "items" : [ + { + "__type" : "textSnippet", + "children" : { + "__type" : "snippets", + "items" : [ ] + }, + "createEmail" : { + "__type" : "email", + "emailString" : "" + }, + "createTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T18:38:08.96254+02:00" + } + }, + "editEmail" : { + "__type" : "email", + "emailString" : "" + }, + "editTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T18:38:10.799078+02:00" + } + }, + "uid" : { + "__type" : "uid", + "uidString" : "6bymapCdDQCBW13UA8ExpA==" + }, + "paragraphStyle" : { + "__type" : "textStyle" + }, + "string" : "You made it!" + }, + { + "__type" : "textSnippet", + "children" : { + "__type" : "snippets", + "items" : [ ] + }, + "createEmail" : { + "__type" : "email", + "emailString" : "" + }, + "createTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T18:38:12.929099+02:00" + } + }, + "editEmail" : { + "__type" : "email", + "emailString" : "" + }, + "editTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T18:38:56.257815+02:00" + } + }, + "uid" : { + "__type" : "uid", + "uidString" : "IW3tapCdDQCBXcd8A8ExpA==" + }, + "paragraphStyle" : { + "__type" : "textStyle" + }, + "string" : "If you read this booklet linearly from start to finish and followed along with your own implementation, you should now be able to execute code in custom Lepiter snippets! Exciting indeed!" + }, + { + "__type" : "textSnippet", + "children" : { + "__type" : "snippets", + "items" : [ ] + }, + "createEmail" : { + "__type" : "email", + "emailString" : "" + }, + "createTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T18:38:59.504959+02:00" + } + }, + "editEmail" : { + "__type" : "email", + "emailString" : "" + }, + "editTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T18:40:09.625312+02:00" + } + }, + "uid" : { + "__type" : "uid", + "uidString" : "4R2abZCdDQCBX8vQA8ExpA==" + }, + "paragraphStyle" : { + "__type" : "textStyle" + }, + "string" : "Now, of course, the fun doesn’t stop here. For full interactivity, we might need to (de)serialize values in an almost seamless manner, like JSLink does. To do that, we will have to make GT understand our language semantically rather than just syntactically." + }, + { + "__type" : "textSnippet", + "children" : { + "__type" : "snippets", + "items" : [ ] + }, + "createEmail" : { + "__type" : "email", + "emailString" : "" + }, + "createTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T18:40:10.264658+02:00" + } + }, + "editEmail" : { + "__type" : "email", + "emailString" : "" + }, + "editTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T18:41:08.513478+02:00" + } + }, + "uid" : { + "__type" : "uid", + "uidString" : "kHzzcZCdDQCBYawWA8ExpA==" + }, + "paragraphStyle" : { + "__type" : "textStyle" + }, + "string" : "We could also start using GT’s powers to visualize things about our codebase or to generate code, or we might want to create a custom IDE. Some of these fun adventures you will have to embark on on your own, but if you want some inspiration, the [[Bonus Chapters]] might hold some interest to you." + }, + { + "__type" : "textSnippet", + "children" : { + "__type" : "snippets", + "items" : [ ] + }, + "createEmail" : { + "__type" : "email", + "emailString" : "" + }, + "createTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T18:41:12.090085+02:00" + } + }, + "editEmail" : { + "__type" : "email", + "emailString" : "" + }, + "editTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T18:41:24.342689+02:00" + } + }, + "uid" : { + "__type" : "uid", + "uidString" : "nEKhdZCdDQCBgxEjA8ExpA==" + }, + "paragraphStyle" : { + "__type" : "textStyle" + }, + "string" : "I hope you enjoyed our little journey, congratulate you to your new po" + } + ] + }, + "createEmail" : { + "__type" : "email", + "emailString" : "" + }, + "createTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T18:38:07.35881+02:00" + } + }, + "editEmail" : { + "__type" : "email", + "emailString" : "" + }, + "editTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T18:38:07.35881+02:00" + } + }, + "pageType" : { + "__type" : "namedPage", + "title" : "Fin" + }, + "uid" : { + "__type" : "uuid", + "uuid" : "1d13a66a-909d-0d00-815a-f07803c131a4" + } +} \ No newline at end of file diff --git a/lepiter/9py149uso8srjrtyq6opyrmzh.lepiter b/lepiter/9py149uso8srjrtyq6opyrmzh.lepiter new file mode 100644 index 0000000..6e90ba9 --- /dev/null +++ b/lepiter/9py149uso8srjrtyq6opyrmzh.lepiter @@ -0,0 +1,224 @@ +{ + "__schema" : "4.1", + "__type" : "page", + "children" : { + "__type" : "snippets", + "items" : [ + { + "__type" : "textSnippet", + "children" : { + "__type" : "snippets", + "items" : [ ] + }, + "createEmail" : { + "__type" : "email", + "emailString" : "" + }, + "createTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T18:38:08.96254+02:00" + } + }, + "editEmail" : { + "__type" : "email", + "emailString" : "" + }, + "editTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T18:38:10.799078+02:00" + } + }, + "uid" : { + "__type" : "uid", + "uidString" : "6bymapCdDQCBW13UA8ExpA==" + }, + "paragraphStyle" : { + "__type" : "textStyle" + }, + "string" : "You made it!" + }, + { + "__type" : "textSnippet", + "children" : { + "__type" : "snippets", + "items" : [ ] + }, + "createEmail" : { + "__type" : "email", + "emailString" : "" + }, + "createTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T18:38:12.929099+02:00" + } + }, + "editEmail" : { + "__type" : "email", + "emailString" : "" + }, + "editTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T18:38:56.257815+02:00" + } + }, + "uid" : { + "__type" : "uid", + "uidString" : "IW3tapCdDQCBXcd8A8ExpA==" + }, + "paragraphStyle" : { + "__type" : "textStyle" + }, + "string" : "If you read this booklet linearly from start to finish and followed along with your own implementation, you should now be able to execute code in custom Lepiter snippets! Exciting indeed!" + }, + { + "__type" : "textSnippet", + "children" : { + "__type" : "snippets", + "items" : [ ] + }, + "createEmail" : { + "__type" : "email", + "emailString" : "" + }, + "createTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T18:38:59.504959+02:00" + } + }, + "editEmail" : { + "__type" : "email", + "emailString" : "" + }, + "editTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T18:40:09.625312+02:00" + } + }, + "uid" : { + "__type" : "uid", + "uidString" : "4R2abZCdDQCBX8vQA8ExpA==" + }, + "paragraphStyle" : { + "__type" : "textStyle" + }, + "string" : "Now, of course, the fun doesn’t stop here. For full interactivity, we might need to (de)serialize values in an almost seamless manner, like JSLink does. To do that, we will have to make GT understand our language semantically rather than just syntactically." + }, + { + "__type" : "textSnippet", + "children" : { + "__type" : "snippets", + "items" : [ ] + }, + "createEmail" : { + "__type" : "email", + "emailString" : "" + }, + "createTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T18:40:10.264658+02:00" + } + }, + "editEmail" : { + "__type" : "email", + "emailString" : "" + }, + "editTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T18:41:08.513478+02:00" + } + }, + "uid" : { + "__type" : "uid", + "uidString" : "kHzzcZCdDQCBYawWA8ExpA==" + }, + "paragraphStyle" : { + "__type" : "textStyle" + }, + "string" : "We could also start using GT’s powers to visualize things about our codebase or to generate code, or we might want to create a custom IDE. Some of these fun adventures you will have to embark on on your own, but if you want some inspiration, the [[Bonus Chapters]] might hold some interest to you." + }, + { + "__type" : "textSnippet", + "children" : { + "__type" : "snippets", + "items" : [ ] + }, + "createEmail" : { + "__type" : "email", + "emailString" : "" + }, + "createTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T18:41:12.090085+02:00" + } + }, + "editEmail" : { + "__type" : "email", + "emailString" : "" + }, + "editTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T18:41:41.069403+02:00" + } + }, + "uid" : { + "__type" : "uid", + "uidString" : "nEKhdZCdDQCBgxEjA8ExpA==" + }, + "paragraphStyle" : { + "__type" : "textStyle" + }, + "string" : "I hope you enjoyed our little journey and congratulate you to your new powers! See you around." + } + ] + }, + "createEmail" : { + "__type" : "email", + "emailString" : "" + }, + "createTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T18:38:07.35881+02:00" + } + }, + "editEmail" : { + "__type" : "email", + "emailString" : "" + }, + "editTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T18:38:07.35881+02:00" + } + }, + "pageType" : { + "__type" : "namedPage", + "title" : "Fin" + }, + "uid" : { + "__type" : "uuid", + "uuid" : "1d13a66a-909d-0d00-815a-f07803c131a4" + } +} \ No newline at end of file diff --git a/lepiter/9py149ytik0od40b9dfzhll4q.bak b/lepiter/9py149ytik0od40b9dfzhll4q.bak new file mode 100644 index 0000000..0ee4780 --- /dev/null +++ b/lepiter/9py149ytik0od40b9dfzhll4q.bak @@ -0,0 +1,224 @@ +{ + "__schema" : "4.1", + "__type" : "page", + "children" : { + "__type" : "snippets", + "items" : [ + { + "__type" : "textSnippet", + "children" : { + "__type" : "snippets", + "items" : [ ] + }, + "createEmail" : { + "__type" : "email", + "emailString" : "" + }, + "createTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T17:49:47.434396+02:00" + } + }, + "editEmail" : { + "__type" : "email", + "emailString" : "" + }, + "editTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T17:50:11.430885+02:00" + } + }, + "uid" : { + "__type" : "uid", + "uidString" : "YVM4u4+dDQC/eTezA8ExpA==" + }, + "paragraphStyle" : { + "__type" : "textStyle" + }, + "string" : "Building a LanguageLink client is easy, but not necessarily simple, since it involves writing a lot of mostly glue code." + }, + { + "__type" : "textSnippet", + "children" : { + "__type" : "snippets", + "items" : [ ] + }, + "createEmail" : { + "__type" : "email", + "emailString" : "" + }, + "createTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T17:50:15.711236+02:00" + } + }, + "editEmail" : { + "__type" : "email", + "emailString" : "" + }, + "editTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T17:52:49.226165+02:00" + } + }, + "uid" : { + "__type" : "uid", + "uidString" : "suJsv4+dDQC/h6EvA8ExpA==" + }, + "paragraphStyle" : { + "__type" : "textStyle" + }, + "string" : "As an entry-point, you will have to write a {{gtClass:LanguageLinkApplication}}. The (very spartan) one for Carp is {{gtClass:CarpApplication}}. Writing the application will give you a lay of the land, since it’s the central piece of infrastructure that holds on to all the rest." + }, + { + "__type" : "textSnippet", + "children" : { + "__type" : "snippets", + "items" : [ ] + }, + "createEmail" : { + "__type" : "email", + "emailString" : "" + }, + "createTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T17:52:50.001285+02:00" + } + }, + "editEmail" : { + "__type" : "email", + "emailString" : "" + }, + "editTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T17:53:48.39118+02:00" + } + }, + "uid" : { + "__type" : "uid", + "uidString" : "/4qmyI+dDQC/i6IPA8ExpA==" + }, + "paragraphStyle" : { + "__type" : "textStyle" + }, + "string" : "In the Carp application, I tried to use stock classes from LanguageLink wherever possible. Only when that didn’t work did I add classes. I also took inspiration from {{gtClass:JSLinkApplication}}, since it was there and seemed to provide rich functionality." + }, + { + "__type" : "textSnippet", + "children" : { + "__type" : "snippets", + "items" : [ ] + }, + "createEmail" : { + "__type" : "email", + "emailString" : "" + }, + "createTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T17:53:50.150304+02:00" + } + }, + "editEmail" : { + "__type" : "email", + "emailString" : "" + }, + "editTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T17:57:15.031561+02:00" + } + }, + "uid" : { + "__type" : "uid", + "uidString" : "hy01zI+dDQC/j6rJA8ExpA==" + }, + "paragraphStyle" : { + "__type" : "textStyle" + }, + "string" : "In total, all the classes that needed filling in were {{gtClass:CarpPostMortemDebugger}} (used in: {{gtMethod:CarpApplication>>debuggerClientFor:}}), {{gtClass:CarpExecutionHandler}} (used in {{gtMethod:CarpApplication>>#initializeHandlers}}), and all the classes in {{gtMethod:LanguageLinkSettings class>>carpDefaultSettings|expanded}} prefixed with `Carp`:" + }, + { + "__type" : "textSnippet", + "children" : { + "__type" : "snippets", + "items" : [ ] + }, + "createEmail" : { + "__type" : "email", + "emailString" : "" + }, + "createTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T17:57:33.45648+02:00" + } + }, + "editEmail" : { + "__type" : "email", + "emailString" : "" + }, + "editTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T17:59:17.862655+02:00" + } + }, + "uid" : { + "__type" : "uid", + "uidString" : "sPXB2I+dDQC/k6TTA8ExpA==" + }, + "paragraphStyle" : { + "__type" : "textStyle" + }, + "string" : "Implementing and understanding all of these classes might seem like a daunting task, and in many ways it is. But implementing most of these classes is simple and doesn’t at first require a thorough understanding of the system, at least that was the case for me. This means that you can just browse the existing implementors of any specific piece of infrastructure and “compare notes”. This approach got me f" + } + ] + }, + "createEmail" : { + "__type" : "email", + "emailString" : "" + }, + "createTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T17:49:04.110462+02:00" + } + }, + "editEmail" : { + "__type" : "email", + "emailString" : "" + }, + "editTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T17:49:04.110462+02:00" + } + }, + "pageType" : { + "__type" : "namedPage", + "title" : "Building a LanguageLink client" + }, + "uid" : { + "__type" : "uuid", + "uuid" : "3aab37bb-8f9d-0d00-bf78-07ff03c131a4" + } +} \ No newline at end of file diff --git a/lepiter/9py149ytik0od40b9dfzhll4q.lepiter b/lepiter/9py149ytik0od40b9dfzhll4q.lepiter new file mode 100644 index 0000000..3859554 --- /dev/null +++ b/lepiter/9py149ytik0od40b9dfzhll4q.lepiter @@ -0,0 +1,224 @@ +{ + "__schema" : "4.1", + "__type" : "page", + "children" : { + "__type" : "snippets", + "items" : [ + { + "__type" : "textSnippet", + "children" : { + "__type" : "snippets", + "items" : [ ] + }, + "createEmail" : { + "__type" : "email", + "emailString" : "" + }, + "createTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T17:49:47.434396+02:00" + } + }, + "editEmail" : { + "__type" : "email", + "emailString" : "" + }, + "editTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T17:50:11.430885+02:00" + } + }, + "uid" : { + "__type" : "uid", + "uidString" : "YVM4u4+dDQC/eTezA8ExpA==" + }, + "paragraphStyle" : { + "__type" : "textStyle" + }, + "string" : "Building a LanguageLink client is easy, but not necessarily simple, since it involves writing a lot of mostly glue code." + }, + { + "__type" : "textSnippet", + "children" : { + "__type" : "snippets", + "items" : [ ] + }, + "createEmail" : { + "__type" : "email", + "emailString" : "" + }, + "createTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T17:50:15.711236+02:00" + } + }, + "editEmail" : { + "__type" : "email", + "emailString" : "" + }, + "editTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T17:52:49.226165+02:00" + } + }, + "uid" : { + "__type" : "uid", + "uidString" : "suJsv4+dDQC/h6EvA8ExpA==" + }, + "paragraphStyle" : { + "__type" : "textStyle" + }, + "string" : "As an entry-point, you will have to write a {{gtClass:LanguageLinkApplication}}. The (very spartan) one for Carp is {{gtClass:CarpApplication}}. Writing the application will give you a lay of the land, since it’s the central piece of infrastructure that holds on to all the rest." + }, + { + "__type" : "textSnippet", + "children" : { + "__type" : "snippets", + "items" : [ ] + }, + "createEmail" : { + "__type" : "email", + "emailString" : "" + }, + "createTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T17:52:50.001285+02:00" + } + }, + "editEmail" : { + "__type" : "email", + "emailString" : "" + }, + "editTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T17:53:48.39118+02:00" + } + }, + "uid" : { + "__type" : "uid", + "uidString" : "/4qmyI+dDQC/i6IPA8ExpA==" + }, + "paragraphStyle" : { + "__type" : "textStyle" + }, + "string" : "In the Carp application, I tried to use stock classes from LanguageLink wherever possible. Only when that didn’t work did I add classes. I also took inspiration from {{gtClass:JSLinkApplication}}, since it was there and seemed to provide rich functionality." + }, + { + "__type" : "textSnippet", + "children" : { + "__type" : "snippets", + "items" : [ ] + }, + "createEmail" : { + "__type" : "email", + "emailString" : "" + }, + "createTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T17:53:50.150304+02:00" + } + }, + "editEmail" : { + "__type" : "email", + "emailString" : "" + }, + "editTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T17:57:15.031561+02:00" + } + }, + "uid" : { + "__type" : "uid", + "uidString" : "hy01zI+dDQC/j6rJA8ExpA==" + }, + "paragraphStyle" : { + "__type" : "textStyle" + }, + "string" : "In total, all the classes that needed filling in were {{gtClass:CarpPostMortemDebugger}} (used in: {{gtMethod:CarpApplication>>debuggerClientFor:}}), {{gtClass:CarpExecutionHandler}} (used in {{gtMethod:CarpApplication>>#initializeHandlers}}), and all the classes in {{gtMethod:LanguageLinkSettings class>>carpDefaultSettings|expanded}} prefixed with `Carp`:" + }, + { + "__type" : "textSnippet", + "children" : { + "__type" : "snippets", + "items" : [ ] + }, + "createEmail" : { + "__type" : "email", + "emailString" : "" + }, + "createTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T17:57:33.45648+02:00" + } + }, + "editEmail" : { + "__type" : "email", + "emailString" : "" + }, + "editTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T17:59:36.36081+02:00" + } + }, + "uid" : { + "__type" : "uid", + "uidString" : "sPXB2I+dDQC/k6TTA8ExpA==" + }, + "paragraphStyle" : { + "__type" : "textStyle" + }, + "string" : "Implementing and understanding all of these classes might seem like a daunting task, and in many ways it is. But implementing most of these classes is simple and doesn’t at first require a thorough understanding of the system, at least that was the case for me. This means that you can just browse the existing implementors of any specific piece of infrastructure and “compare notes”. This approach got me far enough to build a first version (with a little bit of added debugging), and it might get you there too!" + } + ] + }, + "createEmail" : { + "__type" : "email", + "emailString" : "" + }, + "createTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T17:49:04.110462+02:00" + } + }, + "editEmail" : { + "__type" : "email", + "emailString" : "" + }, + "editTime" : { + "__type" : "time", + "time" : { + "__type" : "dateAndTime", + "dateAndTimeString" : "2022-06-11T17:49:04.110462+02:00" + } + }, + "pageType" : { + "__type" : "namedPage", + "title" : "Building a LanguageLink client" + }, + "uid" : { + "__type" : "uuid", + "uuid" : "3aab37bb-8f9d-0d00-bf78-07ff03c131a4" + } +} \ No newline at end of file diff --git a/lepiter/lepiter.properties b/lepiter/lepiter.properties index 866596b..315e74a 100644 --- a/lepiter/lepiter.properties +++ b/lepiter/lepiter.properties @@ -2,5 +2,5 @@ "uuid" : "de53e067-3c99-0d00-9999-34310ea9f69b", "tableOfContents" : "98eic2d1mangtosf26n3aq420", "schema" : "4.1", - "databaseName" : "Adding a Language to the Glamorous Toolkit" + "databaseName" : "Adding a Language to Glamorous Toolkit" } \ No newline at end of file diff --git a/src/Carp/CarpProcess.class.st b/src/Carp/CarpProcess.class.st index 279dc7e..ab1c6aa 100644 --- a/src/Carp/CarpProcess.class.st +++ b/src/Carp/CarpProcess.class.st @@ -11,26 +11,27 @@ Class { #category : #'Carp-Processes' } +{ #category : #accessing } +CarpProcess class >> program [ + ^ 'carp' +] + { #category : #accessing } CarpProcess class >> resolveCarpPath [ | proc | proc := GtSubprocessWithInMemoryOutput new command: 'which'; - arguments: { 'carp' }. + arguments: { self program}. CarpPlatform subProcessEnvironmentDictionary keysAndValuesDo: [ :key :value | proc environmentAt: key put: value ]. proc runAndWait. (#(0 1) includes: proc exitCode) ifFalse: - [ self error: 'Unable to request carp location' ]. + [ self error: 'Unable to request', self program, ' location' ]. ^ proc stdout trim asFileReference ] -{ #category : #accessing } -CarpProcess class >> resolveNodejsPath [ -] - { #category : #accessing } CarpProcess class >> serverPath [ ^ CarpPath @@ -89,12 +90,17 @@ CarpProcess >> processArguments [ self settings serverDebugMode ifTrue: [ args add: '--inspect' ]. args - add: (self workingDirectory / 'src/languagelink.carp') resolve fullName; + add: (self workingDirectory / self programFile) resolve fullName; add: self settings serverSocketAddress port asString; add: self settings clientSocketAddress port asString. ^ args ] +{ #category : #accessing } +CarpProcess >> programFile [ + ^ 'src/languagelink.carp' +] + { #category : #accessing } CarpProcess >> serverPath [ | fileReference | diff --git a/src/Carp/CarpPythonProcess.class.st b/src/Carp/CarpPythonProcess.class.st new file mode 100644 index 0000000..a4ba71f --- /dev/null +++ b/src/Carp/CarpPythonProcess.class.st @@ -0,0 +1,15 @@ +Class { + #name : #CarpPythonProcess, + #superclass : #CarpProcess, + #category : #'Carp-Processes' +} + +{ #category : #accessing } +CarpPythonProcess class >> program [ + ^ 'python' +] + +{ #category : #accessing } +CarpPythonProcess >> programFile [ + ^ 'src/languagelink.py' +] diff --git a/src/Carp/LanguageLinkSettings.extension.st b/src/Carp/LanguageLinkSettings.extension.st index 13b8f09..f99e22e 100644 --- a/src/Carp/LanguageLinkSettings.extension.st +++ b/src/Carp/LanguageLinkSettings.extension.st @@ -9,7 +9,7 @@ LanguageLinkSettings class >> carpDefaultSettings [ serverSocketAddress: (LanguageLinkSocketAddress ipOrName: 'localhost' port: (9900 + 99 atRandom)); messageBrokerStrategy: LanguageLinkHttpMessageBroker; - serverProcessClass: CarpProcess; + serverProcessClass: CarpPythonProcess; platform: CarpPlatform new; commandFactoryClass: CarpCommandFactory; commandClass: LanguageLinkCommand;