Repository navigation
Expand file tree
/
Copy pathpolyglot.lang
More file actions
165 lines (151 loc) · 7.22 KB
/
Copy pathpolyglot.lang
File metadata and controls
165 lines (151 loc) · 7.22 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
// ============================================================
// polyglot - a prime-sieve pipeline across FIVE real languages, one binary
// ============================================================
//
// Not a toy arithmetic chain: a real little program in which each language does
// the job it is best at, and they call each other directly at the i64 ABI -
// no FFI, no VM, no glue layer beyond lang itself.
//
// forth (#forth{...}) divides?/digit_sum - postfix stack language; its
// data stack is erased at read time
// C (#c{...}) c_is_prime(x) - tight imperative trial division
// flow (#flow{...}) gen primes(n) - a COROUTINE streaming the primes,
// suspending/resuming across the loop;
// its driver folds them into a Lisp list
// minilisp (#minilisp{...}) ml_sum/ml_len - functional recursion over a
// first-class cons list
// minipy (#minipy{...}) report/survey - the reporting layer, in a
// layout-delimited language
// lang main() - assertions + I/O
//
// The data path: flow's `primes` generator asks C whether each candidate is
// prime; C's trial-division loop asks Forth whether each divisor divides it;
// flow `yield`s the primes and its driver conses each (boxed) prime onto a real
// Lisp list; Lisp folds that list; Forth folds the digits of the result; and
// minipy loops over the whole pipeline and formats the answer, which is the job
// a scripting language actually has in a system like this. Five paradigms -
// stack, imperative, coroutine/effectful, functional, scripting - meeting in one
// native binary, and a single minipy statement can cross all of them.
//
// Five surface syntaxes, and no two agree on so much as where a block ends:
// parens, braces, `;`, `then`, and - in minipy - nothing but the column the line
// starts in.
//
// Effects are used (flow), so compile with clang, not the lli JIT:
// LANGBE=llvm out/lang example/polyglot.lang -o p.ll && clang -O0 p.ll -o p && ./p
include "std/core.lang"
include "example/minilisp/lisp_runtime.lang"
include "example/flow/flow_runtime.lang"
include "example/forth/forth_runtime.lang"
include "example/minipy/minipy_runtime.lang"
include "example/c/c.lang"
include "example/minilisp/minilisp.lang"
include "example/flow/flow.lang"
include "example/forth/forth.lang"
include "example/minipy/minipy.lang"
// --- forth: the arithmetic kernel, in postfix with no expression grammar at
// all. `divides?` is the innermost step of the sieve and C's loop calls it;
// `digit_sum` recurses over what Lisp computes at the other end. The data
// stack exists only at read time - these compile to plain i64 functions. ---
#forth{
: divides? ( d x -- f ) swap mod 0 = ;
: digit_sum ( n -- s ) dup 10 < if else dup 10 mod swap 10 / recurse + then ;
}
// --- C: primality by trial division (what C is good at: tight integer loops),
// asking Forth about each divisor ---
#c{
int c_is_prime(int x) {
if (x < 2) return 0;
int d = 2;
while (d * d <= x) {
if (divides_p(d, x)) { return 0; }
d = d + 1;
}
return 1;
}
}
// --- minilisp: fold a first-class list (what Lisp is good at: recursion over data) ---
#minilisp{ (defun ml_sum (xs) (if (eq xs nil) 0 (+ (car xs) (ml_sum (cdr xs))))) }
#minilisp{ (defun ml_len (xs) (if (eq xs nil) 0 (+ 1 (ml_len (cdr xs))))) }
// --- flow: a coroutine that STREAMS primes (calling C), and a driver that
// collects them into a Lisp list (calling the Lisp runtime). The generator
// suspends after each prime and resumes to find the next - the thing C and
// Lisp can't say. ---
#flow{
gen primes(n) {
var x = 2;
while x <= n {
if c_is_prime(x) {
yield x;
}
x = x + 1;
}
}
// build a Lisp list of the yielded primes (boxed via lisp_int; cons via the
// Lisp runtime) - so the list is reverse-collected (largest first).
func collect_primes(n) {
var lst = lisp_nil();
for p in primes(n) {
lst = lisp_cons(lisp_int(p), lst);
}
return lst;
}
// flow can also just count, staying in raw i64.
func count_primes(n) {
var c = 0;
for p in primes(n) {
c = c + 1;
}
return c;
}
}
// --- minipy: the reporting layer. Python's actual job in a system like this is
// to drive the fast code and format the answer, so that is the job it has
// here - and every single call in `report` leaves the language it is written
// in. Nothing marks the block structure below but the left margin. ---
#minipy{
def report(n):
lst = collect_primes(n) # flow's coroutine -> C -> forth
count = lisp_to_int(ml_len(lst)) # minilisp, folding a cons list
total = lisp_to_int(ml_sum(lst))
print("primes <=", n, ": count", count, "sum", total, "digitsum", digit_sum(total))
return total # ^ forth
def survey(limit):
best = 0
for n in range(10, limit + 1, 10):
s = report(n)
if s > best:
best = s
return best
}
func main() i64 {
print("=== prime pipeline: flow -> C -> Lisp ===\n\n");
// flow streams primes (asking C), collecting them into a Lisp list:
var plist i64 = collect_primes(30);
print("primes <= 30 (Lisp list) = "); lisp_print(plist); print("\n");
// Lisp folds the list:
print("count (Lisp ml_len) = "); print_int(lisp_to_int(ml_len(plist))); print("\n");
print("sum (Lisp ml_sum) = "); print_int(lisp_to_int(ml_sum(plist))); print("\n");
// flow can count directly too (raw i64, no Lisp):
print("count (flow count_primes)= "); print_int(count_primes(30)); print("\n");
// and Forth folds the digits of the Lisp-computed sum (129 -> 1+2+9 = 12):
var psum i64 = lisp_to_int(ml_sum(plist));
print("digits(sum) (forth) = "); print_int(digit_sum(psum)); print("\n");
// and minipy drives the whole pipeline in a loop and reports:
print("\n--- minipy report ---\n");
var best i64 = survey(30);
print("largest sum (minipy survey) = "); print_int(best); print("\n");
// 10 primes <= 30: 2 3 5 7 11 13 17 19 23 29; sum = 129
if lisp_to_int(ml_len(plist)) != 10 { return 1; }
if lisp_to_int(ml_sum(plist)) != 129 { return 2; }
if count_primes(30) != 10 { return 3; }
if count_primes(100) != 25 { return 4; } // 25 primes <= 100
if lisp_to_int(ml_sum(collect_primes(10))) != 17 { return 5; } // 2+3+5+7
if digit_sum(129) != 12 { return 6; } // forth recursion
if divides_p(3, 12) != 1 { return 7; } // forth, called directly
if divides_p(5, 12) != 0 { return 8; }
if report(10) != 17 { return 9; } // minipy, five languages deep
if best != 129 { return 10; } // minipy's loop over the pipeline
print("\nPOLYGLOT PASS\n");
return 0;
}