@effekt-lang/effekt 0.54.0 → 0.55.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/bin/effekt CHANGED
Binary file
@@ -944,6 +944,22 @@ def each[A](list: List[A]): Unit / emit[A] =
944
944
  each(tail)
945
945
  }
946
946
 
947
+ /// Push iterator that emits all the tails (suffixes) of the given `list`.
948
+ ///
949
+ /// Examples:
950
+ /// ```effekt
951
+ /// > list::collect[List[Int]] { [1, 2, 3].tails }
952
+ /// [[1, 2, 3], [2, 3], [3], []]
953
+ ///
954
+ /// > list::collect[List[Int]] { [].tails }
955
+ /// [[]]
956
+ /// ```
957
+ def tails[A](list: List[A]): Unit / emit[List[A]] =
958
+ list match {
959
+ case Nil() => do emit(Nil())
960
+ case Cons(head, tail) => do emit(list); tails(tail)
961
+ }
962
+
947
963
  def feed[T, R](list: List[T]) { reader: () => R / read[T] }: R = {
948
964
  var l = list
949
965
  try {
@@ -1,4 +1,5 @@
1
1
  module process
2
+ import array
2
3
 
3
4
  extern def exit(errorCode: Int) at io: Nothing =
4
5
  js "(function() { process.exit(${errorCode}) })()"
@@ -7,3 +8,101 @@ extern def exit(errorCode: Int) at io: Nothing =
7
8
  ret %Pos zeroinitializer
8
9
  """
9
10
  chez "(exit ${errorCode})"
11
+
12
+ // Subprocesses
13
+ // ------------
14
+
15
+ extern jsNode """
16
+ const { spawn } = require('node:child_process');
17
+ """
18
+ extern llvm """
19
+ declare %Pos @c_spawn_options_default()
20
+ declare %Pos @c_spawn_options_on_stdout(%Pos, %Pos)
21
+ declare %Pos @c_spawn_options_on_stderr(%Pos, %Pos)
22
+ declare %Pos @c_spawn_options_pipe_stdin(%Pos, %Pos)
23
+ declare %Pos @c_write_stream(%Pos, %Pos)
24
+ declare %Pos @c_close_stream(%Pos)
25
+ declare %Pos @c_spawn(%Pos, %Pos, %Pos)
26
+ """
27
+
28
+ extern type SpawnOptions
29
+ extern def default() at {}: SpawnOptions =
30
+ jsNode """{ stdio: [0,1,2], onSpawn: (p) => {} }"""
31
+ llvm """
32
+ %opts = call %Pos @c_spawn_options_default()
33
+ ret %Pos %opts
34
+ """
35
+ extern def onStdout(opts: SpawnOptions, callback: String => Unit at {io, global}) at {}: SpawnOptions =
36
+ jsNode """(function() {
37
+ let old = ${opts};
38
+ old.stdio[1] = 'pipe';
39
+ let oldSpawn = old.onSpawn;
40
+ old.onSpawn = function(p) {
41
+ oldSpawn(p);
42
+ p.stdout.on('data', (chunk) => {
43
+ $effekt.runToplevel((ks, k) => (${callback})(chunk, ks, k));
44
+ });
45
+ };
46
+ return old; })()"""
47
+ llvm """
48
+ %opts = call %Pos @c_spawn_options_on_stdout(%Pos ${opts}, %Pos ${callback})
49
+ ret %Pos %opts
50
+ """
51
+ extern def onStderr(opts: SpawnOptions, callback: String => Unit at {io, global}) at {}: SpawnOptions =
52
+ jsNode """(function() {
53
+ let old = ${opts};
54
+ old.stdio[2] = 'pipe';
55
+ let oldSpawn = old.onSpawn;
56
+ old.onSpawn = function(p) {
57
+ oldSpawn(p);
58
+ p.stderr.on('data', (chunk) => {
59
+ $effekt.runToplevel((ks, k) => (${callback})(chunk, ks, k));
60
+ });
61
+ };
62
+ return old; })()"""
63
+ llvm """
64
+ %opts = call %Pos @c_spawn_options_on_stderr(%Pos ${opts}, %Pos ${callback})
65
+ ret %Pos %opts
66
+ """
67
+ extern type WritableStream
68
+ extern def withStdin(opts: SpawnOptions, callback: WritableStream => Unit at {io, global}) at {}: SpawnOptions =
69
+ jsNode """(function () {
70
+ let old = ${opts};
71
+ old.stdio[0] = 'pipe'
72
+ let oldSpawn = old.onSpawn;
73
+ old.onSpawn = function(p) {
74
+ oldSpawn(p);
75
+ p.on('spawn', () => {
76
+ $effekt.runToplevel((ks, k) => (${callback})(p.stdin, ks, k));
77
+ });
78
+ };
79
+ return old;
80
+ })()"""
81
+ llvm """
82
+ %opts = call %Pos @c_spawn_options_pipe_stdin(%Pos ${opts}, %Pos ${callback})
83
+ ret %Pos %opts
84
+ """
85
+ extern def write(to: WritableStream, s: String) at io: Unit =
86
+ jsNode """${to}.write(${s})"""
87
+ llvm """
88
+ call void @c_write_stream(%Pos ${to}, %Pos ${s})
89
+ ret %Pos zeroinitializer
90
+ """
91
+ extern def close(to: WritableStream) at io: Unit =
92
+ jsNode """${to}.end()"""
93
+ llvm """
94
+ call void @c_close_stream(%Pos ${to})
95
+ ret %Pos zeroinitializer
96
+ """
97
+
98
+ extern def spawn(cmd: String, args: Array[String], options: SpawnOptions) at async: Int =
99
+ jsNode """$effekt.capture(k => {
100
+ let p = spawn(${cmd}, ${args}, ${options});
101
+ ${options}.onSpawn(p);
102
+ p.on('close', (exitcode, sig) => { k(exitcode) });
103
+ return p;
104
+ })"""
105
+ llvm """
106
+ call void @c_spawn(%Pos ${cmd}, %Pos ${args}, %Pos ${options}, %Stack %stack)
107
+ ret void
108
+ """
@@ -2,6 +2,7 @@ module stream
2
2
 
3
3
  import effekt
4
4
  import option
5
+ import exception
5
6
 
6
7
  // Effects
7
8
  // -------
@@ -128,6 +129,26 @@ def iterate[A](initial: A) { step: A => A }: Unit / emit[A] = {
128
129
  }
129
130
  }
130
131
 
132
+ /// Re-emit from the `stream` as long as the `predicate` holds.
133
+ /// Once the predicate fails, the stream ends.
134
+ ///
135
+ /// Examples:
136
+ /// ```effekt
137
+ /// > list::collect[Int] { takeWhile[Int] { x => x < 3 } { [1, 2, 3, 4, 1].each } }
138
+ /// [1, 2]
139
+ ///
140
+ /// > list::collect[Int] { takeWhile[Int] { x => x < 10 } { [1, 2, 3].each } }
141
+ /// [1, 2, 3]
142
+ ///
143
+ /// > list::collect[Int] { takeWhile[Int] { x => x < 0 } { [1, 2, 3].each } }
144
+ /// []
145
+ /// ```
146
+ def takeWhile[A] { predicate: A => Bool } { stream: () => Unit / emit[A] }: Unit / emit[A] =
147
+ try { stream() } with emit[A] {
148
+ case value and predicate(value) => do emit(value); resume(())
149
+ case _ => () // end the stream
150
+ }
151
+
131
152
 
132
153
  def sum { stream: () => Unit / emit[Int] }: Int =
133
154
  returning::sum[Unit]{stream}.second
@@ -135,6 +156,71 @@ def sum { stream: () => Unit / emit[Int] }: Int =
135
156
  def product { stream: () => Unit / emit[Int] }: Int =
136
157
  returning::product[Unit]{stream}.second
137
158
 
159
+ /// Returns true if all elements in the stream satisfy the given predicate,
160
+ /// false otherwise. Short-circuits as soon as a non-matching element is found.
161
+ ///
162
+ /// Examples:
163
+ /// ```effekt
164
+ /// > all[Int] { x => x > 0 } { [1, 2, 3].each }
165
+ /// true
166
+ ///
167
+ /// > all[Int] { x => x < 3 } { [1, 2, 3, 4].each }
168
+ /// false
169
+ ///
170
+ /// > all[Bool] { x => x } { [false, true, true].each }
171
+ /// false
172
+ ///
173
+ /// > all[Int] { x => x >= 0 } { () } // empty stream
174
+ /// true
175
+ /// ```
176
+ def all[A] { cond: A => Bool } { stream: => Unit / emit[A] }: Bool =
177
+ try { stream(); true } with emit[A] {
178
+ case value and not(cond(value)) => false
179
+ case _ => resume(())
180
+ }
181
+
182
+ /// Returns true if any element in the stream satisfies the given predicate,
183
+ /// false otherwise. Short-circuits as soon as a matching element is found.
184
+ ///
185
+ /// Examples:
186
+ /// ```effekt
187
+ /// > any[Int] { x => x > 0 } { [-2, -1, 0, 1, 2].each }
188
+ /// true
189
+ ///
190
+ /// > any[Int] { x => x > 10 } { [1, 2, 3, 4].each }
191
+ /// false
192
+ ///
193
+ /// > any[Bool] { x => x } { [false, true, false].each }
194
+ /// true
195
+ ///
196
+ /// > any[Int] { x => x == 0 } { () } // empty stream
197
+ /// false
198
+ /// ```
199
+ def any[A] { cond: A => Bool } { stream: => Unit / emit[A] }: Bool =
200
+ try { stream(); false } with emit[A] {
201
+ case value and cond(value) => true
202
+ case _ => resume(())
203
+ }
204
+
205
+ /// Count the number of emitted values that satisfy the given predicate.
206
+ ///
207
+ /// Examples:
208
+ /// ```
209
+ /// > countIf[Int] { x => x > 0 } { [2, -1, 0, 1, -2].each }
210
+ /// 2
211
+ ///
212
+ /// > countIf[Bool] { x => x } { [false, false, true, false].each }
213
+ /// 1
214
+ /// ```
215
+ def countIf[A] { shouldCount: A => Bool } { stream: () => Unit / emit[A] }: Int = {
216
+ var count = 0
217
+ for {stream} {
218
+ case x and shouldCount(x) => count = count + 1
219
+ case _ => ()
220
+ }
221
+ count
222
+ }
223
+
138
224
  def source[A] { stream: () => Unit / emit[A] } { reader: () => Unit / read[A] }: Unit =
139
225
  returning::source[A, Unit]{stream}{reader}
140
226
 
@@ -177,9 +263,9 @@ namespace internal {
177
263
 
178
264
  /// Use cons to handle prod, but also emit to the outside.
179
265
  /// The outside controls iteration, i.e., if cons aborts, the rest of prod will still be emitted.
180
- ///
266
+ ///
181
267
  /// Starts by executing cons. Will stop cons during execution if the outside stops consuming.
182
- ///
268
+ ///
183
269
  /// Example, printing all values consumed by the outside:
184
270
  ///
185
271
  /// with teeing{ {s} => for{s}{ e => println(e) } }
@@ -215,9 +301,9 @@ def teeing[A]{ cons: { => Unit / emit[A] } => Unit }{ prod: => Unit / emit[A] }:
215
301
  }
216
302
 
217
303
  /// Binds a `teeing`-like function in the body, stopping the inner producer once all consumers in tees are done consuming.
218
- ///
304
+ ///
219
305
  /// Example of use, equivalent to `tee[A]{cns1}{cns2}{prd}`:
220
- ///
306
+ ///
221
307
  /// manyTee[A] { {tee} =>
222
308
  /// with tee{cns1}
223
309
  /// with tee{cns2}
@@ -253,6 +339,25 @@ def tee[A]{ cons1: { => Unit / emit[A] } => Unit }{ cons2: { => Unit / emit[A] }
253
339
  }
254
340
  }
255
341
 
342
+ /// Return exactly the very first element of the given stream
343
+ /// or raise MissingValue if no elements are emitted.
344
+ ///
345
+ /// Examples:
346
+ /// ```effekt
347
+ /// > first[Int] { [1, 2, 3].each }
348
+ /// 1
349
+ /// > first[Bool] { do emit(true) }
350
+ /// true
351
+ /// > first[String] { () } // empty stream
352
+ /// <Exception MissingValue: code in first did not emit any values>
353
+ /// ```
354
+ def first[A] { body: => Unit / emit[A] }: A / Exception[MissingValue] =
355
+ try {
356
+ body()
357
+ val r: A = do raise[MissingValue](MissingValue(), "code in first did not emit any values")
358
+ r
359
+ } with emit[A] { a => a }
360
+
256
361
  namespace returning {
257
362
 
258
363
  /// Canonical handler of push streams that performs `action` for every
@@ -627,4 +627,199 @@ void c_signal_notify(struct Pos signal, struct Pos value, Stack stack) {
627
627
  }
628
628
  }
629
629
 
630
+ // Subprocesses
631
+ // ------------
632
+
633
+ struct Pos c_spawn_options_default() {
634
+ uv_process_options_t* options = (uv_process_options_t*)malloc(sizeof(uv_process_options_t));
635
+ memset(options, 0, sizeof(uv_process_options_t));
636
+ uv_stdio_container_t* stdio = (uv_stdio_container_t*)malloc(3*sizeof(uv_stdio_container_t));
637
+ stdio[0].flags = UV_INHERIT_FD;
638
+ stdio[0].data.fd = 0;
639
+ stdio[1].flags = UV_INHERIT_FD;
640
+ stdio[1].data.fd = 1;
641
+ stdio[2].flags = UV_INHERIT_FD;
642
+ stdio[2].data.fd = 2;
643
+ options->stdio = stdio;
644
+ options->stdio_count = 3;
645
+ return (struct Pos) { .tag = 0, .obj = options, };
646
+ }
647
+
648
+ static void subproc_alloc_cb(uv_handle_t* handle, size_t suggested_size, uv_buf_t* buf) {
649
+ (void)handle;
650
+ buf->base = malloc(suggested_size);
651
+ buf->len = suggested_size;
652
+ }
653
+
654
+ typedef struct {
655
+ struct Pos handler;
656
+ } subproc_stream_cb_closure_t;
657
+ void subproc_stream_cb(uv_stream_t *stream, ssize_t nread, const uv_buf_t *buf) {
658
+ subproc_stream_cb_closure_t* clos = (subproc_stream_cb_closure_t*)(stream->data);
659
+ if(nread >= 0) {
660
+ struct Pos chunk = c_bytearray_construct(nread, (uint8_t*)(buf->base));
661
+ if (buf->base) free(buf->base);
662
+ run_Pos(clos->handler, chunk);
663
+ } else {
664
+ uv_read_stop(stream);
665
+ erasePositive(clos->handler);
666
+ free(clos);
667
+ if (buf->base) free(buf->base);
668
+ }
669
+ }
670
+ struct Pos subproc_options_on_stream(struct Pos opts, size_t id, uv_stdio_flags flags, struct Pos callback) {
671
+ uv_process_options_t* options = (uv_process_options_t*)opts.obj;
672
+
673
+ options->stdio[id].flags = UV_CREATE_PIPE | flags;
674
+ uv_pipe_t* pipe = (uv_pipe_t*)malloc(sizeof(uv_pipe_t));
675
+
676
+ uv_pipe_init(uv_default_loop(), pipe, 1);
677
+ options->stdio[id].data.stream = (uv_stream_t*)pipe;
678
+
679
+ subproc_stream_cb_closure_t* clos = (subproc_stream_cb_closure_t*)malloc(sizeof(subproc_stream_cb_closure_t));
680
+ clos->handler = callback;
681
+ pipe->data = clos;
682
+
683
+ return opts;
684
+ }
685
+ struct Pos c_spawn_options_on_stdout(struct Pos opts, struct Pos callback) {
686
+ return subproc_options_on_stream(opts, 1, UV_WRITABLE_PIPE, callback);
687
+ }
688
+ struct Pos c_spawn_options_on_stderr(struct Pos opts, struct Pos callback) {
689
+ return subproc_options_on_stream(opts, 2, UV_WRITABLE_PIPE, callback);
690
+ }
691
+ struct Pos c_spawn_options_pipe_stdin(struct Pos opts, struct Pos callback) {
692
+ return subproc_options_on_stream(opts, 0, UV_READABLE_PIPE, callback);
693
+ }
694
+
695
+
696
+ typedef struct {
697
+ char** args;
698
+ Stack k;
699
+ uv_process_options_t* opts;
700
+ } subproc_proc_data_t;
701
+ void c_close_stream_callback_exit(uv_handle_t* req) {
702
+ free(req);
703
+ }
704
+ void subproc_on_close(uv_handle_t* handle) {
705
+ subproc_proc_data_t* pd = (subproc_proc_data_t*)((uv_process_t*)handle)->data;
706
+ uv_process_options_t* opts = (uv_process_options_t*)(pd->opts);
707
+ if(opts->stdio[0].flags & UV_CREATE_PIPE){
708
+ uv_close((uv_handle_t*)(opts->stdio[0].data.stream), c_close_stream_callback_exit);
709
+ }
710
+ if(opts->stdio[1].flags & UV_CREATE_PIPE){
711
+ uv_close((uv_handle_t*)(opts->stdio[1].data.stream), c_close_stream_callback_exit);
712
+ }
713
+ if(opts->stdio[2].flags & UV_CREATE_PIPE){
714
+ uv_close((uv_handle_t*)(opts->stdio[2].data.stream), c_close_stream_callback_exit);
715
+ }
716
+ // TODO shutdown pipes for stdio
717
+ free(opts->stdio);
718
+ free(opts);
719
+ free(pd);
720
+ free(handle);
721
+ }
722
+ void subproc_on_exit(uv_process_t* proc, int64_t exit_status, int term_signal) {
723
+ (void)term_signal; (void)exit_status;
724
+ subproc_proc_data_t* pd = (subproc_proc_data_t*)proc->data;
725
+ if (pd->args) {
726
+ for(int i = 0; pd->args[i] != NULL; i++) {
727
+ free(pd->args[i]);
728
+ }
729
+ free(pd->args);
730
+ }
731
+ Stack k = pd->k;
732
+ uv_close((uv_handle_t*)proc, subproc_on_close);
733
+ resume_Int(k, exit_status);
734
+ }
735
+
736
+ void c_close_stream_callback(uv_shutdown_t* req, int status) {
737
+ if(status != 0){
738
+ // TODO error handling
739
+ }
740
+ free(req);
741
+ }
742
+ void c_close_stream(struct Pos stream) {
743
+ uv_stream_t* str = (uv_stream_t*)stream.obj;
744
+ uv_shutdown_t* req = (uv_shutdown_t*)malloc(sizeof(uv_shutdown_t));
745
+ uv_shutdown(req, str, c_close_stream_callback);
746
+ }
747
+
748
+ typedef struct {
749
+ struct Pos buffer;
750
+ uv_buf_t buf;
751
+ } subproc_write_req_data_t;
752
+ void c_write_stream_callback(uv_write_t* req, int status) {
753
+ if(status != 0){
754
+ // TODO error handling
755
+ }
756
+ subproc_write_req_data_t* data = (subproc_write_req_data_t*)(req->data);
757
+ erasePositive(data->buffer);
758
+ free(data);
759
+ free(req);
760
+ }
761
+ void c_write_stream(struct Pos stream, struct Pos buffer) {
762
+ uv_stream_t* str = (uv_stream_t*)stream.obj;
763
+ uv_write_t* req = (uv_write_t*)malloc(sizeof(uv_write_t));
764
+ memset(req, 0, sizeof(uv_write_t));
765
+ subproc_write_req_data_t* data = (subproc_write_req_data_t*)malloc(sizeof(subproc_write_req_data_t));
766
+ data->buffer = buffer;
767
+ uv_buf_t buf = uv_buf_init((char*)(c_bytearray_data(buffer)), buffer.tag);
768
+ req->data = data;
769
+ data->buf = buf;
770
+ uv_write(req, str, &(data->buf), 1, c_write_stream_callback);
771
+ }
772
+
773
+ void c_spawn(struct Pos cmd, struct Pos args, struct Pos options, Stack stack) {
774
+ uv_process_options_t* opts = (uv_process_options_t*)options.obj;
775
+ uv_process_t* proc = (uv_process_t*)malloc(sizeof(uv_process_t));
776
+
777
+ // command
778
+ char* cmd_s = c_bytearray_into_nullterminated_string(cmd);
779
+ erasePositive(cmd);
780
+ opts->file = cmd_s;
781
+
782
+ // args
783
+ int _argc = (int)((uint64_t*)args.obj)[2];
784
+ char** _args = (char**)malloc((2 + _argc) * sizeof(char*));
785
+ struct Pos* _argv = (struct Pos*)(((uint64_t*)args.obj) + 3);
786
+ _args[0] = cmd_s;
787
+ for(int i = 0; i < _argc; i++) {
788
+ _args[i + 1] = c_bytearray_into_nullterminated_string(_argv[i]);
789
+ }
790
+ erasePositive(args);
791
+ _args[_argc + 1] = NULL;
792
+ opts->args = _args;
793
+ // callback to free _args
794
+ opts->exit_cb = subproc_on_exit;
795
+ subproc_proc_data_t* pd = (subproc_proc_data_t*)malloc(sizeof(subproc_proc_data_t));
796
+ pd->args = _args;
797
+ pd->k = stack;
798
+ proc->data = pd;
799
+
800
+ // spawn process
801
+ int err = uv_spawn(uv_default_loop(), proc, opts);
802
+
803
+ if(opts->stdio[0].flags & UV_CREATE_PIPE) {
804
+ uv_stream_t* stream = (uv_stream_t*)(opts->stdio[0].data.stream);
805
+ subproc_stream_cb_closure_t* clos = (subproc_stream_cb_closure_t*)(stream->data);
806
+ struct Pos str = (struct Pos) { .tag = 0, .obj = stream, };
807
+ run_Pos(clos->handler, str);
808
+ erasePositive(clos->handler);
809
+ free(clos);
810
+ }
811
+ if(opts->stdio[1].flags & UV_CREATE_PIPE)
812
+ uv_read_start((uv_stream_t*)(opts->stdio[1].data.stream), subproc_alloc_cb, subproc_stream_cb);
813
+ if(opts->stdio[2].flags & UV_CREATE_PIPE)
814
+ uv_read_start((uv_stream_t*)(opts->stdio[2].data.stream), subproc_alloc_cb, subproc_stream_cb);
815
+ pd->opts = opts;
816
+
817
+ erasePositive(options);
818
+ if(err) {
819
+ printf("%s\n", uv_strerror(err));
820
+ free(_args);
821
+ free(proc);
822
+ }
823
+ }
824
+
630
825
  #endif
@@ -1,5 +1,5 @@
1
1
  <!doctype html><html lang=en><head><meta charset=utf-8><meta http-equiv=X-UA-Compatible content="IE=edge"><meta name=viewport content="width=device-width,initial-scale=1"><title>Eclipse Public License 2.0 (EPL) | The Eclipse Foundation</title>
2
- <meta property="og:title" content="Eclipse Public License 2.0 (EPL) | The Eclipse Foundation"><meta name=twitter:title content="Eclipse Public License 2.0 (EPL) | The Eclipse Foundation"><meta name=description content="The Eclipse Foundation provides our global community of individuals and organisations with a mature, scalable, and business-friendly environment for open source …"><meta property="og:description" content="The Eclipse Foundation provides our global community of individuals and organisations with a mature, scalable, and business-friendly environment for open source …"><meta name=twitter:description content="The Eclipse Foundation provides our global community of individuals and organisations with a mature, scalable, and business-friendly environment for open source …"><meta name=author content="Wayne Beaton"><link href=/favicon.ico rel=icon type=image/x-icon><meta property="og:image" content="https://www.eclipse.org/images/og-image.jpg"><meta name=twitter:image content="https://www.eclipse.org/images/og-image.jpg"><meta name=twitter:card content="summary"><meta name=twitter:site content="@eclipsefdn"><meta name=twitter:creator content="@eclipsefdn"><meta property="og:url" content="https://www.eclipse.org/legal/epl-2.0/"><meta property="og:type" content="website"><meta property="og:site_name" content="Eclipse Foundation"><meta name=keywords content="epl,epl-2.0,legal,foundation,eclipse,eclipse public license,license,licenses"><meta name=generator content="Hugo 0.144.2"><link rel=canonical href=https://www.eclipse.org/legal/epl-2.0/><link rel=alternate href=/legal/epl-2.0/index.xml type=application/rss+xml title><link rel=stylesheet href="/public/css/styles.css?v=1764530174"><script>(function(e,t,n,s,o){e[s]=e[s]||[],e[s].push({"gtm.start":(new Date).getTime(),event:"gtm.js"});var a=t.getElementsByTagName(n)[0],i=t.createElement(n),r=s!="dataLayer"?"&l="+s:"";i.async=!0,i.src="https://www.googletagmanager.com/gtm.js?id="+o+r,a.parentNode.insertBefore(i,a)})(window,document,"script","dataLayer","GTM-5WLCZXC")</script><script>var tableClasses="table"</script><link href="https://fonts.googleapis.com/css2?family=Roboto:ital,wght@0,100;0,300;0,400;0,500;0,700;0,900;1,100;1,300;1,400;1,500;1,700;1,900&display=swap" rel=stylesheet type=text/css></head><body><noscript><iframe src="https://www.googletagmanager.com/ns.html?id=GTM-5WLCZXC" height=0 width=0 style=display:none;visibility:hidden></iframe></noscript><a href=#main class=skip-link>Skip to content</a><div class=eclipsefdn-featured-story-banner data-publish-target=eclipse_org></div><header class="header-wrapper header-wrapper" id=header><div class="navigation-bar dark"><div class=container><div class=navigation-bar-inner><div class=navigation-bar-left><a class="focus-highlight focus-inverse" href=/><img class=navigation-bar-brand src=https://www.eclipse.org/eclipse.org-common/themes/solstice/public/images/logo/eclipse-foundation-white-orange.svg alt="Eclipse Foundation"></a></div><nav class="navigation-bar-nav hidden-xs hidden-sm"><ul><li><a href=# data-target=join-us-menu>Join us</a></li><li><a href=# data-target=what-we-do-menu>What we do</a></li><li><a href=# data-target=resources-menu>Resources</a></li><li><a href=# data-target=about-us-menu>About us</a></li></ul></nav><div class=navigation-bar-right><div class="navigation-bar-controls hidden-xs hidden-sm"><ul><li class=dropdown><a class="dropdown-toggle link-unstyled focus-highlight" tabindex=0 id=search-dropdown-toggle data-toggle=dropdown href=# aria-label=Search><i class="fa-solid fa-magnifying-glass" aria-hidden=true></i></a><form class="search-bar dropdown-menu dropdown-menu-right" aria-labelledby=search-dropdown-toggle action=https://www.eclipse.org/home/search/ method=get><input class=search-bar-input name=q placeholder=Search>
2
+ <meta property="og:title" content="Eclipse Public License 2.0 (EPL) | The Eclipse Foundation"><meta name=twitter:title content="Eclipse Public License 2.0 (EPL) | The Eclipse Foundation"><meta name=description content="The Eclipse Foundation provides our global community of individuals and organisations with a mature, scalable, and business-friendly environment for open source …"><meta property="og:description" content="The Eclipse Foundation provides our global community of individuals and organisations with a mature, scalable, and business-friendly environment for open source …"><meta name=twitter:description content="The Eclipse Foundation provides our global community of individuals and organisations with a mature, scalable, and business-friendly environment for open source …"><meta name=author content="Wayne Beaton"><link href=/favicon.ico rel=icon type=image/x-icon><meta property="og:image" content="https://www.eclipse.org/images/og-image.jpg"><meta name=twitter:image content="https://www.eclipse.org/images/og-image.jpg"><meta name=twitter:card content="summary"><meta name=twitter:site content="@eclipsefdn"><meta name=twitter:creator content="@eclipsefdn"><meta property="og:url" content="https://www.eclipse.org/legal/epl-2.0/"><meta property="og:type" content="website"><meta property="og:site_name" content="Eclipse Foundation"><meta name=keywords content="epl,epl-2.0,legal,foundation,eclipse,eclipse public license,license,licenses"><meta name=generator content="Hugo 0.144.2"><link rel=canonical href=https://www.eclipse.org/legal/epl-2.0/><link rel=alternate href=/legal/epl-2.0/index.xml type=application/rss+xml title><link rel=stylesheet href="/public/css/styles.css?v=1765134958"><script>(function(e,t,n,s,o){e[s]=e[s]||[],e[s].push({"gtm.start":(new Date).getTime(),event:"gtm.js"});var a=t.getElementsByTagName(n)[0],i=t.createElement(n),r=s!="dataLayer"?"&l="+s:"";i.async=!0,i.src="https://www.googletagmanager.com/gtm.js?id="+o+r,a.parentNode.insertBefore(i,a)})(window,document,"script","dataLayer","GTM-5WLCZXC")</script><script>var tableClasses="table"</script><link href="https://fonts.googleapis.com/css2?family=Roboto:ital,wght@0,100;0,300;0,400;0,500;0,700;0,900;1,100;1,300;1,400;1,500;1,700;1,900&display=swap" rel=stylesheet type=text/css></head><body><noscript><iframe src="https://www.googletagmanager.com/ns.html?id=GTM-5WLCZXC" height=0 width=0 style=display:none;visibility:hidden></iframe></noscript><a href=#main class=skip-link>Skip to content</a><div class=eclipsefdn-featured-story-banner data-publish-target=eclipse_org></div><header class="header-wrapper header-wrapper" id=header><div class="navigation-bar dark"><div class=container><div class=navigation-bar-inner><div class=navigation-bar-left><a class="focus-highlight focus-inverse" href=/><img class=navigation-bar-brand src=https://www.eclipse.org/eclipse.org-common/themes/solstice/public/images/logo/eclipse-foundation-white-orange.svg alt="Eclipse Foundation"></a></div><nav class="navigation-bar-nav hidden-xs hidden-sm"><ul><li><a href=# data-target=join-us-menu>Join us</a></li><li><a href=# data-target=what-we-do-menu>What we do</a></li><li><a href=# data-target=resources-menu>Resources</a></li><li><a href=# data-target=about-us-menu>About us</a></li></ul></nav><div class=navigation-bar-right><div class="navigation-bar-controls hidden-xs hidden-sm"><ul><li class=dropdown><a class="dropdown-toggle link-unstyled focus-highlight" tabindex=0 id=search-dropdown-toggle data-toggle=dropdown href=# aria-label=Search><i class="fa-solid fa-magnifying-glass" aria-hidden=true></i></a><form class="search-bar dropdown-menu dropdown-menu-right" aria-labelledby=search-dropdown-toggle action=https://www.eclipse.org/home/search/ method=get><input class=search-bar-input name=q placeholder=Search>
3
3
  <button class="btn btn-link" type=submit>
4
4
  <i class="fa-solid fa-arrow-right"></i></button></form></li><li class=dropdown><a class="dropdown-toggle focus-highlight" id=user-menu href=# data-toggle=dropdown aria-haspopup=true aria-expanded=false tabindex=0><i class="fa-regular fa-user" aria-hidden=true></i></a><ul class="dropdown-menu dropdown-menu-right" aria-labelledby=user-menu><li><a href=https://accounts.eclipse.org/user><i class="fa-solid fa-user margin-right-10" aria-hidden=true></i>
5
5
  View my account</a></li><li><a href=https://accounts.eclipse.org/user/edit><i class="fa-solid fa-edit margin-right-10" aria-hidden=true></i>
@@ -39,4 +39,4 @@ Manage cookies</a></li></ul></li></ul></div><div class="navigation-bar-ctas hidd
39
39
  <i class="fa-brands fa-youtube fa-stack-1x fa-inverse" aria-hidden=true></i></span></a></li><li><a href=https://www.linkedin.com/company/eclipse-foundation aria-label=LinkedIn><span class=fa-stack><i class="fa-solid fa-circle fa-stack-2x" aria-hidden=true></i>
40
40
  <i class="fa-brands fa-linkedin-in fa-stack-1x fa-inverse" aria-hidden=true></i></span></a></li></ul></section><section class=newsletter-section><p class="subheading text-tertiary">Stay up to date</p><h2>Subscribe to our newsletter</h2><form class="newsletter-form margin-top-40" id=mc-embedded-subscribe-form action="https://eclipse.us6.list-manage.com/subscribe/post?u=eaf9e1f06f194eadc66788a85&amp;id=e7538485cd&amp;f_id=00f9c2e1f0" method=post novalidate target=_blank><div class=text-field-outlined><input name=EMAIL type=email placeholder="Your email">
41
41
  <button class=newsletter-form-submit type=submit name=subscribe id=mc-embedded-subscribe aria-label=Submit>
42
- <i class="fa-solid fa-arrow-right" aria-hidden=true></i></button></div></form></section><section class=footer-bottom-section><img class="img-responsive logo" src=https://www.eclipse.org/eclipse.org-common/themes/solstice/public/images/logo/eclipse-foundation-white.svg alt="Eclipse Foundation"><div class=legal-information><p>Copyright &copy; Eclipse Foundation AISBL. All rights reserved.</p><ul class=legal-links><li><a href=https://www.eclipse.org/legal/privacy/>Privacy policy</a></li><li><a href=https://www.eclipse.org/legal/terms-of-use/>Terms of use</a></li><li><a href=https://www.eclipse.org/legal/compliance/>Compliance</a></li><li><a href=https://www.eclipse.org/legal/>Legal</a></li></ul></div></section></div></div></footer><div id=footer-js><script src="/public/js/main.js?v1.0?v=1764530174"></script><script>(function(){})(jQuery,document)</script></div></body></html>
42
+ <i class="fa-solid fa-arrow-right" aria-hidden=true></i></button></div></form></section><section class=footer-bottom-section><img class="img-responsive logo" src=https://www.eclipse.org/eclipse.org-common/themes/solstice/public/images/logo/eclipse-foundation-white.svg alt="Eclipse Foundation"><div class=legal-information><p>Copyright &copy; Eclipse Foundation AISBL. All rights reserved.</p><ul class=legal-links><li><a href=https://www.eclipse.org/legal/privacy/>Privacy policy</a></li><li><a href=https://www.eclipse.org/legal/terms-of-use/>Terms of use</a></li><li><a href=https://www.eclipse.org/legal/compliance/>Compliance</a></li><li><a href=https://www.eclipse.org/legal/>Legal</a></li></ul></div></section></div></div></footer><div id=footer-js><script src="/public/js/main.js?v1.0?v=1765134958"></script><script>(function(){})(jQuery,document)</script></div></body></html>