How to compile a simple Command-line OCaml script into Javascript - javascript

I have a simple command line OCaml application that performs a computation on Sys.argv.(1) and outputs the result to stdout. I can compile it to Javascript with js_of_ocaml, but it gives me a lot of errors about caml_ml_output_char being undefined. I fixed those errors by stubbing out the printfs, so it runs, but it freezes firefox while running.
How can I cleanly compile simple OCaml command-line script into a Javascript based webpage; without maintaining a forked version or freezing the browser?

You will probably want to use webworkers, as running software not designed around Javascript's co-operative multi-tasking in the UI thread can cause the browser to lock up.
You can add the following header to the top of your OCaml file to overload the normal OCaml Sys and print implementations
(* JsHeader.ml *)
let output_buffer_ = Buffer.create 1000
let flush x=let module J = Js.Unsafe in let () = J.call
(J.variable "postMessage") (J.variable "self")
[|J.inject (Js.string (Buffer.contents output_buffer_))|]
in Buffer.clear output_buffer_
let print_string = Buffer.add_string output_buffer_
let print_char = Buffer.add_char output_buffer_
let print_newline () = print_char '\n'
let print_endline s = print_string (s^"\n"); flush ()
let caml_ml_output_char = print_char
let printf fmt = Printf.bprintf output_buffer_ fmt
module Printf = struct
include Printf
let printf fmt = Printf.bprintf output_buffer_ fmt
end
The most natural way to pass in commandline arguments is through the URL sent to the web worker. We can override the Ocaml Sys module to instead read ?argv as a sequence of null terminated strings.
module Sys = struct
let char_split delim s = (*Str.split is overkill*)
let hd = ref "" in let l = ref [] in
String.iter (fun c ->
if c = delim
then (l := (!hd)::(!l); hd := "")
else hd := (!hd) ^ (String.make 1 c)
) s;
List.rev ((!hd)::(!l))
let getenv x = List.assoc x Url.Current.arguments
let argv = Array.of_list (char_split '\x00' (getenv "?argv"))
let executable_name = argv.(0)
end
Now that we have entered the header we can enter a simple OCaml Command Line program:
(* cli.ml *)
let _ = print_string (Array.fold_left (^) "" (Array.make 40 (String.lowercase (Sys.argv.(1)^"\n"))) )
This command line program relies on the OS to flush the output, but we will have to manually flush the output.
You may also want to send a null character so the Javascript knows that the command has finished.
This can be achieved by appending the following footer.
(* JsFooter.ml *)
let _ = flush stdout; print_endline "\x00"
We can join the files and compile them as follows:
cat JsHeader.ml cli.ml JsFooter.ml > merged.ml
ocamlbuild -use-menhir -menhir "menhir" \
-pp "camlp4o -I /opt/local/lib/ocaml/site-lib js_of_ocaml/pa_js.cmo" \
-cflags -I,+js_of_ocaml,-I,+site-lib/js_of_ocaml -libs js_of_ocaml \
-lflags -I,+js_of_ocaml,-I,+site-lib/js_of_ocaml merged.byte
js_of_ocaml merged.byte
Now that we have created the file merged.js we can wrap the javascript in a simple web page such as the following:
<html>
<head>
<meta http-equiv="Content-Type" content="text/xhtml+xml; charset=UTF-8" />
<title>ml2js sample_cli</title>
<script type="text/javascript">
<!--
var worker;
function go () {
var output=document.getElementById ("output");
var argv = encodeURIComponent("/bin/sample_cli\0"+document.getElementById ("input").value);
if (worker) {
worker.terminate();
}
worker = new Worker ("sample_cli.js?argv="+argv);
document.getElementById ("output").value="";
worker.onmessage = function (m) {
if (typeof m.data == 'string') {
if (m.data == "\0\n") {
output.scrollTop = output.scrollHeight
} else {
output.value+=m.data;
}
}
}
}
//-->
</script>
</head>
<body onload=go()>
<textarea id="input" rows="2" cols="60" onkeyup="go()" onchange="go()" style="width:90%">SAMPLE_INPUT</textarea>
<button onclick="go()">go</button><br>
<textarea id="output" rows="0" cols="60" style="width:100%;height:90%" readonly onload=go()>
Your browser does not seem to support Webworkers.
Try Firefox, Chrome or IE10+.
</textarea>
</body>
</html>
See http://www.dansted.org/app/bctl-plain.html for an example of this approach in action, and https://github.com/gmatht/TimeLogicUnify/blob/master/ATL/js/webworker/ml2js.sh for a script that appends the appropriate headers, footers etc.

What js_of_ocaml's version are you using ? You should not get errors with caml_ml_output_char. When running on node, you should have sys.argv set correctly. In the browser, Sys.argv is set to [|"a.out"|].
Please open a GitHub issue on https://github.com/ocsigen/js_of_ocaml/issues/new if you still have an issue with this.

Related

in javascript how to call powershell script

I have a powershell script and I run on powershell like :
.\download-packages-license.ps1
But I want to call the javascript file before these lines.
var json =fs.readFileSync('../../dev/licenses/AllLicenses.json', 'utf8');
var options = {compact: true, ignoreComment: true, spaces: 4};
var result = convert.json2xml(json, options);
I could not anything in stackoverflow except : How to run a powershell script from javascript?
So pls help thanks
I think this will work for you -
var spawn = require("child_process").spawn;
spawn("powershell.exe",[".\download-packages-license.ps1"]);
You can work with : Node-Powershell
Code Snippet :
const Shell = require('node-powershell');
const ps = new Shell({
executionPolicy: 'Bypass',
noProfile: true
});
ps.addCommand('echo node-powershell');
ps.invoke()
.then(output => {
console.log(output);
})
.catch(err => {
console.log(err);
});
JScript
Simple way.
Works great. Suitable for simple operations, but loses some data when receiving line feeds as a single \n instead of the expected \r\n. Split by \r\n misinterprets the array which leads to the need to reformat the array of strings. Basically, I just wrote this, so there may be other problems.
var codepage='windows-1251';/*US-Europe-1252 and Js file in that codepage*/
var toPStext='Hello.\nПроверка русских буковок.';
var shell=new ActiveXObject('WScript.Shell');
var std=shell.Exec("C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe -ExecutionPolicy Bypass -command \
$OutputEncoding = [Console]::outputEncoding = [System.Text.Encoding]::GetEncoding('"+codepage+"'); \
Write-Output '"+toPStext+"'");
var output = std.StdOut.ReadAll().split('\r\n');// split('\n') - leads to the loss of some data
if (output.length>0){WScript.echo(output)}
//var x=WScript.StdIn.ReadLine();
Line by line.
Unfortunately, powershell does not accept external data as sequences of lines, unlike cmd.exe with /q /k options, which simplifies this code, but will turn around problems with multiline output code. Of course, if necessary, you can transfer the code as a base64 string to powershell
var codepage='windows-1251';/*US-Europe-1252 and Js file in that codepage*/
var toPStext='Hello.\nПроверка русских буковок.';
var shell=new ActiveXObject('WScript.Shell');
var output=[],errors=[],WshRunning=0,WshFinished=1,WshFailed=2,i=0,tryCount=0;
var std=shell.Exec("C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe -ExecutionPolicy Bypass -command \
$OutputEncoding = [Console]::outputEncoding = [System.Text.Encoding]::GetEncoding('"+codepage+"'); \
Write-Output '"+toPStext+"'");
do{
if (std.Status==WshFailed){
errors.push('String '+i+' data read error: \n '+std.StdErr.ReadLine());
tryCount++
}
else if(std.Status==WshRunning){
output.push(std.StdOut.ReadLine());
tryCount=0;
WScript.Echo('Running ...')
}
else if(std.Status==WshFinished){
var last=std.StdOut.ReadLine();
if(last.length>0){output.push(last)};last=undefined;
tryCount=21;
WScript.Echo('Finished ...')
}i++;
}while(tryCount<21);
if (output.length>0){WScript.echo(output)}
if (errors.length>0){WScript.echo(errors)}
var x=WScript.StdIn.ReadLine();

NodeJS child_process ansi in stdout

I want to get something like
[06:32:35] [Server thread/INFO]: [0;36;1m | [0;36;22m|__) [0;32;22mLuckPerms [0;36;1mv4.3.73[m
[06:32:35] [Server thread/INFO]: [0;36;1m |___ [0;36;22m| [0;30;1mRunning on Bukkit - CraftBukkit[m
but I get
[06:05:02] [Server thread/INFO]: | |__) LuckPerms v4.3.73
[06:05:02] [Server thread/INFO]: |___ | Running on Bukkit - CraftBukkit
When running minecraft server using child_process
prcs.stdout.on("data", function(d) {
console.log(d.toString());
});
Without knowing exactly how d is shaped, here is something that complies with your example, probably not exactly behaving as you need but you can always try and upgrade it (and at least it doesn't require any dependency):
const versionRegExp = /v[0-9]+(\.[0-9]+)*$/;
d.toString().split("\n").forEach((line) => {
// no idea what the spaces are made of
const exploded = line.trim().split(/[ \t]+/);
// add paddings to the first two structures
const first = exploded.shift().padEnd(5, ' ');
const second = exploded.shift().padEnd(7, ' ');
// work out the content
// condition based on `second`, or should it be remainder.match(versionRegExp) ?
const remainder = 0 === second.indexOf('|__)')
? `[0;30;1m${exploded.join(' ').replace(versionRegExp, '[0;36;1m$&')}[m`
: `[0;32;22m${exploded.join(' ')}[m`
;
// format line and display
console.log(`[0;36;1m${first}[0;36;22m${second}${remainder}`);
});

How to interpret the number returned from statSync

I want to get the file states of a specific .js file. so i used the below posted code. but the first log statement returned
33204
while the second one returned:
144
Output:
Stats {
dev: 2049,
mode: 33204,
nlink: 2,
uid: 1000,
}
but I want to interpret these number to know if the file is accessible for (read, write) or not
code:
const fs = require('fs');
var mode = fs.statSync('../../var/opt/personal/guest/op/op_12201/data/persGuesOapDataFolder00/test0.js').mode;
var writePermissions = mode & 0x92; // 010010010
console.log(mode);
console.log(writePermissions);
this mode indicates some info regarding the file like:
Permissions (for user, group and other)
type of file
To filter a bit this data, there are some masks defined at OS to extract the info we need. These masks are:
* For filter permissions (user, group and other)
The following mask values are defined for the file mode component of the st_mode field:
S_ISUID 04000 set-user-ID bit
S_ISGID 02000 set-group-ID bit (see below)
S_ISVTX 01000 sticky bit (see below)
S_IRWXU 00700 owner has read, write, and execute permission
S_IRUSR 00400 owner has read permission
S_IWUSR 00200 owner has write permission
S_IXUSR 00100 owner has execute permission
S_IRWXG 00070 group has read, write, and execute permission
S_IRGRP 00040 group has read permission
S_IWGRP 00020 group has write permission
S_IXGRP 00010 group has execute permission
S_IRWXO 00007 others (not in group) have read, write, and
execute permission
S_IROTH 00004 others have read permission
S_IWOTH 00002 others have write permission
S_IXOTH 00001 others have execute permission
* For detect type of file
The following mask values are defined for the file type:
S_IFMT 0170000 bit mask for the file type bit field
S_IFSOCK 0140000 socket
S_IFLNK 0120000 symbolic link
S_IFREG 0100000 regular file
S_IFBLK 0060000 block device
S_IFDIR 0040000 directory
S_IFCHR 0020000 character device
S_IFIFO 0010000 FIFO
Scenario 1: The file has read access (for everyone)
const fs = require('fs');
const mode = fs.statSync('./yourfile.txt').mode;
if (mode & (fs.constants.S_IRUSR | fs.constants.S_IRGRP | fs.constants.S_IROTH)) {
console.log('file has read permissions');
}
Scenario 2: Is a symbolic link
if (mode & fs.constants.S_IFLNK) {
console.log("It's a symbolic link");
} else {
console.log("It's not a symbolic link");
}
Hope this helps you to understand how OS works (unix systems). More info: http://man7.org/linux/man-pages/man7/inode.7.html
check if a stat object is readable or writable
useful to check if a pipe is readable or writable
const fs = require('fs');
const os = require('os');
function checkAccess(s, check="r") {
// check if s=fs.statSync(path) is readable or writable
const { uid: u, gid: g } = os.userInfo();
const m = s.mode;
if (check == "r") {
return (
((s.uid == u) && (m & fs.constants.S_IRUSR)) ||
((s.gid == g) && (m & fs.constants.S_IRGRP)) ||
(m & fs.constants.S_IROTH)
) != 0;
}
if (check == "w") {
return (
((s.uid == u) && (m & fs.constants.S_IWUSR)) ||
((s.gid == g) && (m & fs.constants.S_IWGRP)) ||
(m & fs.constants.S_IWOTH)
) != 0;
}
throw Exception("check must be r or w");
}
var s = fs.fstatSync(0); // fd 0 == stdin
console.log("fd 0 is readable?", checkAccess(s, "r"));
var s = fs.fstatSync(1); // fd 1 == stdout
console.log("fd 1 is writable?", checkAccess(s, "w"));
fs.writeSync(1, "hello\n");
see also: same thing in python
probably breaks on windows

Which (batch) language to learn for merging sourcefiles?

I'm creating a Greasemonkey/UserScript script. Because of the Greasemonkey sandbox I have to keep everything in one file but at 5k+ lines, maintenance is beginning to become rather difficult.
So I want to split up the script in multiple files and then combine them again for testing and/or releasing. I'd also have to be able to add some logic: Releases are per language for example (I don't want to ship German translations for the English release etc:).
As per the Pragmatic Programmer Tip 8 Invest Regularly in Your Knowledge Portfolio I'd like to learn some language to do this for me. What would be a good choice to merge the files rapidly and easily: makefile? Perl? RequireJS? Visual Studio macro's (I use VS.NET to write the UserScript)? Something else?
This is a bit of a late response, but if all you need to do is merge specific files, you should be able to do it from the command line with a batch script.
Something like:
#ECHO off
COPY file1.txt+file2.txt combined.txt
I rolled my own solution with Autohotkey. I had better used something else but well here is the ahk source:
inputFile := "sourceFileName"
savePath := "C:\Temp\"
saveAs := "targetFileName"
workingDirectory = %A_WorkingDir%
SetWorkingDir, %A_ScriptDir%
ParseFile(fileName, indentCount)
{
if not FileExist(fileName)
MsgBox Couldn't find: %fileName%
replacedFile =
Loop, Read, %fileName%
{
replacedFile .= ParseLine(A_LoopReadLine, indentCount) . "`r"
}
StringTrimRight, replacedFile, replacedFile, 1
return %replacedFile%
}
ParseLine(line, indentCount)
{
found =
FoundInclude := RegExMatch(line, "(^\s*)?//\<!--##INCLUDE "".*"" INDENT=\d //--\>", found)
if FoundInclude
{
; //<!--##INCLUDE "importit.txt" INDENT=X //-->
toIncludeFileName := RegExReplace(found, "^\s*")
StringMid, toIncludeFileName, toIncludeFileName, 18
closingQuotePosition := InStr(toIncludeFileName, """")
StringMid, newIndent, toIncludeFileName, closingQuotePosition + 9
StringMid, newIndent, newIndent, 1, 1
StringMid, toIncludeFileName, toIncludeFileName, 1, closingQuotePosition - 1
If toIncludeFileName
{
toIncludeContent := ParseFile(toIncludeFileName, newIndent)
StringReplace, line, line, %found%, %toIncludeContent%
}
else
{
StringReplace, line, line, %found%
}
}
else if indentCount
{
Loop %indentCount%
{
;line := " " . line
line := A_TAB . line
}
}
return %line%
}
; Keep backups of merges?
IfExist, %savePath%%saveAs%
{
backupCount := 0
backupFileName = %savePath%%saveAs%
while FileExist(backupFileName)
{
backupFileName = backup\%saveAs%%backupCount%
backupCount++
}
FileMove, %savePath%%saveAs%, %backupFileName%
FileCopy, %inputFile%, %backupFileName%_source
}
formattedOutput := ParseFile(inputFile, 0)
;fullFileName = %savePath%%SaveAs%
;MsgBox, %A_FileEncoding%
;file := FileOpen, fullFileName, "w"
FileEncoding, UTF-8-RAW
FileAppend, %formattedOutput%, %savePath%%SaveAs%
SetWorkingDir, workingDirectory
return
The sourceFileName looks like this:
function ready() {
var version = "//<!--##INCLUDE "version.txt" INDENT=0 //-->";
// User config
var user_data = {};
//<!--##INCLUDE "config\settings.js" INDENT=1 //-->
... more code ...
}
So the syntax for including a file is: //<!--##INCLUDE "fileToInclude" INDENT=X //--> with X being the indent level.

Markdown And Escaping Javascript Line Breaks

I am writing a markdown compiler in Erlang for server-side use. Because it will need to work with clients I have decided to adopt the client side library (showdown.js) as the specification and then test my code for compatibility with that.
In the first couple of iterations I built up 260-odd unit tests which checked that my programme produced output which was compatible with what I thought was valid markdown based on reading the syntax notes.
But now I am trying to write a javascript programme to generate my unit tests.
I have an input like:
"3 > 4\na"
I want to run 'showdown' on it to get:
"<p>3 > 4\na</p>"
and I want to stitch this up into an EUnit assertion:
"?_assert(conv(\"3 > 4\na\") == \"<p>3 > 4\na</p>\"),",
which is the valid Erlang syntax for the unit test. To make life easy, and to make the unit test generator portable I am doing it inside a web page so that by appending some lines to a javascript file and then view the page you get the new set of unit tests inside a <textarea /> which you then copy down into the module to run EUnit.
The problem is that I can't get the line breaks to convert to \n in the string so I end up with:
"?_assert(conv(\"3 > 4
a\") == \"<p>3 > 4
a</p>\"),",
I've tried converting the linefeeds to their escaped versions using code like:
text.replace("\\", "\\\\");
text.replace("\n", "\\n");
but no joy...
Tom McNulty helped me out and pointed out that my regex's were super-pants, in particular I wasn't using the global flag :(
The working code is:
var converter;
var text = "";
var item;
var input;
var output;
var head;
var tail;
converter = new Attacklab.showdown.converter();
item = document.getElementById("tests");
for (var test in tests) {
input = tests[test].replace(/[\n\r]+/gi,"\\n" );
input = input.replace(/[\"]+/gi, "\\\"");
output = converter.makeHtml(tests[test]).replace(/[\n\r]+/gi, "\\n");
output = output.replace(/[\"]+/gi, "\\\"");
text += " ?_assert(conv(\"" + input + "\") == \"" + output + "\"),\n";
};
head = "unit_test_() -> \n [\n";
tail = "\n ].";
text = text.slice(0, text.length - 2);
item.value = head + text + tail;

Categories

Resources