$ sudo npm install -g arc-js
Install nodejs first.
In mac:
# from port
$ sudo port install nodejs
$ sudo port install npm
# from brew
$ brew install node.js
In linux (debian):
$ sudo apt-get install nodejs
$ sudo apt-get install npm
$ sudo apt-get install node-legacy
Clone this repository and make.
$ git clone https://github.com/smihica/arc-js.git
$ cd arc-js
$ npm install
$ make; make test
Then go to your project.
$ npm install /ArcJS/Repository/PATH
$ node
> var ArcJS = require('arc-js');
> ArcJS.version
'X.X.X'
> var arc = ArcJS.context();
> arc.evaluate('(prn "hello world")');
hello world
'hello world'
>
$ cp /ArcJS/Repository/PATH/arc.min.js .
$ echo index.html
<html>
<head>
<script type="text/javascript" src="arc.min.js"></script>
<script>
var arc = ArcJS.context();
arc.evaluate('(prn "hello world")');
</script>
</head>
<body></body>
</html>
When open the webpage then "hello world" will be in console.
Using on a webpage (Arc) ----------------------
$ echo index.html
<html>
<head>
<script type="text/javascript" src="arc.min.js"></script>
<script type="text/javascript" src="jquery.min.js"></script>
<script type="text/javascript" src="arc-loader.js"></script>
<script type="text/arc">
(prn "hello world")
</script>
</head>
</html>
If you've installed via npm install -g
$ arcjs
arc>
otherwise npm install
$ node_modules/arc-js/bin/arcjs
arc>
everything following ;
is a comment (until end-of-line)
arc> t
t
arc> nil
nil
arc> 'a
a
arc> 'u-nk_~o#abc$$%%moemoe
u-nk_~o#abc$$%%moemoe
arc> '|a b c| ;; A symbol has delimiter strings
|a b c|
arc> 0
0
arc> 3.14
3.14
arc> -inf.0
-inf.0
arc> #x10 ;; hexadecimal notation
16
arc> #\a
#\a
arc> #\あ ;; unicode
#\あ
Escape characters are #\nul
#\null
#\backspace
#\tab
#\linefeed
#\newline
#\vtab
#\page
#\return
#\space
#\rubout
arc> #\newline
#\newline
arc> "abc"
"abc"
arc> "あいう" ;; unicode
"あいう"
arc> "a\nb"
"a\nb"
arc> "\u000A" ;; unicode
"\n"
arc> '(a b)
(a b)
arc> '(a . (b . c))
(a b . c)
arc> #/a/
#<regex /a/>
arc> #/^\w+@[a-zA-Z_]+?\.[a-zA-Z]{2,3}$/
#<regex /^\w+@[a-zA-Z_]+?\.[a-zA-Z]{2,3}$/>
arc> (table)
{}
arc> (table 'key 'val)
{key val}
n=d
indicates the number of properties. And to access the key
arc> (= tbl (table 'key 'val))
{key val}
arc> (tbl 'key)
val
arc> (tbl 'notfound)
nil
If the key is not found nil is returned.
Special syntax for table. { key value }
to be (table key value)
arc> { :key1 :value1 "key2" "value2" }
{:key1 :value1 "key2" "value2"}
keys can be any type.
arc> (= tbl {
:abc "def"
"GHI" 'jkl
'mno "pqr"
10 100
})
{:abc "def" "GHI" jkl mno "pqr" 10 100}
arc> (tbl :abc)
"def"
arc> (tbl "GHI")
jkl
arc> (tbl 'mno)
"pqr"
arc> (tbl 10)
100
arc> (tbl 'notfound)
nil
arc> (annotate 'my-type (table))
#<tagged my-type {}>
Arc (and most of all lisp languages) uses S-Expression.
arc> (+ 1 2)
3
arc> (+ (/ 1 2) 3)
3.5
To bind local variables, there are let
, with
and withs
syntax.
let
binds just 1 value
(let var val body)
arc> (let a 10
(+ a (* a 2)))
30
with
binds multiple values
(with (var1 val1 var2 val2) body)
arc> (with (x 3 y 4)
(sqrt (+ (expt x 2) (expt y 2))))
5
There is also withs
syntax that can bind sequentially.
(withs (var1 val1 var2 using-var1) body)
arc> (withs (x 3 y (* x 10))
(+ x y))
33
You also can use pattern matching in let / with / withs
.
arc> (let (a b c . d) '(1 2 3 . 4)
(* a b c d))
24
There is some condition statements. if
when
aif
awhen
case
(if condition then else)
In arc, all non-nil values are truthy.
arc> (if 0 'a 'b)
a
arc> (if nil 'a 'b)
b
arc> (if nil 'a)
nil
Use (no x) to invert the logic.
arc> (if (no nil) 'a 'b)
'a
arc> (if (no (odd 2)) 'a)
'a
In arc
(if a b c d e)
is same as
(if a
b
(if c
d
e))
arc> (is 'a 'a)
t
arc> (is '(a b) '(a b))
nil
arc> (iso '(a b) '(a b))
t
using =
, you can bind value into a variable.
arc> (= s '(f o o))
(f o o)
arc> s
(f o o)
You also can bind into a place.
arc> (= (s 0) 'm)
m
arc> s
(m o o)
You can define your own set function by using defset
.
arc> (defset caddr (x)
(w/uniq g
(list (list g x)
`(caddr ,g)
`(fn (val) (scar (cddr ,g) val)))))
Then
arc> (= (caddr s) 'v)
v
arc> s
(m o v)
to get more information for defset
read here.
You can run some expressions sequentially using do
or do1
. do
returns result of the last expression.
arc> (let x 2
(if (even x)
(do (prn x " is even value !!")
(* x 10))
(do (prn x " is odd value!!")
(/ x 10))))
2 is even value !!
20
do1
returns result of the first expression.
arc> (do1 (prn (+ 2 " is even value !!"))
(prn (+ 3 " is odd value !!")))
2 is even value !!
3 is odd value !!
"2 is even value !!"
def
defines new global function into current namespace.
arc> (def fizz-buzz (l)
(for n 1 l
(prn (case (gcd n 15)
1 n
3 'Fizz
5 'Buzz
'FizzBuzz))))
#<fn:fizz-buzz>
arc> (fizz-buzz 100)
1
2
Fizz
...
There are a lot of iterate syntax in arc. for
each
while
repeat
map
arc> (for i 1 10 (pr i " "))
1 2 3 4 5 6 7 8 9 10 nil
arc> (each x '(a b c d e)
(pr x " "))
a b c d e nil
arc> (let x 10
(while (> x 5)
(= x (- x 1))
(pr x)))
98765nil
arc> (repeat 5 (pr "la "))
la la la la la nil
arc> (map (fn (x) (+ x 10)) '(1 2 3))
(11 12 13)
[+ _ 10]
will be compiled to (fn (_) (+ _ 10))
arc> (map [+ _ 10] '(1 2 3))
(11 12 13)
arc> (mac when2 (tes . then) `(if ,tes (do ,@then)))
#<tagged mac #<fn:when2>>
arc> (when2 t 1 2 3)
3
Arc's mac creates legacy macros, so you can create macros that binds variables implicitly.
arc> (mac aif2 (tes then else)
`(let it ,tes
(if it ,then ,else)))
#<tagged mac #<fn:aif2>>
arc> (aif2 (car '(a b c)) it 'x)
a
By using w/uniq
, you can create one-time symbols.
arc> (mac prn-x-times (form times)
(w/uniq v
`(let ,v ,form
(do ,@(map (fn (_) `(prn ,v)) (range 1 times))
nil))))
#<tagged mac #<fn:prn-x-times>>
arc> (let i 5 (prn-x-times (++ i) 3))
6
6
6
nil
(w/uniq (v1 v2 v3 ...) body)
is also OK.
You can create continuations by using ccc
arc> (ccc
(fn (c)
(do (c 10)
(err))))
10
;; like yield
arc> (ccc
(fn (return)
(let x 0
(while t
(let adder
(or (ccc (fn (c)
(= next c)
(return x)))
1)
(++ x adder))))))
0
arc> (next nil)
1
arc> (next nil)
2
arc> (next 10)
12
arc> (next nil)
13
As an arc's function, there are macros that'll be expanded when a symbol matches some patterns. This function named symbol-syntax
. For example (car:cdr x)
will be expanded (car (cdr x))
(If there is :
in the symbol then expands).
arc> (car:cdr '(1 2 3))
2
And ~x
will be expanded (complement x)
arc> (if (~no 'a) 'b 'c)
c
You can check the expanded expression of symbol-syntax
by using ssexpand
.
arc> (ssexpand 'abc:def)
(compose abc def)
arc> (ssedpand '~no)
(complement no)
As ArcJS's expantion, there is a function that makes users be able to define arbitrary symbol-syntax
; defss
.
For example, lets define new special-syntax that is able to expand (caadaar x)
or (cadadadadadar x)
to expressions composed car
and cdr
.
arc> (defss cxr-ss #/^c([ad]{3,})r$/ (xs)
(let ac [case _ #\a 'car #\d 'cdr]
`(fn (x)
,((afn (xs) (if xs `(,(ac (car xs)) ,(self (cdr xs))) 'x))
(coerce (string xs) 'cons)))))
#<tagged special-syntax (#<regex /^c([ad]{3,})r$/> 12 #<fn:cxr-ss>)>
Then
arc> (ssexpand 'caaaar)
(fn (x) (car (car (car (car x)))))
arc> (ssexpand 'cadadar)
(fn (x) (car (cdr (car (cdr (car x))))))
So you are able to do this
arc> (caddddddddr '(1 2 3 4 5 6 7 8 9 0))
9
ArcJS has a namespace extension. It works like Clojure
's namespace To create a namespace use (defns xx)
.
arc> (defns A)
#<namespace A>
And then you can go into the namespace by using (ns namespace)
.
arc> (ns 'A)
#<namespace A>
arc:A>
Or you also can use string
or namespace object
as its' first argument.
;; using string
arc> (ns "A")
#<namespace A>
arc:A>
;; This way is the most commonly pattern.
arc> (ns (defns B))
#<namespace B>
arc:B>
As you see, the prompt has been changed to arc:A>
to describe where namespace we are in now.
To get the namespace that we are in now use (***curr-ns***)
.
arc:A> (***curr-ns***)
#<namespace A>
By the way, When you define a varibale named like ***VAR***
, You can access same value bound in it wherever you are. In a word, a variable named like ***VAR***
will behave a namespace global variable.
To export names use :export
like
arc> (defns A :export fn1 macro1 fn2)
Then, You can access fn1 / macro1 / fn2
in any namespaces that import namespace A
. If you don't specify :export
, every variables in the namespace will be exported.
And To import other namespace use :import
like
arc> (defns C :import A B)
Then, You can access values exported in namespace B and C when you go into namespace A. But you can't access values imported in B and C. By this time, namespace B and C must be loaded beforhand.
And there is also :extend
option.
arc> (defns D :extend A)
When you use it, you can extend the specified namespace. In the new namespace, you can access all the variables in specified namespace.
Now, Let's see how to work ArcJS on a webpage. First, We will begin with Hello Word
. Please create html like following.
<!doctype html>
<html>
<head>
<script type="text/javascript" src="//code.jquery.com/jquery-1.10.2.js"></script>
<script type="text/javascript" src="arc.min.js"></script>
<script type="text/javascript" src="arc_loader.js"></script>
</head>
<body>
<textarea id="holder" style="width:500px;height:600px;"></textarea>
</body>
</html>
arc.min.js <_static/arc.min.js>
arc_loader.js <_static/arc_loader.js>
Then please add some hello world
in ArcJS after arc_loader.js
...
<script type="text/javascript" src="arc_loader.js"></script>
<script type="text/arc">
(js/log "Hello world !!")
</script>
...
As you see, In case of you've loaded arc_loader.js
, The content in <script type="text/arc">...</script>
will be run in ArcJS's context on page onload
timing. And Of course you can do like this src="hello_world.arc"
to export arc code as another file. Like this.
...
<script type="text/javascript" src="arc_loader.js"></script>
<script type="text/arc" src="hello_world.arc"></script>
...
arc_loader.js
requires jQuery.
You've written Hello world
in Arc but there isn't a function named js/log
yet. So you need to define a primitive function named js/log
into ArcJS's namespace. Like this.
...
</body>
<script type="text/javascript">
var holder = $('#holder'), txt = '';
ArcJS.Primitives('user').define({
'js/log': [{dot: -1}, function(log) {
txt += log + "\n";
holder.text(txt);
}]
});
</script>
</html>
...
Then, js/log
is defined into ArcJS's user
namespace.
ArcJS.Primitives(string namespace).define({
'name': [option, function]
});
As you see, you can define a native function
into a specified namespace. {dot: -1}
on option
means that after the number of args will be a list have arbitrary length and will be passed like :rest parameter in CommonLisp. (-1
means there is no rest parameters) For examle, (fn args...) => {dot: 0}
or (fn a b args...) => {dot: 2}
.
The whole code will be like as follows.
<!doctype html>
<html>
<head>
<script type="text/javascript" src="//code.jquery.com/jquery-1.10.2.js"></script>
<script type="text/javascript" src="arc.min.js"></script>
<script type="text/javascript" src="arc_loader.js"></script>
<script type="text/arc">
(js/log "Hello world !!")
</script>
</head>
<body>
<textarea id="holder" style="width:500px;height:600px;"></textarea>
</body>
<script type="text/javascript">
var holder = $('#holder'), txt = '';
ArcJS.Primitives('user').define({
'js/log': [{dot: -1}, function(log) {
txt += log + "\n";
holder.text(txt);
}]
});
</script>
</html>
See example hw.html <_static/hw.html>
Then let's add FizzBuzz
.
<script type="text/arc">
(js/log "Hello world !!")
(def FizzBuzz (l)
(for n 1 l
(js/log:string
(case (gcd n 15)
1 n
3 'Fizz
5 'Buzz
'FizzBuzz))))
</script>
Then you have defined FizzBuzz
function. You can call it from JavaScript like this:
<script type="text/javascript">
ArcJS.Primitives('user').define({ /* ... */ });
$(function(){
var ctx = ArcJS.context();
ctx.evaluate('(FizzBuzz 100)');
});
</script>
Or of course you can simply do:
<script type="text/arc">
(def FizzBuzz (l)
;; ...
)
(FizzBuzz 100)
</script>
Whole code will be like as follows.
<!doctype html>
<html>
<head>
<script type="text/javascript" src="//code.jquery.com/jquery-1.10.2.js"></script>
<script type="text/javascript" src="arc.min.js"></script>
<script type="text/javascript" src="arc_loader.js"></script>
<script type="text/arc">
(js/log "Hello world !!")
(def FizzBuzz (l)
(for n 1 l
(js/log:string
(case (gcd n 15)
1 n
3 'Fizz
5 'Buzz
'FizzBuzz))))
</script>
</head>
<body>
<textarea id="holder" style="width:500px;height:600px;"></textarea>
</body>
<script type="text/javascript">
var holder = $('#holder'), txt = '';
ArcJS.Primitives('user').define({
'js/log': [{dot: -1}, function(log) {
txt += log + "\n";
holder.text(txt);
}]
});
$(function() {
var ctx = ArcJS.context();
ctx.evaluate('(FizzBuzz 100)');
});
</script>
</html>
See example fizzbuzz.html <_static/fizzbuzz.html>
This is an automatic reversi player. The main search logic is written in Arc. It searches 3 turns depth by using depth-first search and Alpha-beta pruning. You can customize the depth and the space of searching by configuring ev-depth
and ev-space
in reversi.arc
.
See example reversi.html <_static/reversi.html>
<!doctype html>
<html lang="en">
<head>
<title>Reversi</title>
<script type="text/javascript" src="//code.jquery.com/jquery-1.10.2.js"></script>
<script type="text/javascript" src="arc.min.js"></script>
<script type="text/javascript" src="arc_loader.js"></script>
<script type="text/arc" src="reversi.arc"></script>
</head>
<body>
<h3>Reversi</h3>
<canvas id="c1" width="600" height="600" style="float:left;border:1px solid #d3d3d3;"></canvas>
<textarea id="holder" style="float:left;width:500px;height:600px;font-family:Consolas,Monaco,monospace;font-size:18px;"></textarea>
<script type="text/javascript" src="reversi_bridge.js"></script>
<script type="text/arc">
(start-game)
</script>
</body>
</html>
The main search function in Arc.
(def get-best (board color depth)
((afn (board color depth target-color alpha beta)
(if (or (is depth 0)
(no (has-empty? board))) ;; last-depth or game-set
(list (get-points board target-color))
(withs (my-turn (is target-color color)
best-con (if my-turn > <)
best-fn (fn (a b) (best-con (car a) (car b)))
invert-color (invert color))
(iflet
puttable (get-puttable-positions-all board color)
(ccc
(fn (return)
(best
best-fn
(map
(fn (vp)
(let new-board (put vp board color)
(ret point-move (self new-board invert-color (- depth 1) target-color alpha beta)
(let point (car point-move)
(if my-turn
(when (> point alpha)
(= alpha point)
(if (>= alpha beta)
(return (cons beta vp)))) ;; alpha-cut
(when (< point beta)
(= beta point)
(if (>= alpha beta)
(return (cons alpha vp)))))) ;; beta-cut
(scdr point-move vp))))
(get-rand puttable ev-space))))) ;; cut-off if candidates are over space.
(self board invert-color (- depth 1) target-color alpha beta))))) ;; pass
board color depth color -inf.0 +inf.0))
See reversi.arc <_static/reversi.arc>
And the bridge code.
(function() {
var canvas = document.getElementById('c1'), size = 600;
var txt = '', holder = $('#holder');
function clear_board() {
var ctx = canvas.getContext('2d');
ctx.fillStyle = "rgb(0, 153, 0)";
ctx.fillRect(0, 0, size, size);
var unit = size / 8;
for (var i = 1, l = 8; i<l; i++) {
var x = i*unit;
for (var j=0; j<2; j++) {
ctx.beginPath();
ctx.moveTo(j?0:x, j?x:0);
ctx.lineTo(j?size:x, j?x:size);
ctx.closePath();
ctx.stroke();
}
}
}
function draw_stone(x, y, color) {
var ctx = canvas.getContext('2d');
var unit = size / 8;
var xp = (unit*x) + (unit/2);
var yp = (unit*y) + (unit/2);
var r = unit * 0.85;
ctx.beginPath();
ctx.arc(xp, yp, r/2, 0, 2 * Math.PI, false);
ctx.fillStyle = color;
ctx.fill();
ctx.lineWidth = 1;
ctx.strokeStyle = '#000';
ctx.stroke();
}
function js_log() {
txt += Array.prototype.slice.call(arguments).join(' ') + "\n";
holder.text(txt);
holder.scrollTop(holder[0].scrollHeight);
}
ArcJS.Primitives('user').define({
'js/clear-board': [{dot:-1}, clear_board],
'js/draw-stone': [{dot:-1}, draw_stone],
'js/log': [{dot:0}, js_log]
});
clear_board();
})();
reversi_bridge.js <_static/reversi_bridge.js>
You can pre-compile arc code to JavaSript. Pass your scripts to the arcjs
command.
$ echo "(prn (gcd 33 77))" > script.arc
$ arcjs script.arc
11
$
Specifiy scripts after -l
option.
$ echo "(def average (x y) (/ (+ x y) 2))" > avg.arc
$ arcjs -l avg.arc
arc> (average 10 20)
15
Use arcjsc
to comple arc to JavaScript.
$ echo "(def average (x y) (/ (+ x y) 2))" > avg.arc
$ arcjsc -o avg.js.fasl avg.arc
$ cat avg.js.fasl
// This is an auto generated file.
// Compiled from ['avg.arc'].
// DON'T EDIT !!!
preloads.push([
[12,7,14,20,0,1,0,20,2,-1,0,10,9,1, ...
]);
preload_vals.push(["2","+","/", ...
$
avg.js.fasl <_static/avg.js.fasl>
Then you can use the script in arcjs.
$ arcjs -l avg.js.fasl
arc> (average 10 20)
15
Or on a webpage.
<!doctype html>
<html lang="en">
<head>
<script type="text/javascript" src="//code.jquery.com/jquery-1.10.2.js"></script>
<script type="text/javascript" src="arc.min.js"></script>
<script type="text/javascript" src="arc_loader.js"></script>
<script type="text/arc-fasl" src="avg.js.fasl"></script>
</head>
<body>
<script type="text/arc">
(prn (average 10 20)) ;; will be printed in console.log()
</script>
</body>
</html>
See example fasl_example.html <_static/fasl_example.html>