diff --git a/ob-dart.el b/ob-dart.el index 4ecd9bf..c4ae4bd 100644 --- a/ob-dart.el +++ b/ob-dart.el @@ -40,15 +40,41 @@ ;;; Code: (require 'ob) -;(use-package 'f) (defvar org-babel-tangle-lang-exts) (add-to-list 'org-babel-tangle-lang-exts '("dart" . "dart")) -(defvar org-babel-default-header-args:dart '()) +(defvar ob-dart-default-header-args '()) + (defvar ob-dart-command "dart" "Name of the command to use for executing Dart code. Windows support is pending.") + +(defun ob-dart-variable-assignments (params) + "Return a list of Dart statements assigning the block's variables. +The assignments are defined in PARAMS." + (mapcar + (lambda (pair) + (format "var %s = %s;" + (car pair) + (ob-dart-var-to-dart (cdr pair)))) + (org-babel--get-vars params))) + +(defun ob-dart-var-to-dart (var) + "Convert an elisp value to a Dart variable. +Convert an elisp value, VAR, into a string of Dart source code +specifying a variable of the same value." + (cond + ((listp var) + (concat "[" (mapconcat #'ob-dart-var-to-dart var ", ") "]")) + ((stringp var) + (format "\"%s\"" (substring-no-properties var))) + ((numberp var) + (number-to-string var)) + ((booleanp var) + (if var "true" "false")) + (t (format "%s" var)))) + (defun org-babel-execute:dart (body params) "Execute a block of Dart code with org-babel. This function is called by `org-babel-execute-src-block'. @@ -62,26 +88,17 @@ Args: or for the key `:results', (value raw) from Babel args `:results value raw'." (message "executing Dart source code block") - ;; (processed-params = org-babel-process-params RETURNS assoc list with slightly - ;; reformatted list of :param values from all elements after #+begin_src - ;; For example, from ':results output raw', the function creates two assoc elements, - ;; one is (:result-type . output), second is (:result-params . raw) - ;; Variables are converted to PROPERTY LIST in the result. - ;; Other less frequent parameters like (:colname-names . (list names)) are cons cells. - (let* ((processed-params (org-babel-process-params params)) - (session (ob-dart-initiate-session (nth 0 processed-params))) - (vars (nth 1 processed-params)) - (result-params (nth 2 processed-params)) + (let* ((ob-dart-command + (or (cdr (assq :dart params)) + ob-dart-command)) + (vars (ob-dart-variable-assignments params)) + (result-params (nth 2 (org-babel-process-params params))) (result-type (cdr (assoc :result-type params))) (full-body (org-babel-expand-body:generic - body params)) - ;; ob-dart-evaluate is the core function, which - ;; - generates dart code from `body' wrapped in `ob-dart-wrapper' - ;; - calls the generated dart code in shell - ;; - processes the shell's stdout and stderr + body params + vars)) (result (ob-dart-evaluate - session full-body result-type result-params))) - + nil full-body result-type result-params))) (org-babel-reassemble-table result (org-babel-pick-name @@ -89,7 +106,6 @@ Args: (org-babel-pick-name (cdr (assoc :rowname-names params)) (cdr (assoc :rownames params)))))) - (defun ob-dart-evaluate (session body &optional result-type result-params) "Evaluate BODY in external Dart process. If RESULT-TYPE equals `output' then return standard output as a string. @@ -111,96 +127,12 @@ Args: ;; Set the name generated-filename='generated-filename=/tmp/dart-RAND.dart' (let* ((generated-filename (org-babel-temp-file "dart-"))) - + ;; Create 'temp-file' named 'generated-filename', ;; and insert into it the Dart code generated from body. - (ob-dart-write-dart-file-from body generated-filename) - ;; In this section below, we call 'org-babel-eval'. - ;; The 'org-babel-eval' runs the first arg 'command' by delegating - ;; the run to 'org-babel--shell-command-on-region'. - ;; The region is in the "current buffer", which is the 'temp-buffer' - ;; created as the "current buffer". The 'temp-buffer' is the buffer where - ;; everything in the 'org-babel--shell-command-on-region' runs, see below. - ;; The 'temp-buffer' is populated with the - ;; second arg QUERY (we set QUERY to "", so only 'command' runs) - ;; We do build the 'command' below as a string - ;; concat of 'org-babel-dart-command' => dart and the 'generated-filename'; - ;; this creates a string which, if executed, - ;; runs OS process as "dart generated-filename". - ;; The 'generated-filename=/tmp/dart-RAND.dart' was named above using - ;; 'org-babel-temp-file'. The 'generated-filename' contains the BEGIN_SRC..END_SRC. - ;; - ;; In detail, the 'org-babel-eval', works like this: - ;; 1. Lets - 'error-buffer' = " *Org-Babel Error*", creates or erases it. - ;; 2. Creates a 'temp-buffer' ('with-temp-buffer' rest of work done here) - ;; where it inserts the QUERY (empty) - ;; 3. As if cursor was put in the temp-buffer, it delegates to - ;; 'org-babel--shell-command-on-region' 'command' 'error-buffer' - ;; where - ;; 'command'='dart /tmp/dart-RAND.dart "output|value"' - ;; 4. On success of the - ;; 'org-babel--shell-command-on-region' 'command' 'error-buffer ' - ;; its stdout is placed in the 'temp-buffer' - ;; in which context steps 3-6 run - ;; (temp-buffer is 'scratch' in testing). - ;; On error, error text is placed in the 'error-buffer'. - ;; 5. Where are we: After step 4, - ;; the 'temp-buffer' contains the stdout from Dart code 'println', - ;; which may be: - ;; - either output from internal 'print' (if results: 'output') - ;; - or toString() on the 'return lastStatement' (results: 'value') - ;; See the 'org-babel-dart-wrapper-method' for Dart code that runs the . - ;; 6. The contents of the temp-buffer is returned to caller as - ;; 'buffer-string' ; string of the temp-buffer, stdout from Dart. - ;; - ;; Note: Test this step 3. in 'scratch' as if the temp-buffer was scratch: - ;; - Place pointer to 'scratch' and do - ;; - Alt-: (org-babel--shell-command-on-region "echo Hello" "") - ;; this runs "echo Hello" is shell, and places - ;; the string Hello in 'scratch' - the 'temp-buffer'! - ;; - ;; - ;; - ;; Details of 'org-babel--shell-command-on-region' 'command' 'error-buffer' - ;; 1. Assumes it is run when pointer is in the 'temp-buffer' created - ;; by caller 'org-babel-eval' - 'temp-buffer' is 'scratch' in our testing. - ;; 2. Lets variables - ;; - input-file named 'ob-input-TEMP-RAND' - ;; - error-file named 'ob-error--TEMP-RAND' - ;; - shell-file-name /bin/sh - ;; 3. Copies the temp-buffer where the pointer is, to 'input-file'; - ;; at the time this is called, the 'temp-buffer' contains the QUERY, - ;; which is a zero-lenght string "" in our case. - ;; 3. Calls 'process-file' (this is like 'call-process' with args: - ;; (process-file - ;; program = shell-file-name ; has /bin/sh - ;; infile = input-file ; has QUERY - ;; buffer = has cons (t . error-file) ; see call-process - ;; display = nil ; has nil - ;; args1 = sh-switch ; has probably empty - ;; args2 = command ; 'dart /tmp/dart-RAND.dart "output|value"' - ;; which calls apply on: - ;; call-process ; runs program synchronously in separate process - ;; program = program ; has /bin/sh - ;; infile = input-file ; has QUERY - would be stdin of command - ;; destination= ; has cons(t . error-file) - ;; ; how to handle program output - ;; ; t = "use current buffer for stdout" - ;; ; error-file is where strerr goes - ;; display ; has nil, means no action - ;; args1 = sh-switch ; has probably empty - ;; args2 = command ; 'dart /tmp/dart-RAND.dart "output|value"' - ;; - ;; 4. The above step 3 means: - ;; - Exec 'dart /tmp/dart-RAND.dart "output|value"' - ;; - Place stdout to current buffer (t), which is the with-temp-buff - ;; created by 'org-babel-eval' - ;; - Places stderr in the error-file viewed by the error-buffer - ;; - - ;; Run org-babel-eval, a shell process that calls dart on the generated-filename. + ;; Run org-babel-eval (let ((raw (org-babel-eval (concat ob-dart-command " " generated-filename " " (symbol-name result-type)) ""))) ;; result-type: both 'value and 'output formats results as table, unless raw is specified @@ -220,11 +152,6 @@ Args: body-part-top wrapper body-part-main))) - ;;(f-write-text - ;; generated-dart-str - ;; 'utf-8 - ;; generated-filename) - (with-temp-file generated-filename (insert generated-dart-str)))) @@ -254,7 +181,7 @@ Comments: can be split into 2 parts, then processed with a common wrapper to generate the Dart source that is executed. -3. The WRAPPER contains format symbols as %s, where block parts are inserted. +3. The WRAPPER contains format symbols as %s, %w, %a. Currently only BODY-PART-MAIN is inserted. 4. If main function exists in BODY: @@ -266,15 +193,15 @@ Currently only BODY-PART-MAIN is inserted. - `:results output' and `:results value' are supported" (let* ( - (main-function-line-index (string-match "^ *\\([A-z0-9_<>]*\\) *main *(.*" body))) + (main-function-line-index (string-match "^ *void *main *\\(\\([A-Za-z0-9_<>]*\\)\\)? *\\(.*\\)" body))) (if main-function-line-index ;; If main function exists in body, - ;; ob-dart uses this Org Babel souce block unchanged, + ;; ob-dart uses this Org Babel source block unchanged, ;; with imports and all code. ;; Only :results output are supported in this branch. - ;; Dart code is "import 'dart:async';\n", appended with body. - ;; The wrapper becomes "%s", effectively becomes the body. + ;; Dart code is \"import 'dart:async';\n\", appended with body. + ;; The wrapper becomes \"%s\", effectively becomes the body. (list "%s" "import 'dart:async';\n" body) ;; main function NOT in body, passed wrapper MUST be ob-dart-wrapper @@ -288,7 +215,7 @@ appended with WRAPPER string which contains format symbols as %s, %w, %a. The WRAPPER is inserted with contents of format-specs at appropriate symbols, and BODY-PART-MAIN at WRAPPER's symbol %s. -The wrapper can be just `%s' then the BODY-PART-MAIN +The wrapper can be just \"%s\" then the BODY-PART-MAIN becomes the WRAPPER, and is appended after BODY-PART-TOP. Comments: @@ -308,12 +235,9 @@ is not part of this method." (format-spec wrapper `((?a . "async ") (?w . "await ") (?s . ,body-part-main)) - ; ignore missing symbols %a in wrapper nil - ; split to list nil))) - (defvar ob-dart-wrapper-imports " import 'dart:async'; @@ -327,7 +251,7 @@ import 'dart:isolate'; import 'dart:math'; import 'dart:mirrors'; " -"Documentation: Variable which returns Dart wrapper imports as String. + "Documentation: Variable which returns Dart wrapper imports as String. Created from wrapper-imports.drap. @@ -345,8 +269,8 @@ class Gen { /// if org file has NO MAIN, code is from between #+begin_src and #+end_src. /// if org file has MAIN, code is copied from main. /// Either way, async is added if the code block contains await. - /// That is theory: for files with MAIN, we add async here if the main is async. - runBlock(List args) %a { + /// That is theory: for files with MAIN, we add async here if the main is async. + runBlock(Map vars) %a { // - Org code block from begin_src .. end_src inserted here by elisp format. // - See `ob-dart-wrapper` and `format-spec` in wrap-body.esh and ob-dart.el %s @@ -358,10 +282,9 @@ class Gen { /// /// See the [runBlockResultsValue] for description how the async propagation /// and await-ing result just before print. - runBlockResultsOutput(List args) %a { - runBlock(args); + runBlockResultsOutput(Map vars) %a { + runBlock(vars); } - /// Runs the BEGIN_SRC .. END_SRC source block. /// /// Uses [ZoneSpecification] to skip the print to stdout, @@ -376,11 +299,11 @@ class Gen { /// /// The [returnedValue.toString] result pops up in elisp code /// in the 'ob-dart.el' function - /// 'org-babel-dart-evaluate'. + /// 'ob-dart-evaluate'. /// This function parses and manipulates the [returnedValue.toString] returned from here. /// /// The elisp parsing and manipulation of the [returnedValue.toString] in - /// the 'org-babel-dart-evaluate' depends on the parameters passed in + /// the 'ob-dart-evaluate' depends on the parameters passed in /// `:results [output|value] [raw]` /// for example, for: /// - [value raw] there is no parsing or manipulation of the result. @@ -422,7 +345,7 @@ class Gen { /// The [runBlock] runs async, /// but BEFORE WE PRINT IN CALLER, this thread WAITs, making async to resolve /// the future [runBlock] returnedValue BACK INTO this FLOW (THREAD) before print. - runBlockResultsValue(List args) %a { + runBlockResultsValue(List args) %a { var returnedValue; /// Runs it's [body], the function in the first argument, /// in a new [Zone], based on [ZoneSpecification]. @@ -453,10 +376,10 @@ class Gen { } } - main(List args) %a { +void main(List args) %a { var results_collection_type = null; if (args.length > 0) { - results_collection_type = args.elementAt(0); + results_collection_type = args[0]; } if (results_collection_type == 'output') { @@ -475,8 +398,7 @@ class Gen { } } " - -"Documentation: Variable which returns Dart wrapper code as String. + "Documentation: Variable which returns Dart wrapper code as String. Created from wrapper.drap @@ -491,12 +413,12 @@ If the passed argument to is: - `output': - The stdout from Dart print() in the snippet is send - to the standart output, and becomes the #+RESULT. + to the standard output, and becomes the #+RESULT. - `value': - The stdout from Dart print() is blocked by the `runZoned' - method, and the `toString()' of the last `return lasExpression' - becomes the #+RESULT." -) + method, and the `toString()' of the last `return lastExpression' + becomes the #+RESULT.") + (defun ob-dart-table-or-string (results) "Convert RESULTS into an appropriate elisp value.