I’ve been toying with emitting Prolog from Coil and its been a ton of fun!
Here’s the basic idea:
// coil code
let ten = 10
// existing coil ast
[
{type: "let",
assign_expr: { type: "id_assign", name: "ten" },
expr: { type: "num", value: 10 }
} ]
% generated prolog ast
, nid_3, nid_2).
id_assign(ten, nid_1).
let(nid_2, 1, 1).
line_and_col(nid_2, 1, 5).
line_and_col(nid_310, nid_2).
num(
% lets ignore metadata give better
% names to the nids (node ids)
, _id_assign_ctx, let_ctx).
id_assign(ten, _top_level_ctx).
let(let_ctx10, let_ctx). num(
The primary difference between these 2 approaches is that with prolog the relationships are (easily) preserved.
For example when I’m traversing the AST when I’m looking at the node
{ type: "num", value: 10 }
, without doing extra work to go
back up the tree I have no way to tell that it belongs to the name
“ten”.
Ok, cool! But what can we do with that?
Prolog has a wonderful query system.
Here are some queries
// coil code
let ten = 10
let twenty = 20
% give me the name of the variable
% who has the value 10
?- id_assign(Name, _, Let), num(10, Let).
% Output:
Name = ten,
Let = nid_2.
% Give me names for variables who's value is a number
?- id_assign(Name, _, Let), num(Val, Let).
% Output:
% first result
Name = ten,
Let = nid_2,
Val = 10 ;
% second result
Name = twenty,
Let = nid_4,
Val = 20.
Prolog lets you build up helper predicates to take care of more complex queries:
let [a b] = [1 2]
% extract id names into node context
Id, Out) :-
value(Id, _, Parent),
id_assign(Parent, Out).
value(% pull out values from array deconstruction
Id, Out) :-
value(Id, Idx, Array),
array_deconstruction(Array, Idx, Out).
index_of(
?- value(a, Out).
Out = 1.
After building up the helper predicates I’ve been able to do the following:
let object =
:name => "marcelle"
{ :age => 26
:hobbies => [:code :guitar] }
% find all object keys for the variable "object"
?- id_assign(object, _, Let),
Object, Let),
object_literal(Object, Key).
object_key(
% first result
Let = nid_11,
Object = nid_13,
Key = keyword(name) ;
% second result
Let = nid_11,
Object = nid_13,
Key = keyword(age) ;
% third result
Let = nid_11,
Object = nid_13,
Key = keyword(hobbies).
let list = [1 2]
let list = 2
% lets find all times a variable has been
% redefined to a different type
?- value(Name, First),
Name, Second),
value(First, FirstType),
type(Second, SecondType),
type(FirstType \= SecondType.
% Output:
Name = list,
First = [1, 2],
Second = 2,
FirstType = array,
SecondType = number.
So now what? Well, mostly I think its cool.
My long term hopes is to build an end-user linter system for coil using prolog, but that’s the future, for now its just fun :)