This chapter covers how Prolog facts compile to AWK associative arrays and how to filter data with various constraints.
In Prolog, facts represent static data. The AWK target compiles these to associative arrays, enabling efficient lookups during stream processing.
% Prolog facts
department(1, "Engineering").
department(2, "Sales").
department(3, "Marketing").
Compiles to AWK:
BEGIN {
department[1] = "Engineering"
department[2] = "Sales"
department[3] = "Marketing"
}
Facts with multiple arguments use composite keys:
% Employee facts: id, name, dept_id, salary
employee(101, "Alice", 1, 75000).
employee(102, "Bob", 2, 65000).
employee(103, "Carol", 1, 80000).
Compiles to:
BEGIN {
employee[101, "name"] = "Alice"
employee[101, "dept_id"] = 1
employee[101, "salary"] = 75000
employee[102, "name"] = "Bob"
employee[102, "dept_id"] = 2
employee[102, "salary"] = 65000
employee[103, "name"] = "Carol"
employee[103, "dept_id"] = 1
employee[103, "salary"] = 80000
}
The power of AWK comes from combining static facts with streaming input.
% Static facts
department(1, "Engineering").
department(2, "Sales").
department(3, "Marketing").
% Rule using facts and input
employee_with_dept(Name, DeptName) :-
employee_record(Id, Name, DeptId, _), % From input stream
department(DeptId, DeptName). % From facts
Input (employees.tsv):
101 Alice 1 75000
102 Bob 2 65000
103 Carol 1 80000
Output:
Alice Engineering
Bob Sales
Carol Engineering
% Filter by exact match
engineering_only(Name) :-
employee(Name, Dept, _),
Dept = "Engineering".
Generated AWK:
{
Name = $1
Dept = $2
if (Dept == "Engineering") {
print Name
}
}
% Salary above threshold
high_salary(Name, Salary) :-
employee(Name, _, Salary),
Salary > 70000.
% Salary in range
mid_range(Name, Salary) :-
employee(Name, _, Salary),
Salary >= 50000,
Salary =< 80000.
% Names starting before 'M' alphabetically
early_names(Name) :-
employee(Name, _, _),
Name < "M".
% Not in sales
not_sales(Name, Dept) :-
employee(Name, Dept, _),
Dept \= "Sales".
Multiple conditions in a rule body are combined with AND:
% Engineering AND high salary
senior_engineer(Name, Salary) :-
employee(Name, Dept, Salary),
Dept = "Engineering",
Salary > 75000.
% Define valid departments
valid_dept("Engineering").
valid_dept("Sales").
valid_dept("Marketing").
% Filter to valid departments only
validated(Name, Dept) :-
employee(Name, Dept, _),
valid_dept(Dept).
The arguments in the head determine output columns:
% Select only name and salary (skip department)
name_salary(Name, Salary) :-
employee(Name, _, Salary).
% Salary first, then name
salary_name(Salary, Name) :-
employee(Name, _, Salary).
% Add bonus calculation
with_bonus(Name, Bonus) :-
employee(Name, _, Salary),
Bonus is Salary * 0.1.
% Total compensation
total_comp(Name, Total) :-
employee(Name, _, Salary),
Total is Salary * 1.15. % Salary + 15% bonus
When processing input, fields map to predicate arguments:
% Input: Name<TAB>Dept<TAB>Salary<TAB>HireYear
% Arguments map to $1, $2, $3, $4
tenure_report(Name, Years) :-
employee_record(Name, _, _, HireYear),
Years is 2025 - HireYear.
Use _ to ignore fields you don’t need:
% Only care about name and salary
simple_report(Name, Salary) :-
employee(Name, _, Salary, _, _, _).
Remove duplicates from output:
?- compile_predicate_to_awk(departments/1, [unique(true)], AWK).
This generates AWK that tracks seen values:
{
Dept = $2
if (!(Dept in seen)) {
seen[Dept] = 1
print Dept
}
}
% file: facts_filtering.pl
:- encoding(utf8).
:- use_module('src/unifyweaver/targets/awk_target').
% Static department facts
department(1, "Engineering", "Building A").
department(2, "Sales", "Building B").
department(3, "Marketing", "Building B").
department(4, "HR", "Building A").
% Filter: Engineering employees earning > 70k
qualified(Name, Salary, Location) :-
employee_record(_, Name, DeptId, Salary),
Salary > 70000,
department(DeptId, "Engineering", Location).
% Generate script
generate :-
compile_predicate_to_awk(qualified/3, [
record_format(tsv),
include_header(true)
], AWK),
write_awk_script('qualified.awk', AWK),
format('Generated qualified.awk~n').
:- initialization(generate, main).
Test data (employees.tsv):
Id Name DeptId Salary
101 Alice 1 75000
102 Bob 2 85000
103 Carol 1 80000
104 Dave 3 70000
105 Eve 1 65000
Run:
swipl facts_filtering.pl
awk -f qualified.awk employees.tsv
Output:
Alice 75000 Building A
Carol 80000 Building A
| Pattern | Example | AWK Condition |
|---|---|---|
| Equality | Dept = "Sales" |
Dept == "Sales" |
| Greater than | Salary > 50000 |
Salary > 50000 |
| Less than | Age < 30 |
Age < 30 |
| Greater or equal | Score >= 80 |
Score >= 80 |
| Less or equal | Price =< 100 |
Price <= 100 |
| Not equal | Status \= "inactive" |
Status != "inactive" |
| Range | X >= 10, X =< 20 |
X >= 10 && X <= 20 |
Basic Filter: Create a predicate that filters employees with salary between 60000 and 80000.
Department Lookup: Define department facts and create a predicate that joins employee input with department names.
Multiple Conditions: Write a predicate that finds employees who are NOT in “HR” AND earn more than 65000.
Computed Field: Create a predicate that shows employee name and their tax (salary * 0.25).
Unique Departments: Generate a script that outputs unique department names from employee data.
In this chapter, you learned:
unique(true)In Chapter 3, we’ll explore rules and constraints in more depth, including arithmetic expressions and complex condition handling.