summary refs log tree commit diff stats
path: root/main.go
blob: 731a4aeff0d7e9354e713b92447476f2cddf56d9 (plain) (blame)
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
// Copyright (c) 2020, Andinus <andinus@inventati.org>

// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.

// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

package main

import (
	"flag"
	"fmt"
	"log"
	"math/rand"
	"net/http"
	"os/exec"
	"strconv"
	"time"
)

var (
	timeout time.Duration

	unsplashAPI string

	width  int
	height int
)

func main() {
	rand.Seed(time.Now().Unix())

	var (
		err error

		imgPath string
		wall    string
		src     string
		srcArr  []string = []string{
			"unsplash",
		}
	)

	// Parse flags passed to program
	flag.StringVar(&src, "src", "random", "Source for the image")
	flag.StringVar(&wall, "wall", "random", "Daily, Weekly or Random wallpaper")

	flag.IntVar(&width, "width", 1920, "Width of the image")
	flag.IntVar(&height, "height", 1080, "Height of the image")

	flag.StringVar(&unsplashAPI, "unsplash-api", "https://source.unsplash.com", "Unsplash Source API URL")
	flag.DurationVar(&timeout, "timeout", 16, "Timeout for http client")
	flag.Parse()

	if src == "random" {
		src = srcArr[rand.Intn(len(srcArr))]
	}

	// Check if the source is known
	if !contains(srcArr, src) {
		log.Fatal("Error: Unknown Source")
	}

	imgPath, err = parseSrcAndGetPath(src, wall)
	errChk(err)

	err = setWall(imgPath)
	errChk(err)
}

func contains(arr []string, str string) bool {
	for _, i := range arr {
		if i == str {
			return true
		}
	}
	return false
}

// Gets image path from src
func parseSrcAndGetPath(src string, wall string) (string, error) {
	var err error
	var imgPath string

	switch src {
	case "unsplash":
		fmt.Println("Unsplash Source")
		imgPath, err = getPathUnsplash(wall)
	}

	return imgPath, err
}

func getPathUnsplash(wall string) (string, error) {
	var err error
	var imgPath string

	switch wall {
	case "daily", "weekly":
		unsplashAPI = fmt.Sprintf("%s/%s",
			unsplashAPI, wall)
	case "random":
		unsplashAPI = fmt.Sprintf("%s/%sx%s",
			unsplashAPI, strconv.Itoa(width), strconv.Itoa(height))
	default:
		return "", fmt.Errorf("Error: Unknown wall")
	}

	req, err := http.NewRequest(http.MethodGet, unsplashAPI, nil)
	if err != nil {
		return "", err
	}

	res, err := getRes(req)
	if err != nil {
		return "", err
	}
	defer res.Body.Close()

	// Unsplash Source API will redirect to the image
	imgPath = res.Request.URL.String()
	return imgPath, err
}

// Calls feh to set the wallpaper
func setWall(imgPath string) error {
	feh, err := exec.LookPath("feh")
	if err != nil {
		fmt.Println("Error: feh is not in $PATH")
		return err
	}

	fmt.Printf("Path to set as Wallpaper: %s\n", imgPath)

	err = exec.Command(feh, "--bg-fill", imgPath).Run()
	return err
}

func getRes(req *http.Request) (*http.Response, error) {
	client := http.Client{
		Timeout: time.Second * timeout,
	}
	res, err := client.Do(req)

	return res, err
}

func errChk(err error) {
	if err != nil {
		log.Fatal(err)
	}
}
"Constant">34</span> ] $warn: <span class="Constant">0</span> <span class="Delimiter">:(scenario ignore_literal_ingredients_for_immutability_checks)</span> <span class="Special">% Hide_warnings = true;</span> recipe main [ local-scope p:address:d1<span class="Special"> &lt;- </span>new d1:type q:number<span class="Special"> &lt;- </span>foo p ] recipe foo p:address:d1 <span class="Delimiter">-&gt;</span> q:number [ local-scope load-ingredients x:address:d1<span class="Special"> &lt;- </span>new d1:type y:address:number<span class="Special"> &lt;- </span>get-address *x<span class="Delimiter">,</span> p:offset <span class="Comment"># ignore this 'p'</span> q<span class="Special"> &lt;- </span>copy <span class="Constant">34</span> ] container d1 [ p:number q:number ] $warn: <span class="Constant">0</span> <span class="Delimiter">:(scenario cannot_take_address_inside_immutable_ingredients)</span> <span class="Special">% Hide_warnings = true;</span> recipe main [ local-scope p:address:point<span class="Special"> &lt;- </span>new point:type foo p ] recipe foo p:address:point [ local-scope load-ingredients x:address:number<span class="Special"> &lt;- </span>get-address *p<span class="Delimiter">,</span> x:offset *x<span class="Special"> &lt;- </span>copy <span class="Constant">34</span> ] <span class="traceContains">+warn: foo: cannot modify ingredient p after instruction 'x:address:number &lt;- get-address *p, x:offset' because it's not also a product of foo</span> <span class="Delimiter">:(scenario cannot_call_mutating_recipes_on_immutable_ingredients)</span> <span class="Special">% Hide_warnings = true;</span> recipe main [ local-scope p:address:point<span class="Special"> &lt;- </span>new point:type foo p ] recipe foo p:address:point [ local-scope load-ingredients bar p ] recipe bar p:address:point <span class="Delimiter">-&gt;</span> p:address:point [ local-scope load-ingredients x:address:number<span class="Special"> &lt;- </span>get-address *p<span class="Delimiter">,</span> x:offset *x<span class="Special"> &lt;- </span>copy <span class="Constant">34</span> ] <span class="traceContains">+warn: foo: cannot modify ingredient p at instruction 'bar p' because it's not also a product of foo</span> <span class="Delimiter">:(scenario cannot_modify_copies_of_immutable_ingredients)</span> <span class="Special">% Hide_warnings = true;</span> recipe main [ local-scope p:address:point<span class="Special"> &lt;- </span>new point:type foo p ] recipe foo p:address:point [ local-scope load-ingredients q:address:point<span class="Special"> &lt;- </span>copy p x:address:number<span class="Special"> &lt;- </span>get-address *q<span class="Delimiter">,</span> x:offset ] <span class="traceContains">+warn: foo: cannot modify q after instruction 'x:address:number &lt;- get-address *q, x:offset' because that would modify ingredient p which is not also a product of foo</span> <span class="Delimiter">:(scenario can_traverse_immutable_ingredients)</span> <span class="Special">% Hide_warnings = true;</span> container test-list [ next:address:test-list ] recipe main [ local-scope p:address:test-list<span class="Special"> &lt;- </span>new test-list:type foo p ] recipe foo p:address:test-list [ local-scope load-ingredients p2:address:test-list<span class="Special"> &lt;- </span>bar p ] recipe bar x:address:test-list <span class="Delimiter">-&gt;</span> y:address:test-list [ local-scope load-ingredients y<span class="Special"> &lt;- </span>get *x<span class="Delimiter">,</span> next:offset ] $warn: <span class="Constant">0</span> <span class="Delimiter">:(scenario handle_optional_ingredients_in_immutability_checks)</span> <span class="Special">% Hide_warnings = true;</span> recipe main [ k:address:number<span class="Special"> &lt;- </span>new number:type test k ] <span class="Comment"># recipe taking an immutable address ingredient</span> recipe test k:address:number [ local-scope load-ingredients foo k ] <span class="Comment"># ..calling a recipe with an optional address ingredient</span> recipe foo <span class="Delimiter">-&gt;</span> [ local-scope load-ingredients k:address:number<span class="Delimiter">,</span> found?:boolean<span class="Special"> &lt;- </span>next-ingredient ] $warn: <span class="Constant">0</span> <span class="Delimiter">:(before &quot;End Transforms&quot;)</span> Transform<span class="Delimiter">.</span>push_back<span class="Delimiter">(</span>check_immutable_ingredients<span class="Delimiter">);</span> <span class="Comment">// idempotent</span> <span class="Delimiter">:(code)</span> void check_immutable_ingredients<span class="Delimiter">(</span>recipe_ordinal r<span class="Delimiter">)</span> <span class="Delimiter">{</span> <span class="Comment">// to ensure a reagent isn't modified, it suffices to show that we never</span> <span class="Comment">// call get-address or index-address with it, and that any non-primitive</span> <span class="Comment">// recipe calls in the body aren't returning it as a product.</span> const recipe&amp; caller = get<span class="Delimiter">(</span>Recipe<span class="Delimiter">,</span> r<span class="Delimiter">);</span> <span class="CommentedCode">//? cerr &lt;&lt; caller.name &lt;&lt; '\n';</span> if <span class="Delimiter">(</span>!caller<span class="Delimiter">.</span>has_header<span class="Delimiter">)</span> <span class="Identifier">return</span><span class="Delimiter">;</span> <span class="Comment">// skip check for old-style recipes calling next-ingredient directly</span> for <span class="Delimiter">(</span>long long int i = <span class="Constant">0</span><span class="Delimiter">;</span> i &lt; SIZE<span class="Delimiter">(</span>caller<span class="Delimiter">.</span>ingredients<span class="Delimiter">);</span> ++i<span class="Delimiter">)</span> <span class="Delimiter">{</span> const reagent&amp; current_ingredient = caller<span class="Delimiter">.</span>ingredients<span class="Delimiter">.</span>at<span class="Delimiter">(</span>i<span class="Delimiter">);</span> <span class="CommentedCode">//? cerr &lt;&lt; &quot; &quot; &lt;&lt; current_ingredient.original_string &lt;&lt; '\n';</span> if <span class="Delimiter">(</span>!is_mu_address<span class="Delimiter">(</span>current_ingredient<span class="Delimiter">))</span> <span class="Identifier">continue</span><span class="Delimiter">;</span> <span class="Comment">// will be copied</span> if <span class="Delimiter">(</span>is_present_in_products<span class="Delimiter">(</span>caller<span class="Delimiter">,</span> current_ingredient<span class="Delimiter">.</span>name<span class="Delimiter">))</span> <span class="Identifier">continue</span><span class="Delimiter">;</span> <span class="Comment">// not expected to be immutable</span> <span class="Comment">// End Immutable Ingredients Special-cases</span> set&lt;string&gt; immutable_vars<span class="Delimiter">;</span> immutable_vars<span class="Delimiter">.</span>insert<span class="Delimiter">(</span>current_ingredient<span class="Delimiter">.</span>name<span class="Delimiter">);</span> for <span class="Delimiter">(</span>long long int i = <span class="Constant">0</span><span class="Delimiter">;</span> i &lt; SIZE<span class="Delimiter">(</span>caller<span class="Delimiter">.</span>steps<span class="Delimiter">);</span> ++i<span class="Delimiter">)</span> <span class="Delimiter">{</span> const instruction&amp; inst = caller<span class="Delimiter">.</span>steps<span class="Delimiter">.</span>at<span class="Delimiter">(</span>i<span class="Delimiter">);</span> <span class="CommentedCode">//? cerr &lt;&lt; &quot; &quot; &lt;&lt; inst.to_string() &lt;&lt; '\n';</span> check_immutable_ingredient_in_instruction<span class="Delimiter">(</span>inst<span class="Delimiter">,</span> immutable_vars<span class="Delimiter">,</span> current_ingredient<span class="Delimiter">.</span>name<span class="Delimiter">,</span> caller<span class="Delimiter">);</span> update_aliases<span class="Delimiter">(</span>inst<span class="Delimiter">,</span> immutable_vars<span class="Delimiter">);</span> <span class="Delimiter">}</span> <span class="Delimiter">}</span> <span class="Delimiter">}</span> void update_aliases<span class="Delimiter">(</span>const instruction&amp; inst<span class="Delimiter">,</span> set&lt;string&gt;&amp; current_ingredient_and_aliases<span class="Delimiter">)</span> <span class="Delimiter">{</span> set&lt;long long int&gt; current_ingredient_indices = ingredient_indices<span class="Delimiter">(</span>inst<span class="Delimiter">,</span> current_ingredient_and_aliases<span class="Delimiter">);</span> if <span class="Delimiter">(</span>!contains_key<span class="Delimiter">(</span>Recipe<span class="Delimiter">,</span> inst<span class="Delimiter">.</span>operation<span class="Delimiter">))</span> <span class="Delimiter">{</span> <span class="Comment">// primitive recipe</span> if <span class="Delimiter">(</span>inst<span class="Delimiter">.</span>operation == COPY<span class="Delimiter">)</span> <span class="Delimiter">{</span> for <span class="Delimiter">(</span>set&lt;long long int&gt;::iterator p = current_ingredient_indices<span class="Delimiter">.</span>begin<span class="Delimiter">();</span> p != current_ingredient_indices<span class="Delimiter">.</span>end<span class="Delimiter">();</span> ++p<span class="Delimiter">)</span> <span class="Delimiter">{</span> current_ingredient_and_aliases<span class="Delimiter">.</span>insert<span class="Delimiter">(</span>inst<span class="Delimiter">.</span>products<span class="Delimiter">.</span>at<span class="Delimiter">(</span>*p<span class="Delimiter">).</span>name<span class="Delimiter">);</span> <span class="Delimiter">}</span> <span class="Delimiter">}</span> <span class="Delimiter">}</span> else <span class="Delimiter">{</span> <span class="Comment">// defined recipe</span> set&lt;long long int&gt; contained_in_product_indices = scan_contained_in_product_indices<span class="Delimiter">(</span>inst<span class="Delimiter">,</span> current_ingredient_indices<span class="Delimiter">);</span> for <span class="Delimiter">(</span>set&lt;long long int&gt;::iterator p = contained_in_product_indices<span class="Delimiter">.</span>begin<span class="Delimiter">();</span> p != contained_in_product_indices<span class="Delimiter">.</span>end<span class="Delimiter">();</span> ++p<span class="Delimiter">)</span> <span class="Delimiter">{</span> if <span class="Delimiter">(</span>*p &lt; SIZE<span class="Delimiter">(</span>inst<span class="Delimiter">.</span>products<span class="Delimiter">))</span> current_ingredient_and_aliases<span class="Delimiter">.</span>insert<span class="Delimiter">(</span>inst<span class="Delimiter">.</span>products<span class="Delimiter">.</span>at<span class="Delimiter">(</span>*p<span class="Delimiter">).</span>name<span class="Delimiter">);</span> <span class="Delimiter">}</span> <span class="Delimiter">}</span> <span class="Delimiter">}</span> set&lt;long long int&gt; scan_contained_in_product_indices<span class="Delimiter">(</span>const instruction&amp; inst<span class="Delimiter">,</span> set&lt;long long int&gt;&amp; ingredient_indices<span class="Delimiter">)</span> <span class="Delimiter">{</span> set&lt;string&gt; selected_ingredient_names<span class="Delimiter">;</span> const recipe&amp; callee = get<span class="Delimiter">(</span>Recipe<span class="Delimiter">,</span> inst<span class="Delimiter">.</span>operation<span class="Delimiter">);</span> for <span class="Delimiter">(</span>set&lt;long long int&gt;::iterator p = ingredient_indices<span class="Delimiter">.</span>begin<span class="Delimiter">();</span> p != ingredient_indices<span class="Delimiter">.</span>end<span class="Delimiter">();</span> ++p<span class="Delimiter">)</span> <span class="Delimiter">{</span> if <span class="Delimiter">(</span>*p &gt;= SIZE<span class="Delimiter">(</span>callee<span class="Delimiter">.</span>ingredients<span class="Delimiter">))</span> <span class="Identifier">continue</span><span class="Delimiter">;</span> <span class="Comment">// optional immutable ingredient</span> selected_ingredient_names<span class="Delimiter">.</span>insert<span class="Delimiter">(</span>callee<span class="Delimiter">.</span>ingredients<span class="Delimiter">.</span>at<span class="Delimiter">(</span>*p<span class="Delimiter">).</span>name<span class="Delimiter">);</span> <span class="Delimiter">}</span> set&lt;long long int&gt; result<span class="Delimiter">;</span> for <span class="Delimiter">(</span>long long int i = <span class="Constant">0</span><span class="Delimiter">;</span> i &lt; SIZE<span class="Delimiter">(</span>callee<span class="Delimiter">.</span>products<span class="Delimiter">);</span> ++i<span class="Delimiter">)</span> <span class="Delimiter">{</span> const reagent&amp; current_product = callee<span class="Delimiter">.</span>products<span class="Delimiter">.</span>at<span class="Delimiter">(</span>i<span class="Delimiter">);</span> const string_tree* contained_in_name = property<span class="Delimiter">(</span>current_product<span class="Delimiter">,</span> <span class="Constant">&quot;contained-in&quot;</span><span class="Delimiter">);</span> if <span class="Delimiter">(</span>contained_in_name &amp;&amp; selected_ingredient_names<span class="Delimiter">.</span>find<span class="Delimiter">(</span>contained_in_name<span class="Delimiter">-&gt;</span>value<span class="Delimiter">)</span> != selected_ingredient_names<span class="Delimiter">.</span>end<span class="Delimiter">())</span> result<span class="Delimiter">.</span>insert<span class="Delimiter">(</span>i<span class="Delimiter">);</span> <span class="Delimiter">}</span> <span class="Identifier">return</span> result<span class="Delimiter">;</span> <span class="Delimiter">}</span> <span class="Delimiter">:(scenarios transform)</span> <span class="Delimiter">:(scenario immutability_infects_contained_in_variables)</span> <span class="Special">% Hide_warnings = true;</span> container test-list [ next:address:test-list ] recipe main [ local-scope p:address:test-list<span class="Special"> &lt;- </span>new test-list:type foo p ] recipe foo p:address:test-list [ <span class="Comment"># p is immutable</span> local-scope load-ingredients p2:address:test-list<span class="Special"> &lt;- </span>test-next p <span class="Comment"># p2 is immutable</span> p3:address:address:test-list<span class="Special"> &lt;- </span>get-address *p2<span class="Delimiter">,</span> next:offset <span class="Comment"># signal modification of p2</span> ] recipe test-next x:address:test-list <span class="Delimiter">-&gt;</span> y:address:test-list/contained-in:x [ local-scope load-ingredients y<span class="Special"> &lt;- </span>get *x<span class="Delimiter">,</span> next:offset ] <span class="traceContains">+warn: foo: cannot modify p2 after instruction 'p3:address:address:test-list &lt;- get-address *p2, next:offset' because that would modify ingredient p which is not also a product of foo</span> <span class="Delimiter">:(code)</span> void check_immutable_ingredient_in_instruction<span class="Delimiter">(</span>const instruction&amp; inst<span class="Delimiter">,</span> const set&lt;string&gt;&amp; current_ingredient_and_aliases<span class="Delimiter">,</span> const string&amp; original_ingredient_name<span class="Delimiter">,</span> const recipe&amp; caller<span class="Delimiter">)</span> <span class="Delimiter">{</span> set&lt;long long int&gt; current_ingredient_indices = ingredient_indices<span class="Delimiter">(</span>inst<span class="Delimiter">,</span> current_ingredient_and_aliases<span class="Delimiter">);</span> if <span class="Delimiter">(</span>current_ingredient_indices<span class="Delimiter">.</span>empty<span class="Delimiter">())</span> <span class="Identifier">return</span><span class="Delimiter">;</span> <span class="Comment">// ingredient not found in call</span> for <span class="Delimiter">(</span>set&lt;long long int&gt;::iterator p = current_ingredient_indices<span class="Delimiter">.</span>begin<span class="Delimiter">();</span> p != current_ingredient_indices<span class="Delimiter">.</span>end<span class="Delimiter">();</span> ++p<span class="Delimiter">)</span> <span class="Delimiter">{</span> const long long int current_ingredient_index = *p<span class="Delimiter">;</span> <span class="CommentedCode">//? cerr &lt;&lt; &quot; ingredient index: &quot; &lt;&lt; *p &lt;&lt; '\n';</span> reagent current_ingredient = inst<span class="Delimiter">.</span>ingredients<span class="Delimiter">.</span>at<span class="Delimiter">(</span>current_ingredient_index<span class="Delimiter">);</span> canonize_type<span class="Delimiter">(</span>current_ingredient<span class="Delimiter">);</span> const string&amp; current_ingredient_name = current_ingredient<span class="Delimiter">.</span>name<span class="Delimiter">;</span> if <span class="Delimiter">(</span>!contains_key<span class="Delimiter">(</span>Recipe<span class="Delimiter">,</span> inst<span class="Delimiter">.</span>operation<span class="Delimiter">))</span> <span class="Delimiter">{</span> <span class="Comment">// primitive recipe</span> if <span class="Delimiter">(</span>inst<span class="Delimiter">.</span>operation == GET_ADDRESS || inst<span class="Delimiter">.</span>operation == INDEX_ADDRESS<span class="Delimiter">)</span> <span class="Delimiter">{</span> if <span class="Delimiter">(</span>current_ingredient_name == original_ingredient_name<span class="Delimiter">)</span> raise &lt;&lt; maybe<span class="Delimiter">(</span>caller<span class="Delimiter">.</span>name<span class="Delimiter">)</span> &lt;&lt; <span class="Constant">&quot;cannot modify ingredient &quot;</span> &lt;&lt; current_ingredient_name &lt;&lt; <span class="Constant">&quot; after instruction '&quot;</span> &lt;&lt; inst<span class="Delimiter">.</span>to_string<span class="Delimiter">()</span> &lt;&lt; <span class="Constant">&quot;' because it's not also a product of &quot;</span> &lt;&lt; caller<span class="Delimiter">.</span>name &lt;&lt; <span class="cSpecial">'\n'</span> &lt;&lt; end<span class="Delimiter">();</span> else raise &lt;&lt; maybe<span class="Delimiter">(</span>caller<span class="Delimiter">.</span>name<span class="Delimiter">)</span> &lt;&lt; <span class="Constant">&quot;cannot modify &quot;</span> &lt;&lt; current_ingredient_name &lt;&lt; <span class="Constant">&quot; after instruction '&quot;</span> &lt;&lt; inst<span class="Delimiter">.</span>to_string<span class="Delimiter">()</span> &lt;&lt; <span class="Constant">&quot;' because that would modify ingredient &quot;</span> &lt;&lt; original_ingredient_name &lt;&lt; <span class="Constant">&quot; which is not also a product of &quot;</span> &lt;&lt; caller<span class="Delimiter">.</span>name &lt;&lt; <span class="cSpecial">'\n'</span> &lt;&lt; end<span class="Delimiter">();</span> <span class="Delimiter">}</span> <span class="Delimiter">}</span> else <span class="Delimiter">{</span> <span class="Comment">// defined recipe</span> if <span class="Delimiter">(</span>!is_mu_address<span class="Delimiter">(</span>current_ingredient<span class="Delimiter">))</span> <span class="Identifier">return</span><span class="Delimiter">;</span> <span class="Comment">// making a copy is ok</span> if <span class="Delimiter">(</span>is_modified_in_recipe<span class="Delimiter">(</span>inst<span class="Delimiter">.</span>operation<span class="Delimiter">,</span> current_ingredient_index<span class="Delimiter">,</span> caller<span class="Delimiter">))</span> <span class="Delimiter">{</span> if <span class="Delimiter">(</span>current_ingredient_name == original_ingredient_name<span class="Delimiter">)</span> raise &lt;&lt; maybe<span class="Delimiter">(</span>caller<span class="Delimiter">.</span>name<span class="Delimiter">)</span> &lt;&lt; <span class="Constant">&quot;cannot modify ingredient &quot;</span> &lt;&lt; current_ingredient_name &lt;&lt; <span class="Constant">&quot; at instruction '&quot;</span> &lt;&lt; inst<span class="Delimiter">.</span>to_string<span class="Delimiter">()</span> &lt;&lt; <span class="Constant">&quot;' because it's not also a product of &quot;</span> &lt;&lt; caller<span class="Delimiter">.</span>name &lt;&lt; <span class="cSpecial">'\n'</span> &lt;&lt; end<span class="Delimiter">();</span> else raise &lt;&lt; maybe<span class="Delimiter">(</span>caller<span class="Delimiter">.</span>name<span class="Delimiter">)</span> &lt;&lt; <span class="Constant">&quot;cannot modify &quot;</span> &lt;&lt; current_ingredient_name &lt;&lt; <span class="Constant">&quot; after instruction '&quot;</span> &lt;&lt; inst<span class="Delimiter">.</span>to_string<span class="Delimiter">()</span> &lt;&lt; <span class="Constant">&quot;' because that would modify ingredient &quot;</span> &lt;&lt; original_ingredient_name &lt;&lt; <span class="Constant">&quot; which is not also a product of &quot;</span> &lt;&lt; caller<span class="Delimiter">.</span>name &lt;&lt; <span class="cSpecial">'\n'</span> &lt;&lt; end<span class="Delimiter">();</span> <span class="Delimiter">}</span> <span class="Delimiter">}</span> <span class="Delimiter">}</span> <span class="Delimiter">}</span> bool is_modified_in_recipe<span class="Delimiter">(</span>recipe_ordinal r<span class="Delimiter">,</span> long long int ingredient_index<span class="Delimiter">,</span> const recipe&amp; caller<span class="Delimiter">)</span> <span class="Delimiter">{</span> const recipe&amp; callee = get<span class="Delimiter">(</span>Recipe<span class="Delimiter">,</span> r<span class="Delimiter">);</span> if <span class="Delimiter">(</span>!callee<span class="Delimiter">.</span>has_header<span class="Delimiter">)</span> <span class="Delimiter">{</span> raise &lt;&lt; maybe<span class="Delimiter">(</span>caller<span class="Delimiter">.</span>name<span class="Delimiter">)</span> &lt;&lt; <span class="Constant">&quot;can't check mutability of ingredients in &quot;</span> &lt;&lt; callee<span class="Delimiter">.</span>name &lt;&lt; <span class="Constant">&quot; because it uses 'next-ingredient' directly, rather than a recipe header.</span><span class="cSpecial">\n</span><span class="Constant">&quot;</span> &lt;&lt; end<span class="Delimiter">();</span> <span class="Identifier">return</span> <span class="Constant">true</span><span class="Delimiter">;</span> <span class="Delimiter">}</span> if <span class="Delimiter">(</span>ingredient_index &gt;= SIZE<span class="Delimiter">(</span>callee<span class="Delimiter">.</span>ingredients<span class="Delimiter">))</span> <span class="Identifier">return</span> <span class="Constant">false</span><span class="Delimiter">;</span> <span class="Comment">// optional immutable ingredient</span> <span class="Identifier">return</span> is_present_in_products<span class="Delimiter">(</span>callee<span class="Delimiter">,</span> callee<span class="Delimiter">.</span>ingredients<span class="Delimiter">.</span>at<span class="Delimiter">(</span>ingredient_index<span class="Delimiter">).</span>name<span class="Delimiter">);</span> <span class="Delimiter">}</span> bool is_present_in_products<span class="Delimiter">(</span>const recipe&amp; callee<span class="Delimiter">,</span> const string&amp; ingredient_name<span class="Delimiter">)</span> <span class="Delimiter">{</span> for <span class="Delimiter">(</span>long long int i = <span class="Constant">0</span><span class="Delimiter">;</span> i &lt; SIZE<span class="Delimiter">(</span>callee<span class="Delimiter">.</span>products<span class="Delimiter">);</span> ++i<span class="Delimiter">)</span> <span class="Delimiter">{</span> if <span class="Delimiter">(</span>callee<span class="Delimiter">.</span>products<span class="Delimiter">.</span>at<span class="Delimiter">(</span>i<span class="Delimiter">).</span>name == ingredient_name<span class="Delimiter">)</span> <span class="Identifier">return</span> <span class="Constant">true</span><span class="Delimiter">;</span> <span class="Delimiter">}</span> <span class="Identifier">return</span> <span class="Constant">false</span><span class="Delimiter">;</span> <span class="Delimiter">}</span> bool is_present_in_ingredients<span class="Delimiter">(</span>const recipe&amp; callee<span class="Delimiter">,</span> const string&amp; ingredient_name<span class="Delimiter">)</span> <span class="Delimiter">{</span> for <span class="Delimiter">(</span>long long int i = <span class="Constant">0</span><span class="Delimiter">;</span> i &lt; SIZE<span class="Delimiter">(</span>callee<span class="Delimiter">.</span>ingredients<span class="Delimiter">);</span> ++i<span class="Delimiter">)</span> <span class="Delimiter">{</span> if <span class="Delimiter">(</span>callee<span class="Delimiter">.</span>ingredients<span class="Delimiter">.</span>at<span class="Delimiter">(</span>i<span class="Delimiter">).</span>name == ingredient_name<span class="Delimiter">)</span> <span class="Identifier">return</span> <span class="Constant">true</span><span class="Delimiter">;</span> <span class="Delimiter">}</span> <span class="Identifier">return</span> <span class="Constant">false</span><span class="Delimiter">;</span> <span class="Delimiter">}</span> set&lt;long long int&gt; ingredient_indices<span class="Delimiter">(</span>const instruction&amp; inst<span class="Delimiter">,</span> const set&lt;string&gt;&amp; ingredient_names<span class="Delimiter">)</span> <span class="Delimiter">{</span> set&lt;long long int&gt; result<span class="Delimiter">;</span> for <span class="Delimiter">(</span>long long int i = <span class="Constant">0</span><span class="Delimiter">;</span> i &lt; SIZE<span class="Delimiter">(</span>inst<span class="Delimiter">.</span>ingredients<span class="Delimiter">);</span> ++i<span class="Delimiter">)</span> <span class="Delimiter">{</span> if <span class="Delimiter">(</span>is_literal<span class="Delimiter">(</span>inst<span class="Delimiter">.</span>ingredients<span class="Delimiter">.</span>at<span class="Delimiter">(</span>i<span class="Delimiter">)))</span> <span class="Identifier">continue</span><span class="Delimiter">;</span> if <span class="Delimiter">(</span>ingredient_names<span class="Delimiter">.</span>find<span class="Delimiter">(</span>inst<span class="Delimiter">.</span>ingredients<span class="Delimiter">.</span>at<span class="Delimiter">(</span>i<span class="Delimiter">).</span>name<span class="Delimiter">)</span> != ingredient_names<span class="Delimiter">.</span>end<span class="Delimiter">())</span> result<span class="Delimiter">.</span>insert<span class="Delimiter">(</span>i<span class="Delimiter">);</span> <span class="Delimiter">}</span> <span class="Identifier">return</span> result<span class="Delimiter">;</span> <span class="Delimiter">}</span> <span class="Comment">//: Sometimes you want to pass in two addresses, one pointing inside the</span> <span class="Comment">//: other. For example, you want to delete a node from a linked list. You</span> <span class="Comment">//: can't pass both pointers back out, because if a caller tries to make both</span> <span class="Comment">//: identical then you can't tell which value will be written on the way out.</span> <span class="Comment">//:</span> <span class="Comment">//: Experimental solution: just tell mu that one points inside the other.</span> <span class="Comment">//: This way we can return just one pointer as high up as necessary to capture</span> <span class="Comment">//: all modifications performed by a recipe.</span> <span class="Comment">//:</span> <span class="Comment">//: We'll see if we end up wanting to abuse /contained-in for other reasons.</span> <span class="Delimiter">:(scenarios transform)</span> <span class="Delimiter">:(scenario can_modify_contained_in_addresses)</span> <span class="Special">% Hide_warnings = true;</span> container test-list [ next:address:test-list ] recipe main [ local-scope p:address:test-list<span class="Special"> &lt;- </span>new test-list:type foo p ] recipe foo p:address:test-list <span class="Delimiter">-&gt;</span> p:address:test-list [ local-scope load-ingredients p2:address:test-list<span class="Special"> &lt;- </span>test-next p p<span class="Special"> &lt;- </span>test-remove p2<span class="Delimiter">,</span> p ] recipe test-next x:address:test-list <span class="Delimiter">-&gt;</span> y:address:test-list [ local-scope load-ingredients y<span class="Special"> &lt;- </span>get *x<span class="Delimiter">,</span> next:offset ] recipe test-remove x:address:test-list/contained-in:from<span class="Delimiter">,</span> from:address:test-list <span class="Delimiter">-&gt;</span> from:address:test-list [ local-scope load-ingredients x2:address:address:test-list<span class="Special"> &lt;- </span>get-address *x<span class="Delimiter">,</span> next:offset <span class="Comment"># pretend modification</span> ] $warn: <span class="Constant">0</span> <span class="Delimiter">:(before &quot;End Immutable Ingredients Special-cases&quot;)</span> if <span class="Delimiter">(</span>has_property<span class="Delimiter">(</span>current_ingredient<span class="Delimiter">,</span> <span class="Constant">&quot;contained-in&quot;</span><span class="Delimiter">))</span> <span class="Delimiter">{</span> const string_tree* tmp = property<span class="Delimiter">(</span>current_ingredient<span class="Delimiter">,</span> <span class="Constant">&quot;contained-in&quot;</span><span class="Delimiter">);</span> if <span class="Delimiter">(</span>tmp<span class="Delimiter">-&gt;</span>left || tmp<span class="Delimiter">-&gt;</span>right || !is_present_in_ingredients<span class="Delimiter">(</span>caller<span class="Delimiter">,</span> tmp<span class="Delimiter">-&gt;</span>value<span class="Delimiter">)</span> || !is_present_in_products<span class="Delimiter">(</span>caller<span class="Delimiter">,</span> tmp<span class="Delimiter">-&gt;</span>value<span class="Delimiter">))</span> raise_error &lt;&lt; maybe<span class="Delimiter">(</span>caller<span class="Delimiter">.</span>name<span class="Delimiter">)</span> &lt;&lt; <span class="Constant">&quot;contained-in can only point to another ingredient+product, but got &quot;</span> &lt;&lt; debug_string<span class="Delimiter">(</span>property<span class="Delimiter">(</span>current_ingredient<span class="Delimiter">,</span> <span class="Constant">&quot;contained-in&quot;</span><span class="Delimiter">))</span> &lt;&lt; <span class="cSpecial">'\n'</span> &lt;&lt; end<span class="Delimiter">();</span> <span class="Identifier">continue</span><span class="Delimiter">;</span> <span class="Delimiter">}</span> </pre> </body> </html> <!-- vim: set foldmethod=manual : -->