Tau Prolog

A Prolog interpreter for the Web

๐Ÿš€ Jornadas de PROgramaciรณn y LEnguajes 2022

๐Ÿ˜ƒ Josรฉ Antonio Riaza Valverde (@jariazavalverde)

http://tau-prolog.org/files/prole2022

๐ŸŒŽ The Web

HTML โ†” ๐Ÿ’€ Structure
CSS โ†” ๐Ÿ‘ฆ Presentation
JavaScript โ†” ๐Ÿ’ช Behaviour

๐Ÿ“œ Tau Prolog

๐Ÿ“š Features

  • +150 builtin ISO predicates control constructs, atom processing, all solutions, IO, clause manipulation, ...
  • Packages lists manipulation, OS interactions, text formatting, DOM manipulation, ...
  • Program transformations term expansion, goal expansion, definite clause grammars, char conversion, ...
  • Mechanisms modules, meta-predicates, clause indexing, FFI (JavaScript), ...

Compatible with ๐ŸŒ browsers ...


                        <script type="text/javascript" src="tau-prolog.js"></script>
                        

... and ๐Ÿ’ป Node.js


                        const pl = require("tau-prolog");
                        

                        const session = pl.create(options);
                        session.consult(program, options);
                        session.query(goal, options);
                        session.answer(options);
                    

                        /* Consult */
                        session.consult(program, {
                          success: function() {
                            /* Query */
                            session.query(goal, {
                              success: function(goal) {
                                /* Answers */
                                session.answer({
                                  success: function(answer) { /* Answer */ },
                                  error: function(err) { /* Uncaught error */ },
                                  fail: function() { /* Fail */ },
                                  limit: function() { /* Limit exceeded */ }
                                })
                              },
                              error: function(err) { /* Error parsing goal */ }
                            });
                          },
                          error: function(err) { /* Error parsing program */ }
                        });
                    

๐Ÿ–๏ธ Sandbox

๐Ÿงฎ Example: Power set calculator

Type elements separated with commas and press Enter:

๐Ÿ‘ ๐Ÿ ๐Ÿ‰ ๐ŸŒ ๐Ÿˆ ๐Ÿ’ ๐Ÿ‡ ๐ŸŠ ๐Ÿ‹ ๐Ÿ“

๐Ÿงฎ Example: Power set calculator


:- use_module(library(dom)).
:- use_module(library(js)).

powerset([], []).
powerset([_|T], P) :- powerset(T, P).
powerset([H|T], [H|P]) :- powerset(T, P).

main :-
    get_by_id(powerset_input, Input),
    get_by_id(powerset_output, Output),
    bind(Input, keypress, Event,
      ( event_property(Event, code, 'Enter'),
        set_html(Output, ''),
        get_attr(Input, value, Value),
        apply(Value, split, [','], Xs),
        forall(powerset(Xs, P),
          ( create(div, Div),
            atomic_list_concat(P, S),
            set_html(Div, S),
            append_child(Output, Div)
          )
        )
      )
    ).

๐ŸŒณ Document Object Model (DOM)

  • Document representation as a logical tree
  • Interface between JavaScript and the document
  • Basis for dynamic web pages

๐Ÿ“ฆ Tau Prolog's DOM package

  • Add, modify, and remove HTML elements
  • Modify CSS styles
  • React to browser events

๐Ÿ” Retrieve HTML elements

๐Ÿ‘
๐Ÿ
๐Ÿ‰
๐ŸŒ
๐ŸŽ
๐Ÿ‡
๐Ÿ
๐Ÿ’

๐Ÿ”— Example: External links


                        :- use_module(library(dom)).

                        external_links(Domain) :-
                            forall(
                              (  get_by_tag(a, A),
                                 get_attr(A, href, Href),
                                 \+atom_concat(Domain, _, Href)
                              ), 
                              (  set_attr(A, target, '_blank')
                              )
                            ).
                        

โœจ Event handling

bind(+HTML, +EventType, -Event, +Goal)
unbind(+HTML, +EventType)
event_property(+Event, +Property, ?Value)
  • Event types click, mouseover, keypress, focus, blur, change, ...

โœ‹ Example: Drag & drop

  ๐Ÿ‘ ๐Ÿ ๐Ÿ‰ ๐ŸŒ ๐ŸŽ ๐Ÿ‡ ๐Ÿ  

                        :- use_module(library(dom)).
                        :- use_module(library(js)).

                        :- dynamic(draggable/1).
                        
                        main :-
                            once(get_by_tag(body, Body)),
                            bind(Body, mousemove, Event,
                              ( draggable(Elem),
                                event_property(Event, pageX, X),
                                event_property(Event, pageY, Y),
                                move(Elem, X, Y)
                              )
                            ),
                            bind(Body, mouseup, _,
                              ( retract(draggable(Elem)),
                                set_style(Elem, 'z-index', 0)
                              )
                            ),
                            forall(
                                get_by_class(draggable, Elem),
                                bind(Elem, mousedown, _,
                                  ( asserta(draggable(Elem)),
                                    set_style(Elem, 'z-index', 999)
                                  )
                                )
                            ).
                        
                        move(Elem, X, Y) :-
                            set_style(Elem, position, absolute),
                            get_prop(Elem, offsetWidth, Width),
                            get_prop(Elem, offsetHeight, Height),
                            Top is Y - Height/2,
                            Left is X - Width/2,
                            set_style(Elem, top, px(Top)),
                            set_style(Elem, left, px(Left)).
                        

tau-prolog.org/examples/draggable

โœ”๏ธ Example: Form validation


                            :- use_module(library(dom)).

                            % Zero or more
                            some(G) --> call(G), !, some(G).
                            some(_) --> [].

                            % One or more
                            many(G) --> call(G), some(G).
                            
                            % Alphabetical char
                            alpha -->
                                [X],
                                { char_code(X, C),
                                  C >= 65,
                                  C =< 122
                                }.
                            
                            % Numeric char
                            num -->
                                [X],
                                { char_code(X, C),
                                  C >= 48,
                                  C =< 57
                                }.
                            
                            % Alphanumeric char
                            alphanum --> alpha ; num.
                            
                            % Natural number
                            natural -->
                                many(num).
                            
                            % Email
                            email -->
                                many(alphanum), [@],
                                many(alphanum), ['.'],
                                many(alphanum).
                            
                            main :-
                                forall(
                                  (  get_by_class(form_check, Input),
                                     get_attr(Input, 'data-grammar', DCG)
                                  ),
                                  (  bind(Input, change, _,
                                       (  get_attr(Input, value, Value),
                                          atom_chars(Value, Chars),
                                          remove_class(Input, form_success),
                                          remove_class(Input, form_error),
                                          (  phrase(DCG, Chars)
                                          -> add_class(Input, form_success)
                                          ;  add_class(Input, form_error)
                                          )
                                       )
                                     )
                                  )
                                ).
                        

๐Ÿ’ฑ Foreign function interface (FFI)

Mechanism allowing a program written in a programming language to call routines written in another

๐Ÿ“ฆ Tau Prolog's JavaScript package

  • Invoke JavaScript functions
  • Perform AJAX requests
  • JavaScript object unification

๐Ÿ” JavaScript FFI

๐ŸŒน Example: Canvas


            :- use_module(library(dom)).
            :- use_module(library(js)).
            :- use_module(library(os)).
            
            rose(Petals, Step) :-
                get_by_id(rose, Rose),
                apply(Rose, getContext, ['2d'], Ctx),
                point(0.0, Petals, (X,Y)),
                apply(Ctx, clearRect, [0, 0, 200, 200], _),
                apply(Ctx, beginPath, [], _),
                apply(Ctx, moveTo, [X,Y], _),
                rose(Ctx, Petals, Step, 0.0).
                
            rose(Ctx, Petals, Step, T0) :-
                point(T0, Petals, (X,Y)),
                apply(Ctx, lineTo, [X,Y], _),
                apply(Ctx, stroke, [], _),
                apply(Ctx, beginPath, [], _),
                apply(Ctx, moveTo, [X,Y], _),
                sleep(1),
                T1 is T0 + Step,
                rose(Ctx, Petals, Step, T1).
                
            point(T, Petals, (X,Y)) :-
                X is 100 + 90 * cos(Petals * T) * cos(T),
                Y is 100 + 90 * cos(Petals * T) * sin(T).
                        

๐Ÿ Example: Snake


                            :- use_module(library(lists)).
                            :- use_module(library(random)).
                            :- use_module(library(dom)).
                            :- use_module(library(js)).
                            :- use_module(library(os)).
                            
                            :- dynamic(direction/1).
                            direction(down).
                            
                            key_direction(w, up) :-
                                direction(Direction),
                                Direction \== down.
                            key_direction(a, left) :-
                                direction(Direction),
                                Direction \== right.
                            key_direction(d, right) :-
                                direction(Direction),
                                Direction \== left.
                            key_direction(s, down) :-
                                direction(Direction),
                                Direction \== up.
                            
                            snake :-
                                once(get_by_tag(body, Body)),
                                bind(Body, keydown, Event, (
                                    event_property(Event, key, Key),
                                    key_direction(Key, Direction),
                                    prevent_default(Event),
                                    retractall(direction(_)),
                                    asserta(direction(Direction))
                                )),
                                get_by_id(snake, Canvas),
                                apply(Canvas, getContext, ['2d'], Ctx),
                                random_between(1, 20, X),
                                random_between(1, 20, Y),
                                snake(Ctx, [(1,1)], (X,Y)).
                            
                            snake(Ctx, Snake, Point) :-
                                get_time(T0),
                                draw(Ctx, Snake, Point),
                                direction(Direction),
                                update(Snake, Point, Direction, Snake1, Point1),
                                get_time(T1),
                                Timeout is max(0, 60-round(T1-T0)),
                                set_timeout(Timeout, snake(Ctx, Snake1, Point1), _).
                            
                            next_position(left, (1,Y), (20,Y)).
                            next_position(left, (X0,Y), (X1,Y)) :-
                                succ(X1, X0).
                            next_position(right, (20,Y), (1,Y)).
                            next_position(right, (X0,Y), (X1,Y)) :-
                                succ(X0, X1).
                            next_position(up, (X,1), (X,20)).
                            next_position(up, (X,Y0), (X,Y1)) :-
                                succ(Y1, Y0).
                            next_position(down, (X,20), (X,1)).
                            next_position(down, (X,Y0), (X,Y1)) :-
                                succ(Y0, Y1).
                            
                            inits([_], []).
                            inits([X,Y|T], [X|S]) :-
                                inits([Y|T], S).
                            
                            update(Snake0, Point0, Direction, [Head1|Snake1], Point1) :-
                                Snake0 = [Head0|_],
                                next_position(Direction, Head0, Head1),
                                ( member(Head1, Snake0) ->
                                  Snake1 = [],
                                  Point1 = Point0 ;
                                  ( Head1 == Point0 ->
                                    Snake1 = Snake0,
                                    Point1 = (X,Y),
                                    random_between(1,20,X),
                                    random_between(1,20,Y)
                                  ; inits(Snake0, Snake1),
                                    Point1 = Point0
                                  )
                                ).
                            
                            draw(Ctx, Snake, Point) :-
                                apply(Ctx, clearRect, [0,0,200,200], _),
                                draw_point(Ctx, Point, red),
                                forall(
                                    member(Body, Snake),
                                    draw_point(Ctx, Body, black)
                                ).
                            
                            draw_point(Ctx, (X,Y), Color) :-
                                PointX is (X-1)*10,
                                PointY is (Y-1)*10,
                                set_prop(Ctx, fillStyle, Color),
                                apply(Ctx, beginPath, [], _),
                                apply(Ctx, rect, [PointX,PointY,10,10], _),
                                apply(Ctx, fill, [], _).            
                        

๐Ÿ”ฎ Concurrent (asynchronous) programming

Asynchronous operations let programs complete work while waiting for another operations to finish

  • Long tasks can monopolize the UI thread for extended periods of time and block other critical tasks from being executed
๐ŸŒ

                        :- use_module(library(dom)).
                        :- use_module(library(random)).

                        fruit(F) :-
                            random_member(F, ['๐Ÿ‘','๐Ÿ','๐Ÿ‰','๐ŸŒ','๐ŸŽ','๐Ÿ‡','๐Ÿ']).

                        fruit_roulette(Roulette) :-
                            fruit(Fruit),
                            set_html(Roulette, Fruit),
                            fruit_roulette(Roulette).
                    

๐Ÿ” Asynchronous tasks

๐Ÿ“ฆ Tau Prolog's Concurrent package

Future objects represent the eventual completion or failure of asynchronous tasks

future(+Template, +Goal, -Future)
await(+Future, ?Value)
future_done(+Future)
future_all(+List, -Future)
future_any(+List, -Future)

๐Ÿด Example: Hippodrome

Type the number of horses and press Enter:

๐Ÿ‡

๐Ÿด Example: Hippodrome


                        :- use_module(library(dom)).
                        :- use_module(library(js)).
                        :- use_module(library(os)).
                        :- use_module(library(concurrent)).
                        :- use_module(library(random)).
            
                        hippodrome :-
                            get_by_id(horses, Input),
                            get_by_id(hippodrome, Hippodrome),
                            bind(Input, keypress, Event,
                              ( event_property(Event, code, 'Enter'),
                                set_html(Hippodrome, ''),
                                get_attr(Input, value, Value),
                                atom_chars(Value, Chars),
                                number_chars(N, Chars),
                                findall(F,
                                  ( between(1, N, _),
                                    make_horse(Hippodrome, Horse),
                                    future(Horse, run_horse(Horse), F)
                                  ), Fs),
                                future_any(Fs, F),
                                await(F, Winner),
                                set_html(Winner, '๐Ÿ‘‘')
                              )
                            ).   
                            
                        make_horse(Hippodrome, Horse) :-
                            create(div, Horse),
                            set_html(Horse, '๐Ÿ‡'),
                            add_class(Horse, horse),
                            append_child(Hippodrome, Horse).

                        run_horse(Horse) :-
                            get_style(Horse, left, px(Left)),
                            ( Left =< 0 ->
                              true
                            ; random_between(1, 10, Jump),
                              Left2 is max(0, Left - Jump),
                              set_style(Horse, left, px(Left2)),
                              random_between(5, 15, Sleep),
                              sleep(Sleep),
                              run_horse(Horse)
                            ).
                        

Tau Prolog

A Prolog interpreter for the Web