├── .gitignore
├── README.md
├── lec01
└── lec01-computer-science.pdf
├── lec02
└── lec02-functions.pdf
├── lec03
├── demo.py
└── lec03-control.pdf
├── lec04
├── demo.py
└── lec04-higher-order-functions.pdf
├── lec05
└── lec05-environments.pdf
├── lec06
└── lec06-design.pdf
├── lec07
├── demo
│ └── demo.py
└── lec07-recursion.pdf
├── lec08
├── demo
│ └── demo.py
└── lec08-tree-recursion.pdf
├── lec09
├── demo
│ └── demo.py
└── lec09-containers.pdf
├── lec10
├── demo
│ ├── demo.py
│ └── demo2.py
└── lec10-data-abstraction.pdf
├── lec11
├── demo
│ └── demo.py
└── lec11-trees.pdf
├── lec12
├── demo
│ └── dog.c
└── lec12-mutable-values.pdf
├── lec13
├── demo
│ └── demo.py
└── lec13-mutable-functions.pdf
├── lec14
├── demo
│ └── demo.py
└── lec14-iterators.pdf
├── lec15
├── demo
│ └── demo.py
└── lec15-objects.pdf
├── lec16
├── demo
│ └── demo.py
└── lec16-inheritance.pdf
├── lec17
├── demo
│ └── demo.py
└── lec17-representation.pdf
├── lec18
├── demo
│ └── demo.py
└── lec18-composition.pdf
├── lec19
├── demo
│ └── demo.py
└── lec19-efficiency.pdf
├── lec20
├── demo
│ └── demo.py
└── lec20-decomposition.pdf
├── lec21
├── demo
│ ├── demo.scm
│ └── scheme
└── lec21-scheme.pdf
├── lec22
├── demo
│ └── demo.py
└── lec22-exceptions.pdf
├── lec23
├── demo
│ ├── buffer.py
│ ├── scalc.py
│ ├── scheme_reader.py
│ ├── scheme_tokens.py
│ └── ucb.py
└── lec23-calculator.pdf
├── lec24
└── lec24-interpreters.pdf
├── lec25
├── demo
│ ├── demo.sql
│ └── sqlite_shell.py
└── lec25-declarative-programming.pdf
├── lec26
├── demo
│ ├── demo.sql
│ └── sqlite_shell.py
└── lec26-tables.pdf
├── lec27
├── demo
│ ├── demo.sql
│ └── sqlite_shell.py
└── lec27-aggregation.pdf
├── lec28
└── lec28-database.pdf
├── lec29
└── lec29-tail-calls.pdf
└── lec30
└── lec30-macros.pdf
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | *.pptx
3 | __pycache__
4 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # SICP系列更新日志
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | | 条目 | 主题 | 链接 |
10 | | ------ | ------------------------------------------------------------ | ------------------------------------------- |
11 | | lec01 | Computer Science(计算机科学) | https://www.bilibili.com/video/BV1ML4y1P7KN |
12 | | lab00 | Start Up(环境配置和测试评分系统) | https://www.bilibili.com/video/BV1aY411N7ZQ |
13 | | lec02 | Functions(函数) | https://www.bilibili.com/video/BV1rv4y1M7GP |
14 | | hw01 | Variables&Functions,Control(变量、函数和控制) | https://www.bilibili.com/video/BV13Z4y1i7Jw |
15 | | lec03 | Control(控制) | https://www.bilibili.com/video/BV1QT411g7dp |
16 | | lab01 | Variables&Functions,Control(变量、函数和控制) | https://www.bilibili.com/video/BV1yB4y1v7F7 |
17 | | lec04 | Higher-Order Functions(高阶函数) | https://www.bilibili.com/video/BV1MB4y1B7GD |
18 | | proj01 | Hog(野猪掷骰子) | https://www.bilibili.com/video/BV1mY4y1n7i5 |
19 | | lec05 | Environments(各种场景下的程序环境) | https://www.bilibili.com/video/BV1Jr4y1u7Pf |
20 | | lab02 | Higher-Order Function and Lambda Expression(高阶函数和匿名函数) | https://www.bilibili.com/video/BV1zf4y1Z7MQ |
21 | | lec06 | Design(函数设计) | https://www.bilibili.com/video/BV1RS4y1n7CX |
22 | | lab03 | Midterm Review(阶段性复习) | https://www.bilibili.com/video/BV1U34y1n79U |
23 | | lec07 | Recursion(递归) | https://www.bilibili.com/video/BV1Ca411p7j5 |
24 | | lec08 | Tree Recursion(树形递归) | https://www.bilibili.com/video/BV1FT411J7Tq |
25 | | hw02 | Recursion(递归) | https://www.bilibili.com/video/BV18Z4y1Y7Wo |
26 | | lec09 | Containers(容器) | https://www.bilibili.com/video/BV1ie4y1R77y |
27 | | lab04 | Recursion, Tree Recursion, Python Lists(递归、树形递归、Python列表) | https://www.bilibili.com/video/BV1gT411J7Tr |
28 | | lec10 | Data Abstraction(数据抽象) | https://www.bilibili.com/video/BV1da411n76i |
29 | | proj02 | Cats(猫猫打字通) | https://www.bilibili.com/video/BV1Dg411f7nj |
30 | | lec11 | Tree(树) | https://www.bilibili.com/video/BV1JB4y1Y73A |
31 | | lab05 | Data Abstraction, Trees(数据抽象,树) | https://www.bilibili.com/video/BV1Ae4y197Lh |
32 | | lec12 | Mutable Values(可修改的值) | https://www.bilibili.com/video/BV1PG4y1i7ps |
33 | | hw03 | Trees, Data Abstraction(树,数据抽象) | https://www.bilibili.com/video/BV11U4y1i7dv |
34 | | lec13 | Mutable Functions(可修性的函数) | https://www.bilibili.com/video/BV1og411Z7wS |
35 | | lab06 | Nonlocal, Mutability(非本地和修改性) | https://www.bilibili.com/video/BV1xB4y187bu |
36 | | lec14 | Iterators(迭代器) | https://www.bilibili.com/video/BV1rW4y127FT |
37 | | lec15 | Objects(对象) | https://www.bilibili.com/video/BV1cU4y1v7cw |
38 | | hw04 | Nonlocal, Iterators(非本地变量,迭代器) | https://www.bilibili.com/video/BV1se4y1Q73F |
39 | | lec16 | Inheritance(继承) | https://www.bilibili.com/video/BV1ZB4y1t7JC |
40 | | lab07 | Iterators, Generators, OOP(迭代器,生成子和面向对象) | https://www.bilibili.com/video/BV18W4y1y7TB |
41 | | proj03 | Ants(蚂蚁塔防) | https://www.bilibili.com/video/BV11g41117zh |
42 | | lec17 | Representation(表示) | https://www.bilibili.com/video/BV1Dt4y137UN |
43 | | lec18 | Composition(组合) | https://www.bilibili.com/video/BV1QF411A7py |
44 | | hw05 | OOP, Linked Lists, Trees(面向对象,链表,树) | https://www.bilibili.com/video/BV1mt4y137pP |
45 | | lec19 | Efficiency(效率) | https://www.bilibili.com/video/BV1Vg411y7WQ |
46 | | lab08 | Linked Lists, Trees(链表,树) | https://www.bilibili.com/video/BV1Ag411k7vn |
47 | | lec20 | Decomposition(解构) | https://www.bilibili.com/video/BV1zv4y1F7vR |
48 | | lab09 | Midterm Review(阶段性复习) | https://www.bilibili.com/video/BV1nS4y1s7pr |
49 | | lec21 | Scheme(Scheme语言基础) | https://www.bilibili.com/video/BV1cd4y1P7Th |
50 | | lab10 | Scheme(Scheme语言基础) | https://www.bilibili.com/video/BV1Fd4y1o7Vt |
51 | | hw06 | Scheme(Scheme语言基础) | https://www.bilibili.com/video/BV1Aa411Z7Ed |
52 | | lec22 | Exceptions(异常) | https://www.bilibili.com/video/BV1R14y1b7Cv |
53 | | lec23 | Calculator(计算器) | https://www.bilibili.com/video/BV1FB4y1z7W3 |
54 | | hw07 | Scheme Lists(Scheme链表) | https://www.bilibili.com/video/BV1vG4y1a7Mb |
55 | | lec24 | Interpreters(解释器) | https://www.bilibili.com/video/BV14G411b7sr |
56 | | lab11 | Interpreters(解释器) | https://www.bilibili.com/video/BV1aB4y1V73q |
57 | | proj04 | Scheme(Scheme解释器) | https://www.bilibili.com/video/BV1WY4y1F74V |
58 | | lec25 | Declarative Programming(申述式编程) | https://www.bilibili.com/video/BV17P411V71J |
59 | | hw08 | More Scheme(Scheme 练习) | https://www.bilibili.com/video/BV1nD4y1B7uN |
60 | | lec26 | Table(表格) | https://www.bilibili.com/video/BV1tP411V75a |
61 | | lab12 | SQL(SQL基础) | https://www.bilibili.com/video/BV1iW4y1t7om |
62 | | lec27 | Aggregation(聚合) | https://www.bilibili.com/video/BV1nU4y1r7EG |
63 | | hw09 | SQL(SQL 基础) | https://www.bilibili.com/video/BV1914y1W7Rk |
64 | | lab13 | More SQL(SQL练习) | https://www.bilibili.com/video/BV1xe4y1d7Lb |
65 | | lab14 | Final Review(最终复习) | https://www.bilibili.com/video/BV1Sg411U7cW |
66 |
67 |
--------------------------------------------------------------------------------
/lec01/lec01-computer-science.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JacyCui/sicp-lectures/0d8e574f3886ec8807937b482e97779fb8918b8b/lec01/lec01-computer-science.pdf
--------------------------------------------------------------------------------
/lec02/lec02-functions.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JacyCui/sicp-lectures/0d8e574f3886ec8807937b482e97779fb8918b8b/lec02/lec02-functions.pdf
--------------------------------------------------------------------------------
/lec03/demo.py:
--------------------------------------------------------------------------------
1 | # source file is an ascii file
2 | print("Hello! I'm a python source file!")
3 |
4 | # multiple return values
5 |
6 | def multiple(a, b):
7 | return a, b
8 | x, y = multiple(1, 2)
9 | print(x, y)
10 |
11 |
12 | # default arguments
13 |
14 | def sum(a, b, c = 2):
15 | return a + b + c
16 |
17 |
18 | # compound statements
19 |
20 | def add(a, b):
21 | return a + b
22 |
23 |
24 | # simple statements
25 | from math import pi
26 | a = 2
27 |
28 |
29 |
30 | # prime fractorization
31 |
32 | def prime_fractorization(n):
33 | """print the prime fractorization of positive integer n
34 |
35 | Args:
36 | n (int): a positive integer
37 |
38 | >>> prime_fractorization(-1)
39 | >>> prime_fractorization(1)
40 | >>> prime_fractorization(8)
41 | 2
42 | 2
43 | 2
44 | >>> prime_fractorization(9)
45 | 3
46 | 3
47 | >>> prime_fractorization(858)
48 | 2
49 | 3
50 | 11
51 | 13
52 | """
53 | if n <= 1:
54 | return
55 | i = 2
56 | while n != 1:
57 | if n % i == 0:
58 | print(i)
59 | n = n // i
60 | i = 2
61 | else:
62 | i = i + 1
63 |
64 |
--------------------------------------------------------------------------------
/lec03/lec03-control.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JacyCui/sicp-lectures/0d8e574f3886ec8807937b482e97779fb8918b8b/lec03/lec03-control.pdf
--------------------------------------------------------------------------------
/lec04/demo.py:
--------------------------------------------------------------------------------
1 | # Iteration
2 |
3 | def fib(n):
4 | """return the nth fibonacii number
5 |
6 | Args:
7 | n (int): n >= 1
8 | 0, 1, 1, 2, 3, 5, 8, 13, 21, 34 ...
9 |
10 | >>> fib(1)
11 | 1
12 | >>> fib(3)
13 | 2
14 | >>> fib(7)
15 | 13
16 | >>> fib(9)
17 | 34
18 | """
19 | pred, curr = 0, 1
20 | while n > 1:
21 | # temp = curr
22 | # curr = pred + curr
23 | # pred = temp
24 | pred, curr = curr, pred + curr
25 | n -= 1
26 | return curr
27 |
28 |
29 |
30 |
31 | # Generalization with arguments
32 |
33 | from math import pi, sqrt
34 |
35 | ## before generalization
36 |
37 | def area_square(r):
38 | """ Return the area of a square with side length r. """
39 | return r * r * 1
40 |
41 | def area_circle(r):
42 | """ Return the area of a circle with radius r. """
43 | return r * r * pi
44 |
45 | def area_hexagon(r):
46 | """ Return the area of a regular hexagon with side length r. """
47 | return r * r * 3 * sqrt(3) / 2
48 |
49 | ## after generalization with arguments
50 |
51 | def area(r, shape_constant):
52 | """ Return the area of a shale from length measurement r. """
53 | assert r > 0, 'A length must be positive'
54 | return r * r * shape_constant
55 |
56 | def square(r):
57 | return area(r, 1)
58 |
59 | def circle(r):
60 | return area(r, pi)
61 |
62 | def hexagon(r):
63 | return area(r, 3 * sqrt(3) / 2)
64 |
65 |
66 |
67 |
68 | # Generalization over a computational process
69 |
70 | ## Before generalization
71 |
72 | def sum_naturals(n):
73 | """ Sum the first n natural numbers
74 |
75 | Args:
76 | n (int): n >= 0
77 |
78 | >>> sum_naturals(5)
79 | 15
80 | """
81 | res, i = 0, 1
82 | while i <= n:
83 | res += i
84 | i += 1
85 | return res
86 |
87 | def sum_cubes(n):
88 | """ Sum the first n cube of natural numbers
89 |
90 | Args:
91 | n (int): n >= 0
92 |
93 | >>> sum_cubes(5)
94 | 225
95 | """
96 | res, i = 0, 1
97 | while i <= n:
98 | res += i * i * i
99 | i += 1
100 | return res
101 |
102 | # After generalization
103 |
104 | from operator import mul
105 |
106 | def natural(i):
107 | return i
108 |
109 | def cube(i):
110 | return i * i * i
111 |
112 | def split_term(i):
113 | return 8 / mul(4 * i - 3, 4 * i - 1)
114 |
115 | def summation(n, term):
116 | """Sum the first n terms of a sequence
117 |
118 | Args:
119 | n (int): n >= 0
120 | term (function): a function describing how to compute each term
121 |
122 | >>> summation(5, cube)
123 | 225
124 | >>> summation(5, natural)
125 | 15
126 | >>> summation(5, split_term)
127 | 3.041839618929402
128 | """
129 | res, i = 0, 1
130 | while i <= n:
131 | res += term(i)
132 | i += 1
133 | return res
134 |
135 |
136 | # function as return values
137 |
138 | def make_adder(n):
139 | """returns a function that will add n to its argument
140 |
141 | Args:
142 | n (number): a number
143 |
144 | >>> add_three = make_adder(3)
145 | >>> add_three(4)
146 | 7
147 | >>> make_adder(4)(8)
148 | 12
149 | """
150 | def adder(k):
151 | return n + k
152 | return adder
153 |
154 |
155 | # if statement and if function
156 |
157 | def condition():
158 | print("This is condition.")
159 | return True
160 |
161 | def if_suite():
162 | print("This is if suite.")
163 |
164 | def else_suite():
165 | print("This is else suite.")
166 |
167 | def if_(condition_value, if_value, else_value):
168 | if condition_value:
169 | return if_value
170 | else:
171 | return else_value
172 |
173 | def if_statement():
174 | if condition():
175 | if_suite()
176 | else:
177 | else_suite()
178 |
179 | def if_function():
180 | if_(condition(), if_suite(), else_suite())
181 |
182 |
183 | # conditional expression
184 |
185 | from math import abs
186 |
187 | def frac_abs(x):
188 | if x == 0:
189 | return 0
190 | else:
191 | return abs(1 / x)
192 |
193 | # return 0 if x == 0 else abs(1 / x)
194 |
195 |
196 |
--------------------------------------------------------------------------------
/lec04/lec04-higher-order-functions.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JacyCui/sicp-lectures/0d8e574f3886ec8807937b482e97779fb8918b8b/lec04/lec04-higher-order-functions.pdf
--------------------------------------------------------------------------------
/lec05/lec05-environments.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JacyCui/sicp-lectures/0d8e574f3886ec8807937b482e97779fb8918b8b/lec05/lec05-environments.pdf
--------------------------------------------------------------------------------
/lec06/lec06-design.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JacyCui/sicp-lectures/0d8e574f3886ec8807937b482e97779fb8918b8b/lec06/lec06-design.pdf
--------------------------------------------------------------------------------
/lec07/demo/demo.py:
--------------------------------------------------------------------------------
1 | def sum_digits_itr(n):
2 | """ Calculate the summation of all digits in n iteratively
3 |
4 | >>> sum_digits_itr(2019)
5 | 12
6 | >>> sum_digits_itr(2022)
7 | 6
8 | """
9 | sum = 0
10 | while n > 0:
11 | sum += n % 10
12 | n //= 10
13 | # iteration invariant
14 | return sum
15 |
16 | def sum_digits_rec(n):
17 | """ Calculate the summation of all digits in n recursively
18 |
19 | >>> sum_digits_rec(2019)
20 | 12
21 | >>> sum_digits_rec(2022)
22 | 6
23 | """
24 | if n < 10:
25 | return n
26 | former, last = n // 10, n % 10
27 | return sum_digits_rec(former) + last
28 |
29 | def sum_digits_rec2(n, digit_sum):
30 | """ Calculate the summation of all digits in n recursively
31 |
32 | >>> sum_digits_rec2(2019, 0)
33 | 12
34 | >>> sum_digits_rec2(2022, 0)
35 | 6
36 | """
37 | if n == 0:
38 | return digit_sum
39 | former, last = n // 10, n % 10
40 | return sum_digits_rec2(former, digit_sum + last)
41 |
42 |
43 | def factor(n):
44 | """ Return n * (n - 1) * ... * 2 * 1, n >= 1
45 |
46 | >>> factor(3)
47 | 6
48 | >>> factor(4)
49 | 24
50 | >>> factor(5)
51 | 120
52 | >>> factor(6)
53 | 720
54 | """
55 | if n == 1:
56 | return 1
57 | return n * factor(n - 1)
58 |
59 |
60 | def is_even(n):
61 | """ Returns whether n is an even integer without using mod(%). (n >= 0)
62 |
63 | >>> is_even(0)
64 | True
65 | >>> is_even(1)
66 | False
67 | >>> is_even(128)
68 | True
69 | >>> is_even(63)
70 | False
71 | """
72 | if n == 0:
73 | return True
74 | return is_odd(n - 1)
75 |
76 |
77 | def is_odd(n):
78 | """ Returns whether n is an odd integer without using mod(%). (n >= 0)
79 |
80 | >>> is_odd(0)
81 | False
82 | >>> is_odd(1)
83 | True
84 | >>> is_odd(128)
85 | False
86 | >>> is_odd(63)
87 | True
88 | """
89 | if n == 0:
90 | return False
91 | return is_even(n - 1)
92 |
--------------------------------------------------------------------------------
/lec07/lec07-recursion.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JacyCui/sicp-lectures/0d8e574f3886ec8807937b482e97779fb8918b8b/lec07/lec07-recursion.pdf
--------------------------------------------------------------------------------
/lec08/demo/demo.py:
--------------------------------------------------------------------------------
1 | def fact(n):
2 | """ Return the factorial of N.
3 |
4 | >>> fact(0)
5 | 1
6 | >>> fact(1)
7 | 1
8 | >>> fact(3)
9 | 6
10 | >>> fact(4)
11 | 24
12 | >>> fact(5)
13 | 120
14 | """
15 | # Base Case
16 | if n == 0:
17 | return 1
18 | # Recursive Case
19 | return n * fact(n - 1)
20 |
21 | def cascade(n):
22 | """ Print a cascade prefixes of N.
23 |
24 | >>> cascade(1)
25 | 1
26 | >>> cascade(234)
27 | 234
28 | 23
29 | 2
30 | 23
31 | 234
32 | >>> cascade(123)
33 | 123
34 | 12
35 | 1
36 | 12
37 | 123
38 | >>> cascade(1234)
39 | 1234
40 | 123
41 | 12
42 | 1
43 | 12
44 | 123
45 | 1234
46 | """
47 | # Base Case
48 | if n < 10:
49 | print(n)
50 | else:
51 | print(n)
52 | # Recursive Case
53 | cascade(n // 10)
54 | print(n)
55 |
56 |
57 | def cascade2(n):
58 | """ Print a cascade prefixes of N.
59 |
60 | >>> cascade2(1)
61 | 1
62 | >>> cascade2(234)
63 | 234
64 | 23
65 | 2
66 | 23
67 | 234
68 | >>> cascade2(123)
69 | 123
70 | 12
71 | 1
72 | 12
73 | 123
74 | >>> cascade2(1234)
75 | 1234
76 | 123
77 | 12
78 | 1
79 | 12
80 | 123
81 | 1234
82 | """
83 | print(n)
84 | if n >= 10:
85 | cascade2(n // 10)
86 | print(n)
87 |
88 |
89 | # TDD: Test-Driven Development
90 |
91 | def inverse_cascade(n):
92 | """ Print an inverse cascade of prefixes of N.
93 |
94 | >>> inverse_cascade(12)
95 | 1
96 | 12
97 | 1
98 | >>> inverse_cascade(123)
99 | 1
100 | 12
101 | 123
102 | 12
103 | 1
104 | >>> inverse_cascade(1234)
105 | 1
106 | 12
107 | 123
108 | 1234
109 | 123
110 | 12
111 | 1
112 | """
113 | # print the grow order of n // 10
114 | grow(n // 10)
115 | # print n
116 | print(n)
117 | # print the shrink order of n // 10
118 | shrink(n // 10)
119 |
120 |
121 | # Functional Abstraction
122 | def grow(n):
123 | # Base Case
124 | if n == 0:
125 | return
126 | # Recursive Call
127 | grow(n // 10)
128 | print(n)
129 |
130 |
131 | def shrink(n):
132 | # Base Case
133 | if n == 0:
134 | return
135 | # Recursive Call
136 | print(n)
137 | shrink(n // 10)
138 |
139 |
140 | # Tree Recursion
141 |
142 | def fib(n):
143 | """ Return the Nth fibonacci number.
144 |
145 | >>> fib(0)
146 | 0
147 | >>> fib(1)
148 | 1
149 | >>> fib(2)
150 | 1
151 | >>> fib(3)
152 | 2
153 | >>> fib(4)
154 | 3
155 | >>> fib(5)
156 | 5
157 | >>> fib(6)
158 | 8
159 | """
160 | # Base Case
161 | if n == 0:
162 | return 0
163 | if n == 1:
164 | return 1
165 | # Recursive Call
166 | return fib(n - 2) + fib(n - 1)
167 |
168 |
169 |
170 | # Hanoi
171 |
172 | def print_move(origin, destination):
173 | """ Print instructions to move a disk. """
174 | print("Move the top disk from rod", origin, "to rod", destination)
175 |
176 |
177 | def move_stack(n, start, end):
178 | """Print the moves required to move N disks on the start pole to the end
179 | pole without violating the rules of Towers of Hanoi.
180 |
181 | Args:
182 | n (int): number of disks
183 | start (int): a pole position, either 1, 2 or 3
184 | end (int): a pole position, either 1, 2, or 3
185 |
186 | There are exactly three poles, and start and end must be different. Assume
187 | that the start pole has at least n disks of increasing size, and the end
188 | pole is either empty or has a top disk larger than the top n start disks.
189 |
190 | >>> move_stack(1, 1, 3)
191 | Move the top disk from rod 1 to rod 3
192 | >>> move_stack(2, 1, 3)
193 | Move the top disk from rod 1 to rod 2
194 | Move the top disk from rod 1 to rod 3
195 | Move the top disk from rod 2 to rod 3
196 | >>> move_stack(3, 1, 3)
197 | Move the top disk from rod 1 to rod 3
198 | Move the top disk from rod 1 to rod 2
199 | Move the top disk from rod 3 to rod 2
200 | Move the top disk from rod 1 to rod 3
201 | Move the top disk from rod 2 to rod 1
202 | Move the top disk from rod 2 to rod 3
203 | Move the top disk from rod 1 to rod 3
204 | """
205 | # Contract
206 | assert 1 <= start <= 3 and 1 <= end <= 3 and start != end, "Illegal Input!"
207 | # Base Case
208 | if n == 1:
209 | print_move(start, end)
210 | else:
211 | bridge = 6 - start - end
212 | move_stack(n - 1, start, bridge)
213 | print_move(start, end)
214 | move_stack(n - 1, bridge, end)
215 |
216 |
217 | def count_partitions(n, m):
218 | """ Count the partitions of N using parts up to(no more than) size m.
219 |
220 | >>> count_partitions(6, 4)
221 | 9
222 | >>> count_partitions(2, 4)
223 | 2
224 | >>> count_partitions(6, 3)
225 | 7
226 | """
227 | # Base Case
228 | if n == 0:
229 | return 1
230 | if n < 0:
231 | return 0
232 | if m == 0:
233 | return 0
234 | # Recursive Call
235 | with_at_least_one_m = count_partitions(n - m, m)
236 | without_m = count_partitions(n, m - 1)
237 | return with_at_least_one_m + without_m
238 |
239 |
240 |
--------------------------------------------------------------------------------
/lec08/lec08-tree-recursion.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JacyCui/sicp-lectures/0d8e574f3886ec8807937b482e97779fb8918b8b/lec08/lec08-tree-recursion.pdf
--------------------------------------------------------------------------------
/lec09/demo/demo.py:
--------------------------------------------------------------------------------
1 | def count_occurance(lst, ele):
2 | """ Count the occurance of ele in lst.
3 |
4 | >>> count_occurance([1, 2, 2, 3, 3], 2)
5 | 2
6 | >>> count_occurance([1, 1, 2, 1, 1], 1)
7 | 4
8 | >>> count_occurance([], 2)
9 | 0
10 | """
11 | count = 0
12 | for e in lst:
13 | if e == ele:
14 | count += 1
15 | return count
16 |
17 | def sum_itr(lst):
18 | """ Return the summation of elements in lst iteratively.
19 |
20 | >>> sum_itr([1, 2, 3])
21 | 6
22 | >>> sum_itr([4, 5, 6])
23 | 15
24 | """
25 | total = 0
26 | for ele in lst:
27 | total += ele
28 | return total
29 |
30 | def sum_rec(lst):
31 | """ Return the summation of elements in lst recursively.
32 |
33 | >>> sum_rec([1, 2, 3])
34 | 6
35 | >>> sum_rec([4, 5, 6])
36 | 15
37 | """
38 | if not lst:
39 | return 0
40 | return lst[0] + sum_rec(lst[1:])
41 |
42 | def reverse_string_itr(s):
43 | """ Return the reversed string of s iteratively.
44 |
45 | >>> reverse_string_itr('draw')
46 | 'ward'
47 | >>> reverse_string_itr('abaaba')
48 | 'abaaba'
49 | """
50 | res = ''
51 | for i in s:
52 | res = i + res
53 | return res
54 |
55 | def reverse_string_rec(s):
56 | """ Return the reversed string of s recursively.
57 |
58 | >>> reverse_string_rec('draw')
59 | 'ward'
60 | >>> reverse_string_rec('abaaba')
61 | 'abaaba'
62 | """
63 | if len(s) == 0:
64 | return s
65 | return reverse_string_rec(s[1:]) + s[0]
66 |
--------------------------------------------------------------------------------
/lec09/lec09-containers.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JacyCui/sicp-lectures/0d8e574f3886ec8807937b482e97779fb8918b8b/lec09/lec09-containers.pdf
--------------------------------------------------------------------------------
/lec10/demo/demo.py:
--------------------------------------------------------------------------------
1 | # Rational Arithmetic
2 |
3 | def add_rational(x, y):
4 | """ Return the sum of rational numbers X and Y,
5 | which is also a rational number.
6 |
7 | >>> x = rational(3, 2)
8 | >>> y = rational(4, 3)
9 | >>> z = add_rational(x, y)
10 | >>> numer(z)
11 | 17
12 | >>> denom(z)
13 | 6
14 | """
15 | nx, dx = numer(x), denom(x)
16 | ny, dy = numer(y), denom(y)
17 | return rational(nx * dy + ny * dx, dx * dy)
18 |
19 | def mul_rational(x, y):
20 | """ Return the product of rational numbers X and Y,
21 | Which is also a rational number.
22 |
23 | >>> x = rational(3, 2)
24 | >>> y = rational(4, 3)
25 | >>> z = mul_rational(x, y)
26 | >>> numer(z)
27 | 2
28 | >>> denom(z)
29 | 1
30 | """
31 | return rational(numer(x) * numer(y), denom(x) * denom(y))
32 |
33 | def equal_rational(x, y):
34 | """ Return whether two rational numbers X and Y are equal.
35 |
36 | >>> x = rational(3, 2)
37 | >>> y = rational(6, 4)
38 | >>> z = rational(2, 3)
39 | >>> equal_rational(x, y)
40 | True
41 | >>> equal_rational(x, z)
42 | False
43 | """
44 | return numer(x) * denom(y) == numer(y) * denom(x)
45 |
46 | def print_rational(x):
47 | """ Print the rational number X.
48 |
49 | >>> x = rational(3, 2)
50 | >>> print_rational(x)
51 | 3 / 2
52 | """
53 | print(numer(x), '/', denom(x))
54 |
55 |
56 |
57 | # Constructor and Selectors
58 |
59 | # gcd = greates common divisor
60 | from math import gcd
61 |
62 | def rational(n, d):
63 | """ Return a representation of the rational number N / D,
64 | which is a compound value.
65 | """
66 | g = gcd(n, d)
67 | return [n // g, d // g]
68 |
69 | def numer(x):
70 | """ Return the numerator of rational number X.
71 |
72 | >>> x = rational(3, 2)
73 | >>> numer(x)
74 | 3
75 | """
76 | return x[0]
77 |
78 | def denom(x):
79 | """ Return the denominator of rational number X.
80 |
81 | >>> x = rational(3, 2)
82 | >>> denom(x)
83 | 2
84 | """
85 | return x[1]
86 |
87 |
88 | # dictionary
89 |
90 | def dict_demo():
91 | # A dictionary stores a bunch of key-value pairs
92 | me = {'name': 'bearSir', 'age': 21, 'gender': 'male'}
93 | # we can access the value through key
94 | me['name']
95 | me['name'] = ['bearSir', 'BigBearSir']
96 | # we can add a new key-value pair like this
97 | me['hobby'] = 'coding'
98 | # we can delete a key-value pair like this
99 | del me['hobby']
100 |
101 |
--------------------------------------------------------------------------------
/lec10/demo/demo2.py:
--------------------------------------------------------------------------------
1 | # Another representation of pair
2 |
3 | def pair(first, second):
4 | """ Return a pair. """
5 | def pair_func(selector):
6 | if selector == 'first':
7 | return first
8 | if selector == 'second':
9 | return second
10 | return pair_func
11 |
12 | def first(p):
13 | """ Rerurn the first element of a pair.
14 |
15 | >>> p = pair(2, 3)
16 | >>> first(p)
17 | 2
18 | """
19 | return p('first')
20 |
21 |
22 | def second(p):
23 | """ Return the second element of a pair.
24 |
25 | >>> p = pair(2, 3)
26 | >>> second(p)
27 | 3
28 | """
29 | return p('second')
30 |
31 |
32 | # Rational Arithmetic
33 |
34 | def add_rational(x, y):
35 | """ Return the sum of rational numbers X and Y,
36 | which is also a rational number.
37 |
38 | >>> x = rational(3, 2)
39 | >>> y = rational(4, 3)
40 | >>> z = add_rational(x, y)
41 | >>> numer(z)
42 | 17
43 | >>> denom(z)
44 | 6
45 | """
46 | nx, dx = numer(x), denom(x)
47 | ny, dy = numer(y), denom(y)
48 | return rational(nx * dy + ny * dx, dx * dy)
49 |
50 | def mul_rational(x, y):
51 | """ Return the product of rational numbers X and Y,
52 | Which is also a rational number.
53 |
54 | >>> x = rational(3, 2)
55 | >>> y = rational(4, 3)
56 | >>> z = mul_rational(x, y)
57 | >>> numer(z)
58 | 2
59 | >>> denom(z)
60 | 1
61 | """
62 | return rational(numer(x) * numer(y), denom(x) * denom(y))
63 |
64 | def equal_rational(x, y):
65 | """ Return whether two rational numbers X and Y are equal.
66 |
67 | >>> x = rational(3, 2)
68 | >>> y = rational(6, 4)
69 | >>> z = rational(2, 3)
70 | >>> equal_rational(x, y)
71 | True
72 | >>> equal_rational(x, z)
73 | False
74 | """
75 | return numer(x) * denom(y) == numer(y) * denom(x)
76 |
77 | def print_rational(x):
78 | """ Print the rational number X.
79 |
80 | >>> x = rational(3, 2)
81 | >>> print_rational(x)
82 | 3 / 2
83 | """
84 | print(numer(x), '/', denom(x))
85 |
86 |
87 |
88 | # Constructor and Selectors
89 |
90 | # gcd = greates common divisor
91 | from math import gcd
92 |
93 | def rational(n, d):
94 | """ Return a representation of the rational number N / D,
95 | which is a compound value.
96 | """
97 | g = gcd(n, d)
98 | n, d = n // g, d // g
99 | def rational_func(selector):
100 | if selector == 'n':
101 | return n
102 | if selector == 'd':
103 | return d
104 | return rational_func
105 |
106 | def numer(x):
107 | """ Return the numerator of rational number X.
108 |
109 | >>> x = rational(3, 2)
110 | >>> numer(x)
111 | 3
112 | """
113 | return x('n')
114 |
115 | def denom(x):
116 | """ Return the denominator of rational number X.
117 |
118 | >>> x = rational(3, 2)
119 | >>> denom(x)
120 | 2
121 | """
122 | return x('d')
123 |
--------------------------------------------------------------------------------
/lec10/lec10-data-abstraction.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JacyCui/sicp-lectures/0d8e574f3886ec8807937b482e97779fb8918b8b/lec10/lec10-data-abstraction.pdf
--------------------------------------------------------------------------------
/lec11/demo/demo.py:
--------------------------------------------------------------------------------
1 | def count_leaves(t):
2 | """ Count the leaves in a tree T.
3 |
4 | >>> t = tree(3, [tree(1), tree(2, [tree(1), tree(1)])])
5 | >>> count_leaves(t)
6 | 3
7 | """
8 | if is_leaf(t):
9 | return 1
10 | return sum([count_leaves(b) for b in branches(t)])
11 |
12 | def leaves(t):
13 | """ Return a list of all leaves in tree T.
14 |
15 | >>> t = tree(3, [tree(1), tree(2, [tree(1), tree(1)])])
16 | >>> leaves(t)
17 | [1, 1, 1]
18 | """
19 | if is_leaf(t):
20 | return [label(t)]
21 | return sum([leaves(b) for b in branches(t)], [])
22 |
23 | def copy_tree(t):
24 | """ Return another tree which is the same as T but is not T.
25 |
26 | >>> t1 = tree(3, [tree(1), tree(2, [tree(1), tree(1)])])
27 | >>> t2 = copy_tree(t1)
28 | >>> t2 == t1
29 | True
30 | >>> t2 is t1
31 | False
32 | """
33 | # Base case is optional
34 | if is_leaf(t):
35 | return tree(label(t))
36 | return tree(label(t), [copy_tree(b) for b in branches(t)])
37 |
38 | def print_tree(t, indent=0):
39 | """ Print a tree.
40 |
41 | >>> print_tree(tree(1))
42 | 1
43 | >>> print_tree(tree(1, [tree(2)]))
44 | 1
45 | 2
46 | >>> print_tree(tree(3, [tree(1), tree(2, [tree(1), tree(1)])]))
47 | 3
48 | 1
49 | 2
50 | 1
51 | 1
52 | >>> print_tree(tree(3, [tree(1, [tree(4), tree(5)]), tree(2, [tree(1), tree(1)])]))
53 | 3
54 | 1
55 | 4
56 | 5
57 | 2
58 | 1
59 | 1
60 | """
61 | # less indent
62 | print(' ' * indent + str(label(t)))
63 | for b in branches(t):
64 | # more indent
65 | print_tree(b, indent + 1)
66 |
67 |
68 | def sum_paths(t):
69 | """ Return a list of all paths in T.
70 |
71 | >>> t1 = tree(3, [tree(1), tree(2, [tree(1), tree(1)])])
72 | >>> sum_paths(t1)
73 | [[3, 1], [3, 2, 1], [3, 2, 1]]
74 | >>> t2 = tree(3, [tree(1, [tree(4), tree(5)]), tree(2, [tree(1), tree(1)])])
75 | >>> sum_paths(t2)
76 | [[3, 1, 4], [3, 1, 5], [3, 2, 1], [3, 2, 1]]
77 | """
78 | if is_leaf(t):
79 | return [[label(t)]]
80 | res = []
81 | for b in branches(t):
82 | paths = sum_paths(b)
83 | for p in paths:
84 | res.append([label(t)] + p)
85 | return res
86 |
87 |
88 | # Implementation of Tree Abstraction
89 |
90 | def tree(label, branches=[]):
91 | """ Constructor of tree.
92 |
93 | >>> t = tree(3, [tree(1), tree(2, [tree(1), tree(1)])])
94 | >>> t
95 | [3, [1], [2, [1], [1]]]
96 | >>> label(t)
97 | 3
98 | >>> branches(t)
99 | [[1], [2, [1], [1]]]
100 | >>> is_leaf(t)
101 | False
102 | >>> is_tree(t)
103 | True
104 | """
105 | for b in branches:
106 | assert is_tree(b)
107 | return [label] + list(branches)
108 |
109 | def is_tree(t):
110 | if type(t) != list or len(t) < 1:
111 | return False
112 | for b in branches(t):
113 | if not is_tree(b):
114 | return False
115 | return True
116 |
117 | def branches(t):
118 | """ Branch selector of tree. """
119 | return t[1:]
120 |
121 | def label(t):
122 | """ Label selector of tree. """
123 | return t[0]
124 |
125 | def is_leaf(t):
126 | return not branches(t)
127 |
128 |
--------------------------------------------------------------------------------
/lec11/lec11-trees.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JacyCui/sicp-lectures/0d8e574f3886ec8807937b482e97779fb8918b8b/lec11/lec11-trees.pdf
--------------------------------------------------------------------------------
/lec12/demo/dog.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 |
5 |
6 | typedef struct {
7 | int size;
8 | char color[20];
9 | } Dog;
10 |
11 | void bark(Dog* this) {
12 | if (this->size < 20) {
13 | printf("%s Dog: Wuwuwu!\n", this->color);
14 | } else {
15 | printf("%s Dog: Wooooooooof!\n", this->color);
16 | }
17 | }
18 |
19 | Dog* makeDog(int size, const char* color) {
20 | Dog* this = (Dog*)malloc(sizeof(Dog));
21 | this->size = size;
22 | strcpy(this->color, color);
23 | return this;
24 | }
25 |
26 | void delete(Dog* this) {
27 | free(this);
28 | }
29 |
30 | int main() {
31 | Dog* smallDog = makeDog(5, "Dark");
32 | Dog* bigDog = makeDog(20, "White");
33 | bark(smallDog);
34 | bark(bigDog);
35 | delete(smallDog);
36 | delete(bigDog);
37 | return 0;
38 | }
39 |
40 |
--------------------------------------------------------------------------------
/lec12/lec12-mutable-values.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JacyCui/sicp-lectures/0d8e574f3886ec8807937b482e97779fb8918b8b/lec12/lec12-mutable-values.pdf
--------------------------------------------------------------------------------
/lec13/demo/demo.py:
--------------------------------------------------------------------------------
1 | def make_withdraw1(balance):
2 | """ Return a Withdarw function that withdraws money
3 | from an account with initial balance of BALANCE.
4 |
5 | >>> withdraw = make_withdraw1(100)
6 | >>> withdraw(25)
7 | 75
8 | >>> withdraw(25)
9 | 50
10 | >>> withdraw(60)
11 | 'Insufficient funds!'
12 | >>> withdraw(15)
13 | 35
14 | """
15 | def withdraw(amount):
16 | nonlocal balance
17 | if amount > balance:
18 | return 'Insufficient funds!'
19 | balance -= amount
20 | return balance
21 | return withdraw
22 |
23 |
24 | def make_withdraw2(balance):
25 | """ Return a Withdarw function that withdraws money
26 | from an account with initial balance of BALANCE.
27 |
28 | >>> withdraw = make_withdraw2(100)
29 | >>> withdraw(25)
30 | 75
31 | >>> withdraw(25)
32 | 50
33 | >>> withdraw(60)
34 | 'Insufficient funds!'
35 | >>> withdraw(15)
36 | 35
37 | """
38 | b = [balance]
39 | def withdraw(amount):
40 | if amount > b[0]:
41 | return 'Insufficient funds!'
42 | b[0] -= amount
43 | return b[0]
44 | return withdraw
45 |
46 | def f(x):
47 | x = 4
48 | def g(y):
49 | def h(z):
50 | nonlocal x
51 | x = x + 1
52 | return x + y + z
53 | return h
54 | return g
55 |
56 |
57 |
58 |
--------------------------------------------------------------------------------
/lec13/lec13-mutable-functions.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JacyCui/sicp-lectures/0d8e574f3886ec8807937b482e97779fb8918b8b/lec13/lec13-mutable-functions.pdf
--------------------------------------------------------------------------------
/lec14/demo/demo.py:
--------------------------------------------------------------------------------
1 | class Countdown:
2 | """ Count down to zero.
3 |
4 | >>> list(Countdown(5))
5 | [5, 4, 3, 2, 1]
6 | >>> for x in Countdown(3):
7 | ... print(x)
8 | 3
9 | 2
10 | 1
11 | """
12 | def __init__(self, start):
13 | self.start = start
14 |
15 | def __iter__(self):
16 | v = self.start
17 | while v > 0:
18 | yield v
19 | v -= 1
20 |
21 |
22 | def countdown(n):
23 | """ Lazily return n ~ 1.
24 |
25 | >>> list(countdown(4))
26 | [4, 3, 2, 1]
27 | >>> list(countdown(8))
28 | [8, 7, 6, 5, 4, 3, 2, 1]
29 | """
30 | if n > 0:
31 | yield n
32 | yield from countdown(n - 1)
33 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/lec14/lec14-iterators.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JacyCui/sicp-lectures/0d8e574f3886ec8807937b482e97779fb8918b8b/lec14/lec14-iterators.pdf
--------------------------------------------------------------------------------
/lec15/demo/demo.py:
--------------------------------------------------------------------------------
1 | class Clown:
2 | """
3 |
4 | >>> Clown.nose
5 | 'big and red'
6 | >>> Clown.dance()
7 | 'No thanks'
8 | """
9 | nose = 'big and red'
10 | def dance():
11 | return 'No thanks'
12 |
13 |
14 | class Account:
15 | """ An account has a balance and a holder.
16 | All accounts share common behaviours.
17 |
18 | >>> a = Account('John')
19 | >>> a.holder
20 | 'John'
21 | >>> a.balance
22 | 0
23 | >>> a.deposit(100)
24 | 100
25 | >>> a.balance
26 | 100
27 | >>> a.withdraw(10)
28 | 90
29 | >>> a.withdraw(100)
30 | 'Insufficient funds'
31 | """
32 |
33 | interest = 0.02 # A Class Attribute
34 |
35 | def __init__(self, account_holder):
36 | """ Constructor of Account class. """
37 | self.holder = account_holder # An object/instance Attribute
38 | self.balance = 0
39 |
40 | def deposit(self, amount):
41 | self.balance += amount
42 | return self.balance
43 |
44 | def withdraw(self, amount):
45 | if amount > self.balance:
46 | return 'Insufficient funds'
47 | self.balance -= amount
48 | return self.balance
49 |
50 |
51 |
52 |
53 |
--------------------------------------------------------------------------------
/lec15/lec15-objects.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JacyCui/sicp-lectures/0d8e574f3886ec8807937b482e97779fb8918b8b/lec15/lec15-objects.pdf
--------------------------------------------------------------------------------
/lec16/demo/demo.py:
--------------------------------------------------------------------------------
1 | class Account:
2 | """ An Account has a balance and a holder.
3 |
4 | >>> a = Account('John') # an instance of class Account
5 | >>> a.holder # attribute lookup
6 | 'John'
7 | >>> a.balance
8 | 0
9 | >>> a.deposit(100) # bound method invoking
10 | 100
11 | >>> a.withdraw(90)
12 | 10
13 | >>> a.withdraw(90)
14 | 'Insufficient funds.'
15 | >>> a.balance
16 | 10
17 | >>> a.interest # instance attribute -> class attribute
18 | 0.02
19 | """
20 | interest = 0.02 # A class attribute
21 |
22 | def __init__(self, account_holder):
23 | """ Constructor for class Account. """
24 | # attribute assignment of an instance will create a new attribute
25 | self.holder = account_holder # an instance attribute
26 | self.balance = 0
27 |
28 | def deposit(self, amount):
29 | """ Add amount to balance. """
30 | self.balance += amount
31 | return self.balance
32 |
33 | def withdraw(self, amount):
34 | """ Subtract amount from balance if funds are avaliable. """
35 | if amount > self.balance:
36 | return 'Insufficient funds.'
37 | self.balance -= amount
38 | return self.balance
39 |
40 |
41 | class CheckingAccount(Account):
42 | """ A bank account that charges for deposits.
43 | >>> ch = CheckingAccount('Tom')
44 | >>> ch.interest
45 | 0.01
46 | >>> ch.deposit(20)
47 | 20
48 | >>> ch.withdraw(5)
49 | 14
50 | """
51 | interest = 0.01
52 | withdraw_fee = 1
53 |
54 | def withdraw(self, amount):
55 | return Account.withdraw(self, amount + self.withdraw_fee)
56 | # return super().withdraw(amount + self.withdraw_fee)
57 |
58 |
59 |
60 |
--------------------------------------------------------------------------------
/lec16/lec16-inheritance.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JacyCui/sicp-lectures/0d8e574f3886ec8807937b482e97779fb8918b8b/lec16/lec16-inheritance.pdf
--------------------------------------------------------------------------------
/lec17/demo/demo.py:
--------------------------------------------------------------------------------
1 | def _repr(x):
2 | """ Return the repr string of object x
3 |
4 | >>> from fractions import Fraction
5 | >>> half = Fraction(1, 2)
6 | >>> _repr(half) == repr(half)
7 | True
8 | """
9 | s = type(x).__repr__(x)
10 | if not isinstance(s, str):
11 | raise TypeError
12 | return s
13 |
14 |
15 |
16 | def _str(x):
17 | """ Return the str string of object x
18 |
19 | >>> from fractions import Fraction
20 | >>> half = Fraction(1, 2)
21 | >>> _str(half) == str(half)
22 | True
23 | """
24 | s = type(x).__str__(x)
25 | if not isinstance(s, str):
26 | raise TypeError
27 | return s
28 |
29 |
30 | class Bear:
31 | """ A Bear.
32 |
33 | >>> bearSir = Bear()
34 | >>> bearSir
35 | Bear()
36 | >>> print(bearSir)
37 | a bear
38 | >>> repr(bearSir)
39 | 'Bear()'
40 | >>> str(bearSir)
41 | 'a bear'
42 | >>> bearSir.__repr__()
43 | 'instance bear repr'
44 | >>> bearSir.__str__()
45 | 'instance bear str'
46 | >>> bool(bearSir)
47 | False
48 | >>> float(bearSir)
49 | 1.1
50 | """
51 |
52 | def __init__(self):
53 | self.__repr__ = lambda: 'instance bear repr' # instance attribute
54 | self.__str__ = lambda: 'instance bear str'
55 |
56 | def __repr__(self): # class attribute
57 | return 'Bear()'
58 |
59 | def __str__(self):
60 | return 'a bear'
61 |
62 | def __bool__(self):
63 | return False
64 |
65 | def __float__(self):
66 | return 1.1
67 |
68 |
69 | # Ratio numbers
70 |
71 | from math import gcd
72 |
73 | class Ratio:
74 | """ A mutable ratio.
75 |
76 | >>> f = Ratio(9, 15)
77 | >>> f
78 | Ratio(9, 15)
79 | >>> print(f)
80 | 9/15
81 |
82 | >>> Ratio(1, 3) + Ratio(1, 6)
83 | Ratio(1, 2)
84 | >>> Ratio(1, 3) + 1
85 | Ratio(4, 3)
86 | >>> 1 + f
87 | Ratio(8, 5)
88 | >>> 1.4 + f
89 | 2.0
90 | """
91 | def __init__(self, n, d):
92 | self.numer = n
93 | self.denom = d
94 |
95 | def __repr__(self):
96 | return 'Ratio({0}, {1})'.format(self.numer, self.denom)
97 |
98 | def __str__(self):
99 | return '{0}/{1}'.format(self.numer, self.denom)
100 |
101 | def __float__(self):
102 | return self.numer / self.denom
103 |
104 | def __add__(self, other):
105 | if isinstance(other, Ratio):
106 | n = self.numer * other.denom + other.numer * self.denom
107 | d = self.denom * other.denom
108 | elif isinstance(other, int):
109 | n = self.numer + self.denom * other
110 | d = self.denom
111 | else:
112 | return float(self) + other
113 | g = gcd(n, d)
114 | return Ratio(n // g, d // g)
115 |
116 | __radd__ = __add__
117 |
118 |
119 |
120 |
121 |
122 |
123 |
--------------------------------------------------------------------------------
/lec17/lec17-representation.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JacyCui/sicp-lectures/0d8e574f3886ec8807937b482e97779fb8918b8b/lec17/lec17-representation.pdf
--------------------------------------------------------------------------------
/lec18/demo/demo.py:
--------------------------------------------------------------------------------
1 | class Link:
2 | """ A Linked List.
3 |
4 | >>> s = Link(3, Link(4, Link(5)))
5 | >>> s
6 | Link(3, Link(4, Link(5)))
7 | >>> print(s)
8 | <3, 4, 5>
9 | >>> s.first
10 | 3
11 | >>> s.rest
12 | Link(4, Link(5))
13 | >>> s.rest.first
14 | 4
15 | >>> s.rest.first = 7
16 | >>> s
17 | Link(3, Link(7, Link(5)))
18 | >>> print(s)
19 | <3, 7, 5>
20 | >>> s.rest.rest = Link.empty
21 | >>> s
22 | Link(3, Link(7))
23 | >>> s.first = 6
24 | >>> print(s)
25 | <6, 7>
26 | >>> t = Link(1, Link(Link(2, Link(3)), Link(4)))
27 | >>> t
28 | Link(1, Link(Link(2, Link(3)), Link(4)))
29 | >>> print(t)
30 | <1, <2, 3>, 4>
31 | """
32 | empty = ()
33 |
34 | def __init__(self, first, rest=empty):
35 | assert rest is Link.empty or isinstance(rest, Link)
36 | self.first = first
37 | self.rest = rest
38 |
39 | def __repr__(self):
40 | if self.rest:
41 | rest_repr = ', ' + repr(self.rest)
42 | else:
43 | rest_repr = ''
44 | return 'Link(' + repr(self.first) + rest_repr + ')'
45 |
46 | def __str__(self):
47 | string = '<'
48 | while self.rest is not Link.empty:
49 | string += str(self.first) + ', '
50 | self = self.rest
51 | return string + str(self.first) + '>'
52 |
53 |
54 | # Linked List Processing
55 |
56 | square, odd = lambda x: x * x, lambda x: x % 2 == 1
57 |
58 | def range_link(start, end):
59 | """ Return a Linked List containing consecutive integers from start to end.
60 |
61 | >>> range_link(3, 6)
62 | Link(3, Link(4, Link(5)))
63 | """
64 | if start >= end:
65 | return Link.empty
66 | return Link(start, range_link(start + 1, end))
67 |
68 | def map_link(f, s):
69 | """ Return a Linked List that contains f(x) for each x in Linked List s.
70 |
71 | >>> map_link(square, range_link(1, 5))
72 | Link(1, Link(4, Link(9, Link(16))))
73 | """
74 | if s is Link.empty:
75 | return s
76 | return Link(f(s.first), map_link(f, s.rest))
77 |
78 | def filter_link(f, s):
79 | """ Return a Linked List that contains only elements x of
80 | Link List s for which f(x) is a true value.
81 |
82 | >>> filter_link(odd, range_link(1, 6))
83 | Link(1, Link(3, Link(5)))
84 | >>> map_link(square, filter_link(odd, range_link(1, 6)))
85 | Link(1, Link(9, Link(25)))
86 | """
87 | if s is Link.empty:
88 | return s
89 | return Link(s.first, filter_link(f, s.rest)) if f(s.first) else filter_link(f, s.rest)
90 |
91 |
92 | # Linked List Mutation
93 |
94 | def add(s, v):
95 | """ Add v to s, return modified s.
96 |
97 | >>> s = Link(1, Link(3, Link(5)))
98 | >>> add(s, 0)
99 | Link(0, Link(1, Link(3, Link(5))))
100 | >>> add(s, 3)
101 | Link(0, Link(1, Link(3, Link(5))))
102 | >>> add(s, 4)
103 | Link(0, Link(1, Link(3, Link(4, Link(5)))))
104 | >>> add(s, 6)
105 | Link(0, Link(1, Link(3, Link(4, Link(5, Link(6))))))
106 | """
107 | assert s is not Link.empty
108 | if v < s.first:
109 | s.rest = Link(s.first, s.rest)
110 | s.first = v
111 | elif v > s.first:
112 | s.rest = add(s.rest, v) if s.rest is not Link.empty else Link(v)
113 | return s
114 |
115 |
116 | class Tree:
117 | """ A Tree is a label and a list of branches.
118 |
119 | >>> t = Tree(3, [Tree(1, [Tree(0), Tree(1)]), Tree(2, [Tree(1), Tree(1, [Tree(0), Tree(1)])])])
120 | >>> t
121 | Tree(3, [Tree(1, [Tree(0), Tree(1)]), Tree(2, [Tree(1), Tree(1, [Tree(0), Tree(1)])])])
122 | >>> print(t)
123 | 3
124 | 1
125 | 0
126 | 1
127 | 2
128 | 1
129 | 1
130 | 0
131 | 1
132 | """
133 | def __init__(self, label, branches=[]):
134 | self.label = label
135 | for branch in branches:
136 | assert isinstance(branch, Tree)
137 | self.branches = list(branches)
138 |
139 | def __repr__(self):
140 | if self.branches:
141 | branch_str = ', ' + repr(self.branches)
142 | else:
143 | branch_str = ''
144 | return 'Tree({0}{1})'.format(repr(self.label), branch_str)
145 |
146 | def __str__(self):
147 | return '\n'.join(self.indented())
148 |
149 | def indented(self):
150 | lines = []
151 | for b in self.branches:
152 | for line in b.indented():
153 | lines.append(' ' + line)
154 | return [str(self.label)] + lines
155 |
156 | def is_leaf(self):
157 | return not self.branches
158 |
159 |
160 | # Tree Creation
161 |
162 | def fib_tree(n):
163 | """ A Fibonacci tree.
164 |
165 | >>> print(fib_tree(4))
166 | 3
167 | 1
168 | 0
169 | 1
170 | 2
171 | 1
172 | 1
173 | 0
174 | 1
175 | """
176 | if n == 0 or n == 1:
177 | return Tree(n)
178 | left = fib_tree(n - 2)
179 | right = fib_tree(n - 1)
180 | fib_n = left.label + right.label
181 | return Tree(fib_n, [left, right])
182 |
183 |
184 | # Tree Mutation
185 |
186 | def prune(t, n):
187 | """ Prune sub-trees whose label value is n.
188 |
189 | >>> t1 = fib_tree(4)
190 | >>> prune(t1, 1)
191 | >>> print(t1)
192 | 3
193 | 2
194 | >>> t2 = fib_tree(5)
195 | >>> prune(t2, 1)
196 | >>> print(t2)
197 | 5
198 | 2
199 | 3
200 | 2
201 | """
202 | assert n != t.label
203 | t.branches = [b for b in t.branches if b.label != n]
204 | for b in t.branches:
205 | prune(b, n)
206 |
207 |
208 | # Tree Processing
209 |
210 | def leaves(tree):
211 | """ Return the list of leaf values of a tree.
212 |
213 | >>> leaves(fib_tree(4))
214 | [0, 1, 1, 0, 1]
215 | """
216 | if tree.is_leaf():
217 | return [tree.label]
218 | return sum([leaves(b) for b in tree.branches], [])
219 |
220 |
221 | def height(tree):
222 | """ The height of a tree.
223 |
224 | >>> height(fib_tree(4))
225 | 3
226 | """
227 | if tree.is_leaf():
228 | return 0
229 | return 1 + max([height(b) for b in tree.branches])
230 |
231 |
--------------------------------------------------------------------------------
/lec18/lec18-composition.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JacyCui/sicp-lectures/0d8e574f3886ec8807937b482e97779fb8918b8b/lec18/lec18-composition.pdf
--------------------------------------------------------------------------------
/lec19/demo/demo.py:
--------------------------------------------------------------------------------
1 | def fib(n):
2 | """ Return the Nth fibonacci number.
3 |
4 | >>> fib(0)
5 | 0
6 | >>> fib(1)
7 | 1
8 | >>> fib(2)
9 | 1
10 | >>> fib(3)
11 | 2
12 | >>> fib(4)
13 | 3
14 | >>> fib(5)
15 | 5
16 | """
17 | if n == 0:
18 | return 0
19 | if n == 1:
20 | return 1
21 | return fib(n - 2) + fib(n - 1)
22 |
23 | def memo(f):
24 | """ Reconstruct a single argument function into a memoized version."""
25 | cache = {}
26 | def memoized(n):
27 | if n not in cache:
28 | cache[n] = f(n)
29 | return cache[n]
30 | return memoized
31 |
32 | def make_memo_fib():
33 | """ Make a memoized fib function that works efficiently.
34 |
35 | >>> memo_fib = make_memo_fib()
36 | >>> memo_fib(0)
37 | 0
38 | >>> memo_fib(1)
39 | 1
40 | >>> memo_fib(2)
41 | 1
42 | >>> memo_fib(3)
43 | 2
44 | >>> memo_fib(4)
45 | 3
46 | >>> memo_fib(5)
47 | 5
48 | >>> memo_fib(35)
49 | 9227465
50 | """
51 | cache = {0: 0, 1: 1}
52 | def memo_fib(n):
53 | if n in cache:
54 | return cache[n]
55 | res = memo_fib(n - 2) + memo_fib(n - 1)
56 | cache[n] = res
57 | return cache[n]
58 | return memo_fib
59 |
60 |
61 |
62 |
63 |
--------------------------------------------------------------------------------
/lec19/lec19-efficiency.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JacyCui/sicp-lectures/0d8e574f3886ec8807937b482e97779fb8918b8b/lec19/lec19-efficiency.pdf
--------------------------------------------------------------------------------
/lec20/demo/demo.py:
--------------------------------------------------------------------------------
1 | def brute_overlap(s, t):
2 | """ Quartic Time Brute Overlap.
3 |
4 | >>> brute_overlap([3, 4, 6, 7, 9, 10], [1, 3, 5, 7, 8])
5 | 2
6 | """
7 | count = 0
8 | for i in s:
9 | for j in t:
10 | if i == j:
11 | count += 1
12 | return count
13 |
14 |
15 | def fast_overlap(s, t):
16 | """ Linear Time Fast Overlap for Sorted Lists.
17 |
18 | >>> fast_overlap([3, 4, 6, 7, 9, 10], [1, 3, 5, 7, 8])
19 | 2
20 | """
21 | count, i, j = 0, 0, 0
22 | while i < len(s) and j < len(t):
23 | if s[i] < t[j]:
24 | i = i + 1
25 | elif s[i] > t[j]:
26 | j = j + 1
27 | else:
28 | count, i, j = count + 1, i + 1, j + 1
29 | return count
30 |
31 |
--------------------------------------------------------------------------------
/lec20/lec20-decomposition.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JacyCui/sicp-lectures/0d8e574f3886ec8807937b482e97779fb8918b8b/lec20/lec20-decomposition.pdf
--------------------------------------------------------------------------------
/lec21/demo/demo.scm:
--------------------------------------------------------------------------------
1 | ; Definitions
2 | (define (square x) (* x x))
3 |
4 | (define (average x y) (/ (+ x y) 2))
5 |
6 | (define pi 3.14)
7 |
8 | (define (abs x)
9 | (if (< x 0) (- x) x )
10 | )
11 |
12 | (define sum-square
13 | (lambda (x y) (+ (square x) (square y)))
14 | )
15 |
16 | (define (fib n)
17 | (cond ((= n 0) 0)
18 | ((= n 1) 1)
19 | (else (+ (fib (- n 1)) (fib (- n 2))))
20 | )
21 | )
22 |
23 | ; cons = construction
24 | (define a (cons 1 (cons 2 (cons 3 (cons 4 nil)))))
25 |
26 | ; car = content of the address register
27 | ; get the first element of a linked list
28 | (define a1 (car a))
29 |
30 | ; cdr = content of the decrement register
31 | ; get the rest of a linked list
32 | (define b (cdr a))
33 |
34 | ; the first of a linked list can be a linked list
35 | (define c (cons 0 (cons (cons 1 (cons 2 (cons 3 nil))) (cons 4 (cons 5 nil)))))
36 |
37 | (define (sum-while starting-x while-condition add-to-total update-x)
38 | `(begin
39 | (define (f x total)
40 | (if ,while-condition
41 | (f ,update-x (+ total ,add-to-total))
42 | total
43 | )
44 | )
45 | (f ,starting-x 0)
46 | )
47 | )
48 | ; (eval (sum-while 2 '(< x 10) '(* x x) '(+ x 2))) => 120
49 | ; (eval (sum-while 1 '(< (* x x) 50) 'x '(+ x 1))) => 28
50 |
51 |
--------------------------------------------------------------------------------
/lec21/demo/scheme:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JacyCui/sicp-lectures/0d8e574f3886ec8807937b482e97779fb8918b8b/lec21/demo/scheme
--------------------------------------------------------------------------------
/lec21/lec21-scheme.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JacyCui/sicp-lectures/0d8e574f3886ec8807937b482e97779fb8918b8b/lec21/lec21-scheme.pdf
--------------------------------------------------------------------------------
/lec22/demo/demo.py:
--------------------------------------------------------------------------------
1 | def reduce(f, s, initial):
2 | """ Combine elements of s pairwise using combiner f and starter value initial
3 |
4 | >>> from operator import mul
5 | >>> reduce(mul, [2, 4, 8], 1)
6 | 64
7 | >>> reduce(pow, [1, 2, 3, 4], 2)
8 | 16777216
9 | """
10 | if not s:
11 | return initial
12 | return reduce(f, s[1:], f(initial, s[0]))
13 |
14 |
15 | def divide_all(n, ds):
16 | """ Divide n by every d in ds.
17 |
18 | >>> divide_all(1024, [2, 4, 8])
19 | 16.0
20 | >>> divide_all(1024, [2, 4, 0, 8])
21 | inf
22 | """
23 | from operator import truediv
24 | try:
25 | return reduce(truediv, ds, n)
26 | except ZeroDivisionError:
27 | return float('inf')
28 |
29 |
30 |
--------------------------------------------------------------------------------
/lec22/lec22-exceptions.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JacyCui/sicp-lectures/0d8e574f3886ec8807937b482e97779fb8918b8b/lec22/lec22-exceptions.pdf
--------------------------------------------------------------------------------
/lec23/demo/buffer.py:
--------------------------------------------------------------------------------
1 | """The buffer module assists in iterating through lines and tokens."""
2 |
3 | import math
4 |
5 | class Buffer(object):
6 | """A Buffer provides a way of accessing a sequence of tokens across lines.
7 |
8 | Its constructor takes an iterator, called "the source", that returns the
9 | next line of tokens as a list each time it is queried, or None to indicate
10 | the end of data.
11 |
12 | The Buffer in effect concatenates the sequences returned from its source
13 | and then supplies the items from them one at a time through its pop()
14 | method, calling the source for more sequences of items only when needed.
15 |
16 | In addition, Buffer provides a current method to look at the
17 | next item to be supplied, without sequencing past it.
18 |
19 | The __str__ method prints all tokens read so far, up to the end of the
20 | current line, and marks the current token with >>.
21 |
22 | >>> buf = Buffer(iter([['(', '+'], [15], [12, ')']]))
23 | >>> buf.pop()
24 | '('
25 | >>> buf.pop()
26 | '+'
27 | >>> buf.current()
28 | 15
29 | >>> print(buf)
30 | 1: ( +
31 | 2: >> 15
32 | >>> buf.pop()
33 | 15
34 | >>> buf.current()
35 | 12
36 | >>> buf.pop()
37 | 12
38 | >>> print(buf)
39 | 1: ( +
40 | 2: 15
41 | 3: 12 >> )
42 | >>> buf.pop()
43 | ')'
44 | >>> print(buf)
45 | 1: ( +
46 | 2: 15
47 | 3: 12 ) >>
48 | >>> buf.pop() # returns None
49 | """
50 | def __init__(self, source):
51 | self.index = 0
52 | self.lines = []
53 | self.source = source
54 | self.current_line = ()
55 | self.current()
56 |
57 | def pop(self):
58 | """Remove the next item from self and return it. If self has
59 | exhausted its source, returns None."""
60 | current = self.current()
61 | self.index += 1
62 | return current
63 |
64 | @property
65 | def more_on_line(self):
66 | return self.index < len(self.current_line)
67 |
68 | def current(self):
69 | """Return the current element, or None if none exists."""
70 | while not self.more_on_line:
71 | self.index = 0
72 | try:
73 | self.current_line = next(self.source)
74 | self.lines.append(self.current_line)
75 | except StopIteration:
76 | self.current_line = ()
77 | return None
78 | return self.current_line[self.index]
79 |
80 | def __str__(self):
81 | """Return recently read contents; current element marked with >>."""
82 | # Format string for right-justified line numbers
83 | n = len(self.lines)
84 | msg = '{0:>' + str(math.floor(math.log10(n))+1) + "}: "
85 |
86 | # Up to three previous lines and current line are included in output
87 | s = ''
88 | for i in range(max(0, n-4), n-1):
89 | s += msg.format(i+1) + ' '.join(map(str, self.lines[i])) + '\n'
90 | s += msg.format(n)
91 | s += ' '.join(map(str, self.current_line[:self.index]))
92 | s += ' >> '
93 | s += ' '.join(map(str, self.current_line[self.index:]))
94 | return s.strip()
95 |
96 | # Try to import readline for interactive history
97 | try:
98 | import readline
99 | except:
100 | pass
101 |
102 | class InputReader(object):
103 | """An InputReader is an iterable that prompts the user for input."""
104 | def __init__(self, prompt):
105 | self.prompt = prompt
106 |
107 | def __iter__(self):
108 | while True:
109 | yield input(self.prompt)
110 | self.prompt = ' ' * len(self.prompt)
111 |
--------------------------------------------------------------------------------
/lec23/demo/scalc.py:
--------------------------------------------------------------------------------
1 | """An interpreter for the Scheme-Syntax Calculator Language
2 |
3 | An interpreter for a calculator language that uses prefix-order call syntax.
4 | Operator expressions must be operator symbols. Operand expressions are
5 | separated by spaces.
6 |
7 | Examples:
8 | > (* 1 2 3)
9 | 6
10 | > (+)
11 | 0
12 | > (+ 2 (/ 4 8))
13 | 2.5
14 | > (+ 2 2) (* 3 3)
15 | 4
16 | 9
17 | > (+ 1
18 | (- 23)
19 | (* 4 2.5))
20 | -12
21 | > )
22 | SyntaxError: unexpected token: )
23 | > 2.3.4
24 | ValueError: invalid numeral: 2.3.4
25 | > +
26 | TypeError: + is not a number or call expression
27 | > (/ 5)
28 | TypeError: / requires exactly 2 arguments
29 | > (/ 1 0)
30 | ZeroDivisionError: division by zero
31 | """
32 |
33 | from ucb import trace, main, interact
34 | from operator import add, sub, mul, truediv
35 | from scheme_reader import Pair, nil, scheme_read, buffer_input
36 |
37 |
38 | # Eval & Apply
39 |
40 | def calc_eval(exp):
41 | """Evaluate a Calculator expression.
42 |
43 | >>> calc_eval(as_scheme_list('+', 2, as_scheme_list('*', 4, 6)))
44 | 26
45 | >>> calc_eval(as_scheme_list('+', 2, as_scheme_list('/', 40, 5)))
46 | 10
47 | """
48 | if type(exp) in (int, float):
49 | return simplify(exp)
50 | elif isinstance(exp, Pair):
51 | arguments = exp.second.map(calc_eval)
52 | return simplify(calc_apply(exp.first, arguments))
53 | else:
54 | raise TypeError(str(exp) + ' is not a number or call expression')
55 |
56 | def calc_apply(operator, args):
57 | """Apply the named operator to a list of args.
58 |
59 | >>> calc_apply('+', as_scheme_list(1, 2, 3))
60 | 6
61 | >>> calc_apply('-', as_scheme_list(10, 1, 2, 3))
62 | 4
63 | >>> calc_apply('-', as_scheme_list(10))
64 | -10
65 | >>> calc_apply('*', nil)
66 | 1
67 | >>> calc_apply('*', as_scheme_list(1, 2, 3, 4, 5))
68 | 120
69 | >>> calc_apply('/', as_scheme_list(40, 5))
70 | 8.0
71 | >>> calc_apply('/', as_scheme_list(10))
72 | 0.1
73 | """
74 | if not isinstance(operator, str):
75 | raise TypeError(str(operator) + ' is not a symbol')
76 | if operator == '+':
77 | return reduce(add, args, 0)
78 | elif operator == '-':
79 | if len(args) == 0:
80 | raise TypeError(operator + ' requires at least 1 argument')
81 | elif len(args) == 1:
82 | return -args.first
83 | else:
84 | return reduce(sub, args.second, args.first)
85 | elif operator == '*':
86 | return reduce(mul, args, 1)
87 | elif operator == '/':
88 | if len(args) == 0:
89 | raise TypeError(operator + ' requires at least 1 argument')
90 | elif len(args) == 1:
91 | return 1/args.first
92 | else:
93 | return reduce(truediv, args.second, args.first)
94 | else:
95 | raise TypeError(operator + ' is an unknown operator')
96 |
97 | def simplify(value):
98 | """Return an int if value is an integer, or value otherwise.
99 |
100 | >>> simplify(8.0)
101 | 8
102 | >>> simplify(2.3)
103 | 2.3
104 | >>> simplify('+')
105 | '+'
106 | """
107 | if isinstance(value, float) and int(value) == value:
108 | return int(value)
109 | return value
110 |
111 | def reduce(fn, scheme_list, start):
112 | """Reduce a recursive list of Pairs using fn and a start value.
113 |
114 | >>> reduce(add, as_scheme_list(1, 2, 3), 0)
115 | 6
116 | """
117 | if scheme_list is nil:
118 | return start
119 | return reduce(fn, scheme_list.second, fn(start, scheme_list.first))
120 |
121 | def as_scheme_list(*args):
122 | """Return a recursive list of Pairs that contains the elements of args.
123 |
124 | >>> as_scheme_list(1, 2, 3)
125 | Pair(1, Pair(2, Pair(3, nil)))
126 | """
127 | if len(args) == 0:
128 | return nil
129 | return Pair(args[0], as_scheme_list(*args[1:]))
130 |
131 | @main
132 | def read_eval_print_loop():
133 | """Run a read-eval-print loop for Calculator."""
134 | while True:
135 | try:
136 | src = buffer_input()
137 | while src.more_on_line:
138 | expression = scheme_read(src)
139 | print(calc_eval(expression))
140 | except (SyntaxError, TypeError, ValueError, ZeroDivisionError) as err:
141 | print(type(err).__name__ + ':', err)
142 | except (KeyboardInterrupt, EOFError): # -D, etc.
143 | print('Calculation completed.')
144 | return
145 |
--------------------------------------------------------------------------------
/lec23/demo/scheme_reader.py:
--------------------------------------------------------------------------------
1 | from ucb import main, trace, interact
2 | from scheme_tokens import tokenize_lines, DELIMITERS
3 | from buffer import Buffer, InputReader
4 |
5 | # Pairs and Scheme lists
6 |
7 | class Pair(object):
8 | """A pair has two instance attributes: first and second. For a Pair to be
9 | a well-formed list, second is either a well-formed list or nil. Some
10 | methods only apply to well-formed lists.
11 |
12 | >>> s = Pair(1, Pair(2, nil))
13 | >>> s
14 | Pair(1, Pair(2, nil))
15 | >>> print(s)
16 | (1 2)
17 | >>> len(s)
18 | 2
19 | >>> s[1]
20 | 2
21 | >>> print(s.map(lambda x: x+4))
22 | (5 6)
23 | """
24 | def __init__(self, first, second):
25 | self.first = first
26 | self.second = second
27 |
28 | def __repr__(self):
29 | return "Pair({0}, {1})".format(repr(self.first), repr(self.second))
30 |
31 | def __str__(self):
32 | s = "(" + str(self.first)
33 | second = self.second
34 | while isinstance(second, Pair):
35 | s += " " + str(second.first)
36 | second = second.second
37 | if second is not nil:
38 | s += " . " + str(second)
39 | return s + ")"
40 |
41 | def __len__(self):
42 | n, second = 1, self.second
43 | while isinstance(second, Pair):
44 | n += 1
45 | second = second.second
46 | if second is not nil:
47 | raise TypeError("length attempted on improper list")
48 | return n
49 |
50 | def __getitem__(self, k):
51 | if k < 0:
52 | raise IndexError("negative index into list")
53 | y = self
54 | for _ in range(k):
55 | if y.second is nil:
56 | raise IndexError("list index out of bounds")
57 | elif not isinstance(y.second, Pair):
58 | raise TypeError("ill-formed list")
59 | y = y.second
60 | return y.first
61 |
62 | def map(self, fn):
63 | """Return a Scheme list after mapping Python function FN to SELF."""
64 | mapped = fn(self.first)
65 | if self.second is nil or isinstance(self.second, Pair):
66 | return Pair(mapped, self.second.map(fn))
67 | else:
68 | raise TypeError("ill-formed list")
69 |
70 | class nil(object):
71 | """The empty list"""
72 |
73 | def __repr__(self):
74 | return "nil"
75 |
76 | def __str__(self):
77 | return "()"
78 |
79 | def __len__(self):
80 | return 0
81 |
82 | def __getitem__(self, k):
83 | if k < 0:
84 | raise IndexError("negative index into list")
85 | raise IndexError("list index out of bounds")
86 |
87 | def map(self, fn):
88 | return self
89 |
90 | nil = nil() # Assignment hides the nil class; there is only one instance
91 |
92 |
93 | # Scheme list parser, without quotation or dotted lists.
94 |
95 | def scheme_read(src):
96 | """Read the next expression from src, a Buffer of tokens.
97 |
98 | >>> lines = ['(+ 1 ', '(+ 23 4)) (']
99 | >>> src = Buffer(tokenize_lines(lines))
100 | >>> print(scheme_read(src))
101 | (+ 1 (+ 23 4))
102 | """
103 | if src.current() is None:
104 | raise EOFError
105 | val = src.pop()
106 | if val == 'nil':
107 | return nil
108 | elif val not in DELIMITERS: # ( ) ' .
109 | return val
110 | elif val == "(":
111 | return read_tail(src)
112 | else:
113 | raise SyntaxError("unexpected token: {0}".format(val))
114 |
115 | def read_tail(src):
116 | """Return the remainder of a list in src, starting before an element or ).
117 |
118 | >>> read_tail(Buffer(tokenize_lines([')'])))
119 | nil
120 | >>> read_tail(Buffer(tokenize_lines(['2 3)'])))
121 | Pair(2, Pair(3, nil))
122 | >>> read_tail(Buffer(tokenize_lines(['2 (3 4))'])))
123 | Pair(2, Pair(Pair(3, Pair(4, nil)), nil))
124 | """
125 | if src.current() is None:
126 | raise SyntaxError("unexpected end of file")
127 | if src.current() == ")":
128 | src.pop()
129 | return nil
130 | first = scheme_read(src)
131 | rest = read_tail(src)
132 | return Pair(first, rest)
133 |
134 |
135 | # Interactive loop
136 |
137 | def buffer_input():
138 | return Buffer(tokenize_lines(InputReader('> ')))
139 |
140 | @main
141 | def read_print_loop():
142 | """Run a read-print loop for Scheme expressions."""
143 | while True:
144 | try:
145 | src = buffer_input()
146 | while src.more_on_line:
147 | expression = scheme_read(src)
148 | print(repr(expression))
149 | except (SyntaxError, ValueError) as err:
150 | print(type(err).__name__ + ':', err)
151 | except (KeyboardInterrupt, EOFError): # -D, etc.
152 | return
153 |
--------------------------------------------------------------------------------
/lec23/demo/scheme_tokens.py:
--------------------------------------------------------------------------------
1 | """The scheme_tokens module provides functions tokenize_line and tokenize_lines
2 | for converting (iterators producing) strings into (iterators producing) lists
3 | of tokens. A token may be:
4 |
5 | * A number (represented as an int or float)
6 | * A boolean (represented as a bool)
7 | * A symbol (represented as a string)
8 | * A delimiter, including parentheses, dots, and single quotes
9 | """
10 |
11 | import string
12 | import sys
13 |
14 | _SYMBOL_STARTS = set('!$%&*/:<=>?@^_~') | set(string.ascii_lowercase)
15 | _SYMBOL_INNERS = _SYMBOL_STARTS | set(string.digits) | set('+-.')
16 | _NUMERAL_STARTS = set(string.digits) | set('+-.')
17 | _WHITESPACE = set(' \t\n\r')
18 | _SINGLE_CHAR_TOKENS = set("()'")
19 | _TOKEN_END = _WHITESPACE | _SINGLE_CHAR_TOKENS
20 | DELIMITERS = _SINGLE_CHAR_TOKENS | {'.'}
21 |
22 | def valid_symbol(s):
23 | """Returns whether s is not a well-formed value."""
24 | if len(s) == 0 or s[0] not in _SYMBOL_STARTS:
25 | return False
26 | for c in s[1:]:
27 | if c not in _SYMBOL_INNERS:
28 | return False
29 | return True
30 |
31 | def next_candidate_token(line, k):
32 | """A tuple (tok, k'), where tok is the next substring of line at or
33 | after position k that could be a token (assuming it passes a validity
34 | check), and k' is the position in line following that token. Returns
35 | (None, len(line)) when there are no more tokens."""
36 | while k < len(line):
37 | c = line[k]
38 | if c == ';':
39 | return None, len(line)
40 | elif c in _WHITESPACE:
41 | k += 1
42 | elif c in _SINGLE_CHAR_TOKENS:
43 | return c, k+1
44 | elif c == '#': # Boolean values #t and #f
45 | return line[k:k+2], min(k+2, len(line))
46 | else:
47 | j = k
48 | while j < len(line) and line[j] not in _TOKEN_END:
49 | j += 1
50 | return line[k:j], min(j, len(line))
51 | return None, len(line)
52 |
53 | def tokenize_line(line):
54 | """The list of Scheme tokens on line. Excludes comments and whitespace."""
55 | result = []
56 | text, i = next_candidate_token(line, 0)
57 | while text is not None:
58 | if text in DELIMITERS:
59 | result.append(text)
60 | elif text == '+' or text == '-':
61 | result.append(text)
62 | elif text == '#t' or text.lower() == 'true':
63 | result.append(True)
64 | elif text == '#f' or text.lower() == 'false':
65 | result.append(False)
66 | elif text == 'nil':
67 | result.append(text)
68 | elif text[0] in _NUMERAL_STARTS:
69 | try:
70 | result.append(int(text))
71 | except ValueError:
72 | try:
73 | result.append(float(text))
74 | except ValueError:
75 | raise ValueError("invalid numeral: {0}".format(text))
76 | elif text[0] in _SYMBOL_STARTS and valid_symbol(text):
77 | result.append(text)
78 | else:
79 | print("warning: invalid token: {0}".format(text), file=sys.stderr)
80 | print(" ", line, file=sys.stderr)
81 | print(" " * (i+3), "^", file=sys.stderr)
82 | text, i = next_candidate_token(line, i)
83 | return result
84 |
85 | def tokenize_lines(input):
86 | """An iterator that returns lists of tokens, one for each line of the
87 | iterable input sequence."""
88 | return map(tokenize_line, input)
89 |
--------------------------------------------------------------------------------
/lec23/demo/ucb.py:
--------------------------------------------------------------------------------
1 | """The ucb module contains functions specific to 61A at UC Berkeley."""
2 |
3 | import code
4 | import functools
5 | import inspect
6 | import re
7 | import signal
8 | import sys
9 |
10 |
11 | def main(fn):
12 | """Call fn with command line arguments. Used as a decorator.
13 |
14 | The main decorator marks the function that starts a program. For example,
15 |
16 | @main
17 | def my_run_function():
18 | # function body
19 |
20 | Use this instead of the typical __name__ == "__main__" predicate.
21 | """
22 | if inspect.stack()[1][0].f_locals['__name__'] == '__main__':
23 | args = sys.argv[1:] # Discard the script name from command line
24 | fn(*args) # Call the main function
25 | return fn
26 |
27 | _PREFIX = ''
28 | def trace(fn):
29 | """A decorator that prints a function's name, its arguments, and its return
30 | values each time the function is called. For example,
31 |
32 | @trace
33 | def compute_something(x, y):
34 | # function body
35 | """
36 | @functools.wraps(fn)
37 | def wrapped(*args, **kwds):
38 | global _PREFIX
39 | reprs = [repr(e) for e in args]
40 | reprs += [repr(k) + '=' + repr(v) for k, v in kwds.items()]
41 | log('{0}({1})'.format(fn.__name__, ', '.join(reprs)) + ':')
42 | _PREFIX += ' '
43 | try:
44 | result = fn(*args, **kwds)
45 | _PREFIX = _PREFIX[:-4]
46 | except Exception as e:
47 | log(fn.__name__ + ' exited via exception')
48 | _PREFIX = _PREFIX[:-4]
49 | raise
50 | # Here, print out the return value.
51 | log('{0}({1}) -> {2}'.format(fn.__name__, ', '.join(reprs), result))
52 | return result
53 | return wrapped
54 |
55 |
56 | def log(message):
57 | """Print an indented message (used with trace)."""
58 | if type(message) is not str:
59 | message = str(message)
60 | print(_PREFIX + re.sub('\n', '\n' + _PREFIX, message))
61 |
62 |
63 | def log_current_line():
64 | """Print information about the current line of code."""
65 | frame = inspect.stack()[1]
66 | log('Current line: File "{f[1]}", line {f[2]}, in {f[3]}'.format(f=frame))
67 |
68 |
69 | def interact(msg=None):
70 | """Start an interactive interpreter session in the current environment.
71 |
72 | On Unix:
73 | -D exits the interactive session and returns to normal execution.
74 | In Windows:
75 | -Z exists the interactive session and returns to normal
76 | execution.
77 | """
78 | # use exception trick to pick up the current frame
79 | try:
80 | raise None
81 | except:
82 | frame = sys.exc_info()[2].tb_frame.f_back
83 |
84 | # evaluate commands in current namespace
85 | namespace = frame.f_globals.copy()
86 | namespace.update(frame.f_locals)
87 |
88 | # exit on interrupt
89 | def handler(signum, frame):
90 | print()
91 | exit(0)
92 | signal.signal(signal.SIGINT, handler)
93 |
94 | if not msg:
95 | _, filename, line, _, _, _ = inspect.stack()[1]
96 | msg = 'Interacting at File "{0}", line {1} \n'.format(filename, line)
97 | msg += ' Unix: -D continues the program; \n'
98 | msg += ' Windows: -Z continues the program; \n'
99 | msg += ' exit() or -C exits the program'
100 |
101 | code.interact(msg, None, namespace)
102 |
--------------------------------------------------------------------------------
/lec23/lec23-calculator.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JacyCui/sicp-lectures/0d8e574f3886ec8807937b482e97779fb8918b8b/lec23/lec23-calculator.pdf
--------------------------------------------------------------------------------
/lec24/lec24-interpreters.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JacyCui/sicp-lectures/0d8e574f3886ec8807937b482e97779fb8918b8b/lec24/lec24-interpreters.pdf
--------------------------------------------------------------------------------
/lec25/demo/demo.sql:
--------------------------------------------------------------------------------
1 | .open
2 |
3 | --new
4 |
5 | -- Cities
6 | create table cities as
7 | select 38 as latitude, 122 as longitude, "Berkeley" as name union
8 | select 42, 71, "Cambridge" union
9 | select 45, 93, "Minneapolis";
10 |
11 | -- Parents
12 | create table parents as
13 | select "abraham" as parent, "barack" as child union
14 | select "abraham" , "clinton" union
15 | select "delano" , "herbert" union
16 | select "fillmore" , "abraham" union
17 | select "fillmore" , "delano" union
18 | select "fillmore" , "grover" union
19 | select "eisenhower" , "fillmore";
20 |
21 | -- Arithmetic
22 |
23 | create table lift as
24 | select 101 as chair, 2 as single, 2 as couple union
25 | select 102 , 0 , 3 union
26 | select 103 , 4 , 1;
27 |
28 | -- Ints
29 | create table ints as
30 | select "zero" as word, 0 as one, 0 as two, 0 as four, 0 as eight union
31 | select "one" , 1 , 0 , 0 , 0 union
32 | select "two" , 0 , 2 , 0 , 0 union
33 | select "three" , 1 , 2 , 0 , 0 union
34 | select "four" , 0 , 0 , 4 , 0 union
35 | select "five" , 1 , 0 , 4 , 0 union
36 | select "six" , 0 , 2 , 4 , 0 union
37 | select "seven" , 1 , 2 , 4 , 0 union
38 | select "eight" , 0 , 0 , 0 , 8 union
39 | select "nine" , 1 , 0 , 0 , 8;
40 |
41 |
--------------------------------------------------------------------------------
/lec25/demo/sqlite_shell.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | # Licensed under the MIT license
4 |
5 | # A simple SQLite shell that uses the built-in Python adapter.
6 |
7 | import codecs
8 | import io
9 | import os
10 | import sys
11 | import sqlite3
12 | import time
13 | import warnings
14 |
15 | try: FileNotFoundError
16 | except NameError: FileNotFoundError = OSError
17 |
18 | if str != bytes: buffer = bytes
19 | if str != bytes: unicode = str
20 |
21 | try: import msvcrt
22 | except ImportError: msvcrt = None
23 |
24 | CP_UTF8 = 65001
25 | pythonapi = None
26 | if msvcrt:
27 | import ctypes
28 | (BOOL, DWORD, HANDLE, UINT) = (ctypes.c_long, ctypes.c_ulong, ctypes.c_void_p, ctypes.c_uint)
29 | GetConsoleCP = ctypes.WINFUNCTYPE(UINT)(('GetConsoleCP', ctypes.windll.kernel32))
30 | SetConsoleCP = ctypes.WINFUNCTYPE(BOOL, UINT)(('SetConsoleCP', ctypes.windll.kernel32))
31 | GetConsoleOutputCP = ctypes.WINFUNCTYPE(UINT)(('GetConsoleOutputCP', ctypes.windll.kernel32))
32 | SetConsoleOutputCP = ctypes.WINFUNCTYPE(BOOL, UINT)(('SetConsoleOutputCP', ctypes.windll.kernel32))
33 | GetConsoleMode = ctypes.WINFUNCTYPE(BOOL, HANDLE, ctypes.POINTER(DWORD), use_last_error=True)(('GetConsoleMode', ctypes.windll.kernel32))
34 | GetNumberOfConsoleInputEvents = ctypes.WINFUNCTYPE(BOOL, HANDLE, ctypes.POINTER(DWORD), use_last_error=True)(('GetNumberOfConsoleInputEvents', ctypes.windll.kernel32))
35 | ReadConsoleW = ctypes.WINFUNCTYPE(BOOL, HANDLE, ctypes.c_void_p, DWORD, ctypes.POINTER(DWORD), ctypes.c_void_p, use_last_error=True)(('ReadConsoleW', ctypes.windll.kernel32))
36 | WriteConsoleW = ctypes.WINFUNCTYPE(BOOL, HANDLE, ctypes.c_void_p, DWORD, ctypes.POINTER(DWORD), ctypes.c_void_p, use_last_error=True)(('WriteConsoleW', ctypes.windll.kernel32))
37 | class Py_buffer(ctypes.Structure): _fields_ = [('buf', ctypes.c_void_p), ('obj', ctypes.py_object), ('len', ctypes.c_ssize_t), ('itemsize', ctypes.c_ssize_t), ('readonly', ctypes.c_int), ('ndim', ctypes.c_int), ('format', ctypes.c_char_p), ('shape', ctypes.POINTER(ctypes.c_ssize_t)), ('strides', ctypes.POINTER(ctypes.c_ssize_t)), ('suboffsets', ctypes.POINTER(ctypes.c_ssize_t))] + ([('smalltable', ctypes.c_ssize_t * 2)] if sys.version_info[0] <= 2 else []) + [('internal', ctypes.c_void_p)]
38 | try: from ctypes import pythonapi
39 | except ImportError: pass
40 | if pythonapi:
41 | def getbuffer(b, writable):
42 | arr = Py_buffer()
43 | pythonapi.PyObject_GetBuffer(ctypes.py_object(b), ctypes.byref(arr), ctypes.c_int(writable))
44 | try: buf = (ctypes.c_ubyte * arr.len).from_address(arr.buf)
45 | finally: pythonapi.PyBuffer_Release(ctypes.byref(arr))
46 | return buf
47 |
48 | ENCODING = 'utf-8'
49 |
50 | if sys.version_info[0] < 3:
51 | class NotASurrogateError(Exception): pass
52 | def surrogateescape_handler(exc):
53 | # Source: https://github.com/PythonCharmers/python-future/blob/aef57391c0cd58bf840dff5e2bc2c8c0f5b0a1b4/src/future/utils/surrogateescape.py
54 | mystring = exc.object[exc.start:exc.end]
55 | try:
56 | if isinstance(exc, UnicodeDecodeError):
57 | decoded = []
58 | for ch in mystring:
59 | if isinstance(ch, int):
60 | code = ch
61 | else:
62 | code = ord(ch)
63 | if 0x80 <= code <= 0xFF:
64 | decoded.append(unichr(0xDC00 + code))
65 | elif code <= 0x7F:
66 | decoded.append(unichr(code))
67 | else:
68 | raise NotASurrogateError()
69 | decoded = str().join(decoded)
70 | elif isinstance(exc, UnicodeEncodeError):
71 | decoded = []
72 | for ch in mystring:
73 | code = ord(ch)
74 | if not 0xD800 <= code <= 0xDCFF:
75 | raise NotASurrogateError()
76 | if 0xDC00 <= code <= 0xDC7F:
77 | decoded.append(unichr(code - 0xDC00))
78 | elif code <= 0xDCFF:
79 | decoded.append(unichr(code - 0xDC00))
80 | else:
81 | raise NotASurrogateError()
82 | decoded = str().join(decoded)
83 | else:
84 | raise exc
85 | except NotASurrogateError:
86 | raise exc
87 | return (decoded, exc.end)
88 | codecs.register_error('surrogateescape', surrogateescape_handler)
89 |
90 | def exception_encode(ex, codec):
91 | if str == bytes:
92 | reduced = ex.__reduce__()
93 | ex = reduced[0](*tuple(map(lambda arg: codec.decode(arg)[0] if isinstance(arg, bytes) else arg, reduced[1])))
94 | return ex
95 |
96 | def sql_commands(read_line):
97 | delims = ['"', "'", ';', '--']
98 | counter = 0
99 | in_string = None
100 | j = i = 0
101 | prev_line = None
102 | line = None
103 | concat = []
104 | while True:
105 | if line is None:
106 | while True: # process preprocessor directives
107 | counter += 1
108 | not_in_the_middle_of_any_input = not in_string and i == j and all(map(lambda chunk_: len(chunk_) == 0, concat))
109 | line = read_line(counter - 1, not_in_the_middle_of_any_input, prev_line)
110 | empty_string = line[:0] if line is not None else line
111 | prev_line = line
112 | if not line:
113 | break
114 | if not_in_the_middle_of_any_input and line.startswith("."):
115 | yield line
116 | line = None
117 | else:
118 | break
119 | if not line:
120 | break
121 | j = i = 0
122 | if j < len(line):
123 | (j, delim) = min(map(lambda pair: pair if pair[0] >= 0 else (len(line), pair[1]), map(lambda d: (line.find(d, j), d), in_string or delims if in_string != '--' else "\n")))
124 | if i < j: concat.append(line[i:j]); i = j
125 | if not in_string:
126 | if j < len(line):
127 | j += len(delim)
128 | if delim == ';':
129 | i = j
130 | concat.append(line[j : j + len(delim)]) # ensure delimeter is the same type as the string (it may not be due to implicit conversion)
131 | # Eat up any further spaces until a newline
132 | while j < len(line):
133 | delim = line[j:j+1]
134 | if not delim.isspace(): break
135 | j += 1
136 | if delim == "\n": break
137 | if i < j: concat.append(line[i:j]); i = j
138 | yield empty_string.join(concat)
139 | del concat[:]
140 | else:
141 | in_string = delim
142 | else:
143 | if j < len(line):
144 | ch = line[j:j+1]
145 | assert ch == in_string or in_string == '--'
146 | j += 1
147 | i = j
148 | concat.append(ch)
149 | in_string = None
150 | else:
151 | if i < j: concat.append(line[i:j]); i = j
152 | line = None
153 |
154 | class WindowsConsoleIOMixin(object):
155 | # Ctrl+C handling with ReadFile() is messed up on Windows starting on Windows 8... here's some background reading:
156 | # https://stackoverflow.com/a/43260436
157 | # https://github.com/microsoft/terminal/issues/334
158 | # We use ReadConsole when we can, so it doesn't affect us, but it's good info to know regardless.
159 | def __init__(self, fd):
160 | assert isatty(fd), "file descriptor must refer to a console (note that on Windows, NUL satisfies isatty(), but is not a console)"
161 | self.fd = fd
162 | self.handle = msvcrt.get_osfhandle(fd)
163 | def fileno(self): return self.fd
164 | def isatty(self): return isatty(self.fd)
165 | def seekable(self): return False
166 | def readable(self): return GetNumberOfConsoleInputEvents(self.handle, ctypes.byref(DWORD(0))) != 0
167 | def writable(self): n = DWORD(0); return WriteConsoleW(self.handle, ctypes.c_void_p(), n, ctypes.byref(n), ctypes.c_void_p()) != 0
168 | def readwcharsinto(self, buf, n):
169 | nr = DWORD(n)
170 | old_error = ctypes.get_last_error()
171 | ctypes.set_last_error(0)
172 | success = ReadConsoleW(self.handle, buf, nr, ctypes.byref(nr), ctypes.c_void_p())
173 | error = ctypes.get_last_error()
174 | ctypes.set_last_error(old_error)
175 | if not success: raise ctypes.WinError(error)
176 | ERROR_OPERATION_ABORTED = 995
177 | if nr.value == 0 and error == ERROR_OPERATION_ABORTED:
178 | # Apparently this can trigger pending KeyboardInterrupts?
179 | time.sleep(1.0 / (1 << 64))
180 | raise KeyboardInterrupt() # If Python doesn't raise it, we can
181 | return nr.value
182 | def writewchars(self, buf, n):
183 | nw = DWORD(n)
184 | if not WriteConsoleW(self.handle, buf, nw, ctypes.byref(nw), ctypes.c_void_p()):
185 | raise ctypes.WinError()
186 | return nw.value
187 |
188 | class WindowsConsoleRawIO(WindowsConsoleIOMixin, io.RawIOBase):
189 | def readinto(self, b):
190 | wordsize = ctypes.sizeof(ctypes.c_wchar)
191 | return self.readwcharsinto(getbuffer(b, True), len(b) // wordsize) * wordsize
192 | def write(self, b):
193 | wordsize = ctypes.sizeof(ctypes.c_wchar)
194 | return self.writewchars(getbuffer(b, False), len(b) // wordsize) * wordsize
195 |
196 | class WindowsConsoleTextIO(WindowsConsoleIOMixin, io.TextIOBase):
197 | buf = None
198 | buffered = unicode()
199 | translate = True
200 | def getbuf(self, ncodeunits):
201 | buf = self.buf
202 | if buf is None or len(buf) < ncodeunits:
203 | self.buf = buf = ctypes.create_unicode_buffer(ncodeunits)
204 | return buf
205 | @staticmethod # Don't let classes override this... they can override the caller instead
206 | def do_read(self, nchars, translate_newlines):
207 | prenewline = os.linesep[:-1]
208 | newline = os.linesep[-1:]
209 | empty = os.linesep[:0]
210 | if nchars is None or nchars < -1: nchars = -1
211 | ncodeunits = nchars if nchars >= 0 else io.DEFAULT_BUFFER_SIZE # Unit mismatch, but doesn't matter; we'll loop
212 | buf = None
213 | istart = 0
214 | while True:
215 | iend = self.buffered.find(newline, istart, min(istart + nchars, len(self.buffered)) if nchars >= 0 else None) if newline is not None else nchars
216 | if iend >= 0: iend += len(newline) if newline is not None else 0
217 | if 0 <= iend <= len(self.buffered):
218 | break
219 | if buf is None: buf = self.getbuf(ncodeunits)
220 | istart = len(self.buffered)
221 | chunk = buf[:self.readwcharsinto(buf, ncodeunits)]
222 | if translate_newlines: chunk = chunk.replace(prenewline, empty)
223 | if chunk.startswith('\x1A'): # EOF on Windows (Ctrl+Z) at the beginning of a line results in the entire rest of the buffer being discarded
224 | iend = istart
225 | break
226 | # Python 2 and Python 3 behaviors differ on Windows... Python 2's sys.stdin.readline() just deletes the next character if it sees EOF in the middle of a string! I won't emulate that here.
227 | self.buffered += chunk # We're relying on Python's concatenation optimization here... we don't do it ourselves, since we want self.buffered to be valid every iteration in case there is an exception raised
228 | result = self.buffered[:iend]
229 | self.buffered = self.buffered[iend:]
230 | return result
231 | def read(self, nchars=-1): return WindowsConsoleTextIO.do_read(self, nchars, None, self.translate)
232 | def readline(self, nchars=-1): return WindowsConsoleTextIO.do_read(self, nchars, self.translate)
233 | def write(self, text): buf = ctypes.create_unicode_buffer(text); return self.writewchars(buf, max(len(buf) - 1, 0))
234 |
235 | def wrap_windows_console_io(stream, is_output):
236 | fd = None
237 | if stream is not None and sys.version_info[0] < 3 and msvcrt and (is_output or pythonapi) and isatty(stream):
238 | try: fd = stream.fileno()
239 | except io.UnsupportedOperation: pass
240 | result = stream
241 | if fd is not None:
242 | f = GetConsoleOutputCP if is_output else GetConsoleCP
243 | if not f or f() != CP_UTF8:
244 | try:
245 | if True or is_output:
246 | result = WindowsConsoleTextIO(fd)
247 | else:
248 | result = io.TextIOWrapper((io.BufferedWriter if is_output else io.BufferedReader)(WindowsConsoleRawIO(fd)), 'utf-16-le', 'strict', line_buffering=True)
249 | except IOError: pass
250 | return result
251 |
252 | class NonOwningTextIOWrapper(io.TextIOWrapper):
253 | def __init__(self, base_textiowrapper, **kwargs):
254 | assert isinstance(base_textiowrapper, io.TextIOWrapper)
255 | self.base = base_textiowrapper # must keep a reference to this alive so it doesn't get closed
256 | super(NonOwningTextIOWrapper, self).__init__(base_textiowrapper.buffer, **kwargs)
257 | def close(self):
258 | super(NonOwningTextIOWrapper, self).flush()
259 |
260 | def wrap_unicode_stdio(stream, is_writer, encoding): # The underlying stream must NOT be used directly until the stream returned by this function is disposed of
261 | if isinstance(stream, io.TextIOWrapper):
262 | stream.flush() # Make sure nothing is left in the buffer before we re-wrap it
263 | none = object()
264 | kwargs = {}
265 | for key in ['encoding', 'errors', 'newline', 'line_buffering', 'write_through']:
266 | value = getattr(stream, 'newlines' if key == 'newline' else key, none)
267 | if value is not none:
268 | kwargs[key] = value
269 | kwargs['encoding'] = encoding
270 | result = NonOwningTextIOWrapper(stream, **kwargs)
271 | elif 'PYTHONIOENCODING' not in os.environ and str == bytes and stream in (sys.stdin, sys.stdout, sys.stderr):
272 | result = (codecs.getwriter if is_writer else codecs.getreader)(encoding)(stream)
273 | else:
274 | result = stream
275 | return result
276 |
277 | class StringEscapeParser(object):
278 | def __init__(self):
279 | import re
280 | self.pattern = re.compile("\"((?:[^\"\\n]+|\\\\.)*)(?:\"|$)|\'([^\'\\n]*)(?:\'|$)|(\\S+)")
281 | self.escape_pattern = re.compile("\\\\(.)", re.DOTALL)
282 | @staticmethod
283 | def escape_replacement(m):
284 | text = m.group(1)
285 | if text == "\\": text = "\\"
286 | elif text == "/": text = "\n"
287 | elif text == "n": text = "\n"
288 | elif text == "r": text = "\r"
289 | elif text == "t": text = "\t"
290 | elif text == "v": text = "\v"
291 | elif text == "f": text = "\f"
292 | elif text == "a": text = "\a"
293 | elif text == "b": text = "\b"
294 | return text
295 | def __call__(self, s):
296 | escape_pattern = self.escape_pattern
297 | escape_replacement = self.escape_replacement
298 | result = []
299 | for match in self.pattern.finditer(s):
300 | [m1, m2, m3] = match.groups()
301 | if m1 is not None: result.append(escape_pattern.sub(escape_replacement, m1))
302 | if m2 is not None: result.append(m2)
303 | if m3 is not None: result.append(escape_pattern.sub(escape_replacement, m3))
304 | return result
305 |
306 | class Database(object):
307 | def __init__(self, name, *args, **kwargs):
308 | self.connection = sqlite3.connect(name, *args, **kwargs)
309 | self.cursor = self.connection.cursor()
310 | self.name = name # assign name only AFTER cursor is created
311 |
312 | def isatty(file_or_fd):
313 | result = True
314 | method = getattr(file_or_fd, 'isatty', None) if not isinstance(file_or_fd, int) else None # this check is just an optimization
315 | if method is not None:
316 | try: tty = method()
317 | except io.UnsupportedOperation: tty = None
318 | result = result and tty is not None and tty
319 | method = getattr(file_or_fd, 'fileno', None) if not isinstance(file_or_fd, int) else None # this check is just an optimization
320 | if method is not None:
321 | try: fd = method()
322 | except io.UnsupportedOperation: fd = None
323 | result = result and fd is not None and os.isatty(fd) and (not msvcrt or GetConsoleMode(msvcrt.get_osfhandle(fd), ctypes.byref(DWORD(0))) != 0)
324 | return result
325 |
326 | def can_call_input_for_stdio(stream):
327 | return stream == sys.stdin and sys.version_info[0] >= 3
328 |
329 | class StdIOProxy(object):
330 | # Maybe useful later: codecs.StreamRecoder(bytesIO, codec.decode, codec.encode, codec.streamwriter, codec.streamreader, errors='surrogateescape')
331 | def __init__(self, stdin, stdout, stderr, codec, allow_set_code_page):
332 | self.codec = codec
333 | streams = (stdin, stdout, stderr)
334 | for stream in streams:
335 | assert isinstance(stream, io.IOBase) or sys.version_info[0] < 3 and isinstance(stream, file) or hasattr(stream, 'mode'), "unable to determine stream type"
336 | assert not isinstance(stream, io.RawIOBase), "RAW I/O APIs are different and not supported"
337 | self.streaminfos = tuple(map(lambda stream:
338 | (
339 | stream,
340 | isinstance(stream, io.BufferedIOBase) or isinstance(stream, io.RawIOBase) or not isinstance(stream, io.TextIOBase) and 'b' in stream.mode,
341 | isinstance(stream, io.TextIOBase) or not (isinstance(stream, io.BufferedIOBase) or isinstance(stream, io.RawIOBase)) and 'b' not in stream.mode,
342 | allow_set_code_page
343 | ),
344 | streams))
345 | @property
346 | def stdin(self): return self.streaminfos[0][0]
347 | @property
348 | def stdout(self): return self.streaminfos[1][0]
349 | @property
350 | def stderr(self): return self.streaminfos[2][0]
351 | def _coerce(self, streaminfo, codec, arg):
352 | stream = streaminfo[0]
353 | can_binary = streaminfo[1]
354 | can_text = streaminfo[2]
355 | if not isinstance(arg, bytes) and not isinstance(arg, buffer) and not isinstance(arg, unicode):
356 | arg = unicode(arg)
357 | if isinstance(arg, bytes) or isinstance(arg, buffer):
358 | if not can_binary:
359 | arg = codec.decode(arg, 'surrogateescape')[0]
360 | elif isinstance(arg, unicode):
361 | if not can_text:
362 | arg = codec.encode(unicode(arg), 'strict')[0]
363 | return arg
364 | @staticmethod
365 | def _do_readline(stream, allow_set_code_page, *args):
366 | new_code_page = CP_UTF8
367 | old_code_page = GetConsoleCP() if msvcrt and GetConsoleCP and isatty(stream) else None
368 | if old_code_page == new_code_page: old_code_page = None # Don't change code page if it's already correct...
369 | if old_code_page is not None:
370 | if not SetConsoleCP(new_code_page):
371 | old_code_page = None
372 | try:
373 | result = stream.readline(*args)
374 | finally:
375 | if old_code_page is not None:
376 | SetConsoleCP(old_code_page)
377 | return result
378 | @staticmethod
379 | def _do_write(stream, allow_set_code_page, *args):
380 | new_code_page = CP_UTF8
381 | old_code_page = GetConsoleOutputCP() if msvcrt and GetConsoleOutputCP and isatty(stream) else None
382 | if old_code_page == new_code_page: old_code_page = None # Don't change code page if it's already correct...
383 | if old_code_page is not None:
384 | if not SetConsoleOutputCP(new_code_page):
385 | old_code_page = None
386 | try:
387 | result = stream.write(*args)
388 | finally:
389 | if old_code_page is not None:
390 | SetConsoleCP(old_code_page)
391 | return result
392 | def _readln(self, streaminfo, codec, prompt):
393 | stream = streaminfo[0]
394 | can_binary = streaminfo[1]
395 | allow_set_code_page = streaminfo[3]
396 | if can_call_input_for_stdio(stream) and not can_binary: # input() can't work with binary data
397 | result = self._coerce(streaminfo, codec, "")
398 | try:
399 | result = input(*((self._coerce(streaminfo, codec, prompt),) if prompt is not None else ()))
400 | result += self._coerce(streaminfo, codec, "\n")
401 | except EOFError: pass
402 | else:
403 | self.output(*((prompt,) if prompt is not None else ()))
404 | self.error()
405 | result = StdIOProxy._do_readline(stream, allow_set_code_page)
406 | return result
407 | def _writeln(self, streaminfo, codec, *args, **kwargs):
408 | stream = streaminfo[0]
409 | allow_set_code_page = streaminfo[3]
410 | flush = kwargs.pop('flush', True)
411 | kwargs.setdefault('end', '\n')
412 | kwargs.setdefault('sep', ' ')
413 | end = kwargs.get('end')
414 | sep = kwargs.get('sep')
415 | first = True
416 | for arg in args:
417 | if first: first = False
418 | elif sep is not None:
419 | StdIOProxy._do_write(stream, allow_set_code_page, self._coerce(streaminfo, codec, sep))
420 | StdIOProxy._do_write(stream, allow_set_code_page, self._coerce(streaminfo, codec, arg))
421 | if end is not None:
422 | StdIOProxy._do_write(stream, allow_set_code_page, self._coerce(streaminfo, codec, end))
423 | if flush: stream.flush()
424 | def inputln(self, prompt=None): return self._readln(self.streaminfos[0], self.codec, prompt)
425 | def output(self, *args, **kwargs): kwargs.setdefault('end', None); return self._writeln(self.streaminfos[1], self.codec, *args, **kwargs)
426 | def outputln(self, *args, **kwargs): return self._writeln(self.streaminfos[1], self.codec, *args, **kwargs)
427 | def error(self, *args, **kwargs): kwargs.setdefault('end', None); return self._writeln(self.streaminfos[2], self.codec, *args, **kwargs)
428 | def errorln(self, *args, **kwargs): return self._writeln(self.streaminfos[2], self.codec, *args, **kwargs)
429 |
430 | class bytes_comparable_with_unicode(bytes): # For Python 2/3 compatibility, to allow implicit conversion between strings and bytes when it is safe. (Used for strings like literals which we know be safe.)
431 | codec = codecs.lookup('ascii') # MUST be a safe encoding
432 | @classmethod
433 | def coerce(cls, other, for_output=False):
434 | return cls.codec.encode(other)[0] if not isinstance(other, bytes) else bytes_comparable_with_unicode(other) if for_output else other
435 | @classmethod
436 | def translate_if_bytes(cls, value):
437 | if value is not None and isinstance(value, bytes): value = cls(value)
438 | return value
439 | def __hash__(self): return super(bytes_comparable_with_unicode, self).__hash__() # To avoid warning
440 | def __eq__(self, other): return super(bytes_comparable_with_unicode, self).__eq__(self.coerce(other))
441 | def __ne__(self, other): return super(bytes_comparable_with_unicode, self).__ne__(self.coerce(other))
442 | def __lt__(self, other): return super(bytes_comparable_with_unicode, self).__lt__(self.coerce(other))
443 | def __gt__(self, other): return super(bytes_comparable_with_unicode, self).__gt__(self.coerce(other))
444 | def __le__(self, other): return super(bytes_comparable_with_unicode, self).__le__(self.coerce(other))
445 | def __ge__(self, other): return super(bytes_comparable_with_unicode, self).__ge__(self.coerce(other))
446 | def __getitem__(self, index): return self.coerce(super(bytes_comparable_with_unicode, self).__getitem__(index), True)
447 | def __add__(self, other): return self.coerce(super(bytes_comparable_with_unicode, self).__add__(self.coerce(other)), True)
448 | def __iadd__(self, other): return self.coerce(super(bytes_comparable_with_unicode, self).__iadd__(self.coerce(other)), True)
449 | def __radd__(self, other): return self.coerce(self.coerce(other).__add__(self), True)
450 | def find(self, other, *args): return super(bytes_comparable_with_unicode, self).find(self.coerce(other), *args)
451 | def join(self, others): return self.coerce(super(bytes_comparable_with_unicode, self).join(map(self.coerce, others)), True)
452 | def startswith(self, other): return super(bytes_comparable_with_unicode, self).startswith(self.coerce(other))
453 | def __str__(self): return self.codec.decode(self)[0]
454 | if str == bytes:
455 | __unicode__ = __str__
456 | def __str__(self): raise NotImplementedError()
457 |
458 | def wrap_bytes_comparable_with_unicode_readline(readline):
459 | def callback(*args):
460 | line = readline(*args)
461 | line = bytes_comparable_with_unicode.translate_if_bytes(line)
462 | return line
463 | return callback
464 |
465 | def main(program, *args, **kwargs): # **kwargs = dict(stdin=file, stdout=file, stderr=file); useful for callers who import this module
466 | import argparse # slow import (compiles regexes etc.), so don't import it until needed
467 | argparser = argparse.ArgumentParser(
468 | prog=os.path.basename(program),
469 | usage=None,
470 | description=None,
471 | epilog=None,
472 | parents=[],
473 | formatter_class=argparse.RawTextHelpFormatter)
474 | argparser.add_argument('-version', '--version', action='store_true', help="show SQLite version")
475 | argparser.add_argument('-batch', '--batch', action='store_true', help="force batch I/O")
476 | argparser.add_argument('-init', '--init', metavar="FILE", help="read/process named file")
477 | argparser.add_argument('filename', nargs='?', metavar="FILENAME", help="is the name of an SQLite database.\nA new database is created if the file does not previously exist.")
478 | argparser.add_argument('sql', nargs='*', metavar="SQL", help="SQL commnds to execute after opening database")
479 | argparser.add_argument('--readline', action='store', metavar="(true|false)", default="true", choices=("true", "false"), help="whether to import readline if available (default: %(default)s)")
480 | argparser.add_argument('--self-test', action='store_true', help="perform a basic self-test")
481 | argparser.add_argument('--cross-test', action='store_true', help="perform a basic test against the official executable")
482 | argparser.add_argument('--unicode-stdio', action='store', metavar="(true|false)", default="true", choices=("true", "false"), help="whether to enable Unicode wrapper for standard I/O (default: %(default)s)")
483 | argparser.add_argument('--console', action='store', metavar="(true|false)", default="true", choices=("true", "false"), help="whether to auto-detect and use console window APIs (default: %(default)s)")
484 | argparser.add_argument('--encoding', default=ENCODING, help="the default encoding to use (default: %(default)s)")
485 | (stdin, stdout, stderr) = (kwargs.pop('stdin', sys.stdin), kwargs.pop('stdout', sys.stdout), kwargs.pop('stderr', sys.stderr))
486 | parsed_args = argparser.parse_args(args)
487 | codec = codecs.lookup(parsed_args.encoding or argparser.get_default('encoding'))
488 | if parsed_args.self_test: self_test(codec)
489 | if parsed_args.cross_test: cross_test("sqlite3", codec)
490 | parse_escaped_strings = StringEscapeParser()
491 | if parsed_args.unicode_stdio == "true":
492 | stdin = wrap_unicode_stdio(stdin, False, codec.name)
493 | stdout = wrap_unicode_stdio(stdout, True, codec.name)
494 | stderr = wrap_unicode_stdio(stderr, True, codec.name)
495 | if parsed_args.console == "true":
496 | stdin = wrap_windows_console_io(stdin, False)
497 | stdout = wrap_windows_console_io(stdout, True)
498 | stderr = wrap_windows_console_io(stderr, True)
499 | allow_set_code_page = sys.version_info[0] < 3 and False # This is only necessary on Python 2 if we use the default I/O functions instead of bypassing to ReadConsole()/WriteConsole()
500 | stdio = StdIOProxy(stdin, stdout, stderr, codec, allow_set_code_page)
501 | db = None
502 | no_args = len(args) == 0
503 | init_sql = parsed_args.sql
504 | is_nonpipe_input = stdin.isatty() # NOT the same thing as TTY! (NUL and /dev/null are the difference)
505 | init_show_prompt = not parsed_args.batch and is_nonpipe_input
506 | if not parsed_args.batch and isatty(stdin) and (parsed_args.readline == "true" or __name__ == '__main__') and parsed_args.readline != "false":
507 | try:
508 | with warnings.catch_warnings():
509 | warnings.filterwarnings('ignore', category=DeprecationWarning)
510 | import readline
511 | except ImportError: pass
512 | if parsed_args and parsed_args.version:
513 | stdio.outputln(sqlite3.sqlite_version);
514 | else:
515 | filename = parsed_args.filename
516 | if filename is None: filename = ":memory:"
517 | db = Database(filename, isolation_level=None)
518 | def exec_script(db, filename, ignore_io_errors):
519 | try:
520 | with io.open(filename, 'r', encoding=codec.name) as f: # Assume .sql files are text -- any binary data inside them should be X'' encoded, not embedded directly
521 | for command in sql_commands(wrap_bytes_comparable_with_unicode_readline(lambda *args: (lambda s: (s) or None)(f.readline()))):
522 | result = exec_command(db, command, False and ignore_io_errors)
523 | if result is not None:
524 | return result
525 | except IOError as ex:
526 | stdio.errorln(ex)
527 | if not ignore_io_errors: return ex.errno
528 | def raise_invalid_command_error(command):
529 | if isinstance(command, bytes): command = codec.decode(command)[0]
530 | if command.startswith("."): command = command[1:]
531 | raise RuntimeError("Error: unknown command or invalid arguments: \"%s\". Enter \".help\" for help" % (command.rstrip().replace("\\", "\\\\").replace("\"", "\\\""),))
532 | def exec_command(db, command, ignore_io_errors):
533 | results = None
534 | query = None
535 | query_parameters = {}
536 | try:
537 | if command.startswith("."):
538 | args = list(parse_escaped_strings(command))
539 | if args[0] in (".quit", ".exit"):
540 | return 0
541 | elif args[0] == ".help":
542 | stdio.error("""
543 | .cd DIRECTORY Change the working directory to DIRECTORY
544 | .dump Dump the database in an SQL text format
545 | .exit Exit this program
546 | .help Show this message
547 | .open FILE Close existing database and reopen FILE
548 | .print STRING... Print literal STRING
549 | .quit Exit this program
550 | .read FILENAME Execute SQL in FILENAME
551 | .schema ?PATTERN? Show the CREATE statements matching PATTERN
552 | .show Show the current values for various settings
553 | .tables ?TABLE? List names of tables
554 | """.lstrip())
555 | elif args[0] == ".cd":
556 | if len(args) != 2: raise_invalid_command_error(command)
557 | os.chdir(args[1])
558 | elif args[0] == ".dump":
559 | if len(args) != 1: raise_invalid_command_error(command)
560 | foreign_keys = db.cursor.execute("PRAGMA foreign_keys;").fetchone()[0]
561 | if foreign_keys in (0, "0", "off", "OFF"):
562 | stdio.outputln("PRAGMA foreign_keys=OFF;", flush=False)
563 | for line in db.connection.iterdump():
564 | stdio.outputln(line, flush=False)
565 | stdio.output()
566 | elif args[0] == ".open":
567 | if len(args) <= 1: raise_invalid_command_error(command)
568 | filename = args[-1]
569 | for option in args[+1:-1]:
570 | raise ValueError("option %s not supported" % (repr(option),))
571 | try: db.__init__(filename)
572 | except sqlite3.OperationalError as ex:
573 | ex.args = ex.args[:0] + ("Error: unable to open database \"%s\": %s" % (filename, ex.args[0]),) + ex.args[1:]
574 | raise
575 | elif args[0] == ".print":
576 | stdio.outputln(*args[1:])
577 | elif args[0] == ".read":
578 | if len(args) != 2: raise_invalid_command_error(command)
579 | exec_script(db, args[1], ignore_io_errors)
580 | elif args[0] == ".schema":
581 | if len(args) > 2: raise_invalid_command_error(command)
582 | pattern = args[1] if len(args) > 1 else None
583 | query_parameters['type'] = 'table'
584 | if pattern is not None:
585 | query_parameters['pattern'] = pattern
586 | query = "SELECT sql || ';' FROM sqlite_master WHERE type = :type" + (" AND name LIKE :pattern" if pattern is not None else "") + ";"
587 | elif args[0] == ".show":
588 | if len(args) > 2: raise_invalid_command_error(command)
589 | stdio.errorln(" filename:", db.name)
590 | elif args[0] == ".tables":
591 | if len(args) > 2: raise_invalid_command_error(command)
592 | pattern = args[1] if len(args) > 1 else None
593 | query_parameters['type'] = 'table'
594 | if pattern is not None:
595 | query_parameters['pattern'] = pattern
596 | query = "SELECT name FROM sqlite_master WHERE type = :type" + (" AND name LIKE :pattern" if pattern is not None else "") + ";"
597 | else:
598 | raise_invalid_command_error(args[0])
599 | else:
600 | query = command
601 | if query is not None:
602 | results = db.cursor.execute(query if isinstance(query, unicode) else codec.decode(query, 'surrogatereplace')[0], query_parameters)
603 | except (RuntimeError, OSError, FileNotFoundError, sqlite3.OperationalError) as ex:
604 | stdio.errorln(exception_encode(ex, codec))
605 | if results is not None:
606 | for row in results:
607 | stdio.outputln(*tuple(map(lambda item: item if item is not None else "", row)), sep="|", flush=False)
608 | stdio.output()
609 | if db:
610 | if parsed_args and parsed_args.init:
611 | if is_nonpipe_input: stdio.errorln("-- Loading resources from", parsed_args.init)
612 | exec_script(db, parsed_args.init, False)
613 | def read_stdin(index, not_in_the_middle_of_any_input, prev_line):
614 | show_prompt = init_show_prompt
615 | to_write = []
616 | if index < len(init_sql):
617 | line = init_sql[index]
618 | if not line.startswith(".") and not line.rstrip().endswith(";"):
619 | line += ";"
620 | elif index == len(init_sql) and len(init_sql) > 0:
621 | line = None
622 | else:
623 | if show_prompt:
624 | if not_in_the_middle_of_any_input:
625 | show_prompt = False
626 | if index == 0:
627 | to_write.append("SQLite version %s (adapter version %s)\nEnter \".help\" for usage hints.\n" % (sqlite3.sqlite_version, sqlite3.version))
628 | if no_args:
629 | to_write.append("Connected to a transient in-memory database.\nUse \".open FILENAME\" to reopen on a persistent database.\n")
630 | if index > 0 and not prev_line:
631 | to_write.append("\n")
632 | to_write.append("%7s " % ("sqlite%s>" % ("",) if not_in_the_middle_of_any_input else "...>",))
633 | try:
634 | line = stdio.inputln("".join(to_write))
635 | except KeyboardInterrupt:
636 | line = ""
637 | raise # just kidding, don't handle it for now...
638 | return line
639 | for command in sql_commands(wrap_bytes_comparable_with_unicode_readline(read_stdin)):
640 | result = exec_command(db, command, True)
641 | if result is not None:
642 | return result
643 | if init_show_prompt and len(init_sql) == 0:
644 | stdio.outputln()
645 |
646 | def call_program(cmdline, input_text):
647 | import subprocess
648 | return subprocess.Popen(cmdline, bufsize=0, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=False).communicate(input_text)
649 |
650 | def test_query():
651 | hexcodec = codecs.lookup('hex_codec')
652 | ascii = 'ascii'
653 | data1 = b"\xD8\xA2"
654 | data2 = b"\x01\x02\xFF\x01\xFF\xFE\xFD"
655 | values = [data1, data2]
656 | query_bytes = b'SELECT %s;' % (b", ".join(map(lambda b: b"X'%s'" % (hexcodec.encode(b)[0].upper(),), values)),)
657 | expected_bytes = b"%s\n" % (b"|".join(values),)
658 | return query_bytes, expected_bytes
659 |
660 | def cross_test(sqlite_cmdline, codec):
661 | (query_bytes, expected_bytes) = test_query()
662 | (official_output, official_error) = call_program(sqlite_cmdline, query_bytes)
663 | # We can't use os.linesep here since binaries may belong to different platforms (Win32/MinGW vs. MSYS/Cygwin vs. WSL...)
664 | official_output = official_output.replace(b"\r\n", b"\n")
665 | official_error = official_error.replace(b"\r\n", b"\n")
666 | if official_output != expected_bytes:
667 | raise sqlite3.ProgrammingError("expected bytes are wrong: official %s != expected %s" % (repr(official_output), repr(expected_bytes)))
668 | if official_error:
669 | raise sqlite3.ProgrammingError("did not expect errors from official binary")
670 |
671 | def self_test(codec):
672 | (query_bytes, expected_bytes) = test_query()
673 | if not (lambda stdin, stdout, stderr: not main(sys.argv[0], stdin=stdin, stdout=stdout, stderr=stderr) and stdout.getvalue() == expected_bytes)(io.BytesIO(query_bytes), io.BytesIO(), io.BytesIO()):
674 | raise sqlite3.ProgrammingError("byte I/O is broken")
675 | if not (lambda stdin, stdout, stderr: not main(sys.argv[0], stdin=stdin, stdout=stdout, stderr=stderr) and stdout.getvalue() == codec.decode(expected_bytes, 'surrogateescape'))(io.StringIO(query_bytes.decode(ascii)), io.StringIO(), io.StringIO()):
676 | raise sqlite3.ProgrammingError("string I/O is broken")
677 |
678 | if __name__ == '__main__':
679 | import sys
680 | exit_code = main(*sys.argv)
681 | if exit_code not in (None, 0): raise SystemExit(exit_code)
682 |
--------------------------------------------------------------------------------
/lec25/lec25-declarative-programming.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JacyCui/sicp-lectures/0d8e574f3886ec8807937b482e97779fb8918b8b/lec25/lec25-declarative-programming.pdf
--------------------------------------------------------------------------------
/lec26/demo/demo.sql:
--------------------------------------------------------------------------------
1 | .open
2 |
3 | --new
4 |
5 | ----------
6 | -- Dogs --
7 | ----------
8 |
9 | -- Parents
10 | CREATE TABLE parents AS
11 | SELECT "abraham" AS parent, "barack" AS child UNION
12 | SELECT "abraham" , "clinton" UNION
13 | SELECT "delano" , "herbert" UNION
14 | SELECT "fillmore" , "abraham" UNION
15 | SELECT "fillmore" , "delano" UNION
16 | SELECT "fillmore" , "grover" UNION
17 | SELECT "eisenhower" , "fillmore";
18 |
19 | -- Fur
20 | CREATE TABLE dogs AS
21 | SELECT "abraham" AS name, "long" AS fur UNION
22 | SELECT "barack" , "short" UNION
23 | SELECT "clinton" , "long" UNION
24 | SELECT "delano" , "long" UNION
25 | SELECT "eisenhower" , "short" UNION
26 | SELECT "fillmore" , "curly" UNION
27 | SELECT "grover" , "short" UNION
28 | SELECT "herbert" , "curly";
29 |
30 | -- Granddog
31 | CREATE TABLE grandparents AS
32 | SELECT a.parent AS grandog, b.child AS granpup
33 | FROM parents AS a, parents AS b
34 | WHERE a.child = b.parent;
35 |
36 | ------------
37 | -- Cities --
38 | ------------
39 |
40 | CREATE TABLE cities AS
41 | SELECT 38 AS latitude, 122 AS longitude, "Berkeley" AS name UNION
42 | SELECT 42, 71, "Cambridge" UNION
43 | SELECT 45, 93, "Minneapolis" UNION
44 | SELECT 33, 117, "San Diego" UNION
45 | SELECT 26, 80, "Miami" UNION
46 | SELECT 90, 0, "North Pole";
47 |
48 |
49 | ---------------
50 | -- Sentences --
51 | ---------------
52 |
53 | CREATE TABLE nouns AS
54 | SELECT "the dog" AS phrase UNION
55 | SELECT "the cat" UNION
56 | SELECT "the bird";
57 |
--------------------------------------------------------------------------------
/lec26/demo/sqlite_shell.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | # Licensed under the MIT license
4 |
5 | # A simple SQLite shell that uses the built-in Python adapter.
6 |
7 | import codecs
8 | import io
9 | import os
10 | import sys
11 | import sqlite3
12 | import time
13 | import warnings
14 |
15 | try: FileNotFoundError
16 | except NameError: FileNotFoundError = OSError
17 |
18 | if str != bytes: buffer = bytes
19 | if str != bytes: unicode = str
20 |
21 | try: import msvcrt
22 | except ImportError: msvcrt = None
23 |
24 | CP_UTF8 = 65001
25 | pythonapi = None
26 | if msvcrt:
27 | import ctypes
28 | (BOOL, DWORD, HANDLE, UINT) = (ctypes.c_long, ctypes.c_ulong, ctypes.c_void_p, ctypes.c_uint)
29 | GetConsoleCP = ctypes.WINFUNCTYPE(UINT)(('GetConsoleCP', ctypes.windll.kernel32))
30 | SetConsoleCP = ctypes.WINFUNCTYPE(BOOL, UINT)(('SetConsoleCP', ctypes.windll.kernel32))
31 | GetConsoleOutputCP = ctypes.WINFUNCTYPE(UINT)(('GetConsoleOutputCP', ctypes.windll.kernel32))
32 | SetConsoleOutputCP = ctypes.WINFUNCTYPE(BOOL, UINT)(('SetConsoleOutputCP', ctypes.windll.kernel32))
33 | GetConsoleMode = ctypes.WINFUNCTYPE(BOOL, HANDLE, ctypes.POINTER(DWORD), use_last_error=True)(('GetConsoleMode', ctypes.windll.kernel32))
34 | GetNumberOfConsoleInputEvents = ctypes.WINFUNCTYPE(BOOL, HANDLE, ctypes.POINTER(DWORD), use_last_error=True)(('GetNumberOfConsoleInputEvents', ctypes.windll.kernel32))
35 | ReadConsoleW = ctypes.WINFUNCTYPE(BOOL, HANDLE, ctypes.c_void_p, DWORD, ctypes.POINTER(DWORD), ctypes.c_void_p, use_last_error=True)(('ReadConsoleW', ctypes.windll.kernel32))
36 | WriteConsoleW = ctypes.WINFUNCTYPE(BOOL, HANDLE, ctypes.c_void_p, DWORD, ctypes.POINTER(DWORD), ctypes.c_void_p, use_last_error=True)(('WriteConsoleW', ctypes.windll.kernel32))
37 | class Py_buffer(ctypes.Structure): _fields_ = [('buf', ctypes.c_void_p), ('obj', ctypes.py_object), ('len', ctypes.c_ssize_t), ('itemsize', ctypes.c_ssize_t), ('readonly', ctypes.c_int), ('ndim', ctypes.c_int), ('format', ctypes.c_char_p), ('shape', ctypes.POINTER(ctypes.c_ssize_t)), ('strides', ctypes.POINTER(ctypes.c_ssize_t)), ('suboffsets', ctypes.POINTER(ctypes.c_ssize_t))] + ([('smalltable', ctypes.c_ssize_t * 2)] if sys.version_info[0] <= 2 else []) + [('internal', ctypes.c_void_p)]
38 | try: from ctypes import pythonapi
39 | except ImportError: pass
40 | if pythonapi:
41 | def getbuffer(b, writable):
42 | arr = Py_buffer()
43 | pythonapi.PyObject_GetBuffer(ctypes.py_object(b), ctypes.byref(arr), ctypes.c_int(writable))
44 | try: buf = (ctypes.c_ubyte * arr.len).from_address(arr.buf)
45 | finally: pythonapi.PyBuffer_Release(ctypes.byref(arr))
46 | return buf
47 |
48 | ENCODING = 'utf-8'
49 |
50 | if sys.version_info[0] < 3:
51 | class NotASurrogateError(Exception): pass
52 | def surrogateescape_handler(exc):
53 | # Source: https://github.com/PythonCharmers/python-future/blob/aef57391c0cd58bf840dff5e2bc2c8c0f5b0a1b4/src/future/utils/surrogateescape.py
54 | mystring = exc.object[exc.start:exc.end]
55 | try:
56 | if isinstance(exc, UnicodeDecodeError):
57 | decoded = []
58 | for ch in mystring:
59 | if isinstance(ch, int):
60 | code = ch
61 | else:
62 | code = ord(ch)
63 | if 0x80 <= code <= 0xFF:
64 | decoded.append(unichr(0xDC00 + code))
65 | elif code <= 0x7F:
66 | decoded.append(unichr(code))
67 | else:
68 | raise NotASurrogateError()
69 | decoded = str().join(decoded)
70 | elif isinstance(exc, UnicodeEncodeError):
71 | decoded = []
72 | for ch in mystring:
73 | code = ord(ch)
74 | if not 0xD800 <= code <= 0xDCFF:
75 | raise NotASurrogateError()
76 | if 0xDC00 <= code <= 0xDC7F:
77 | decoded.append(unichr(code - 0xDC00))
78 | elif code <= 0xDCFF:
79 | decoded.append(unichr(code - 0xDC00))
80 | else:
81 | raise NotASurrogateError()
82 | decoded = str().join(decoded)
83 | else:
84 | raise exc
85 | except NotASurrogateError:
86 | raise exc
87 | return (decoded, exc.end)
88 | codecs.register_error('surrogateescape', surrogateescape_handler)
89 |
90 | def exception_encode(ex, codec):
91 | if str == bytes:
92 | reduced = ex.__reduce__()
93 | ex = reduced[0](*tuple(map(lambda arg: codec.decode(arg)[0] if isinstance(arg, bytes) else arg, reduced[1])))
94 | return ex
95 |
96 | def sql_commands(read_line):
97 | delims = ['"', "'", ';', '--']
98 | counter = 0
99 | in_string = None
100 | j = i = 0
101 | prev_line = None
102 | line = None
103 | concat = []
104 | while True:
105 | if line is None:
106 | while True: # process preprocessor directives
107 | counter += 1
108 | not_in_the_middle_of_any_input = not in_string and i == j and all(map(lambda chunk_: len(chunk_) == 0, concat))
109 | line = read_line(counter - 1, not_in_the_middle_of_any_input, prev_line)
110 | empty_string = line[:0] if line is not None else line
111 | prev_line = line
112 | if not line:
113 | break
114 | if not_in_the_middle_of_any_input and line.startswith("."):
115 | yield line
116 | line = None
117 | else:
118 | break
119 | if not line:
120 | break
121 | j = i = 0
122 | if j < len(line):
123 | (j, delim) = min(map(lambda pair: pair if pair[0] >= 0 else (len(line), pair[1]), map(lambda d: (line.find(d, j), d), in_string or delims if in_string != '--' else "\n")))
124 | if i < j: concat.append(line[i:j]); i = j
125 | if not in_string:
126 | if j < len(line):
127 | j += len(delim)
128 | if delim == ';':
129 | i = j
130 | concat.append(line[j : j + len(delim)]) # ensure delimeter is the same type as the string (it may not be due to implicit conversion)
131 | # Eat up any further spaces until a newline
132 | while j < len(line):
133 | delim = line[j:j+1]
134 | if not delim.isspace(): break
135 | j += 1
136 | if delim == "\n": break
137 | if i < j: concat.append(line[i:j]); i = j
138 | yield empty_string.join(concat)
139 | del concat[:]
140 | else:
141 | in_string = delim
142 | else:
143 | if j < len(line):
144 | ch = line[j:j+1]
145 | assert ch == in_string or in_string == '--'
146 | j += 1
147 | i = j
148 | concat.append(ch)
149 | in_string = None
150 | else:
151 | if i < j: concat.append(line[i:j]); i = j
152 | line = None
153 |
154 | class WindowsConsoleIOMixin(object):
155 | # Ctrl+C handling with ReadFile() is messed up on Windows starting on Windows 8... here's some background reading:
156 | # https://stackoverflow.com/a/43260436
157 | # https://github.com/microsoft/terminal/issues/334
158 | # We use ReadConsole when we can, so it doesn't affect us, but it's good info to know regardless.
159 | def __init__(self, fd):
160 | assert isatty(fd), "file descriptor must refer to a console (note that on Windows, NUL satisfies isatty(), but is not a console)"
161 | self.fd = fd
162 | self.handle = msvcrt.get_osfhandle(fd)
163 | def fileno(self): return self.fd
164 | def isatty(self): return isatty(self.fd)
165 | def seekable(self): return False
166 | def readable(self): return GetNumberOfConsoleInputEvents(self.handle, ctypes.byref(DWORD(0))) != 0
167 | def writable(self): n = DWORD(0); return WriteConsoleW(self.handle, ctypes.c_void_p(), n, ctypes.byref(n), ctypes.c_void_p()) != 0
168 | def readwcharsinto(self, buf, n):
169 | nr = DWORD(n)
170 | old_error = ctypes.get_last_error()
171 | ctypes.set_last_error(0)
172 | success = ReadConsoleW(self.handle, buf, nr, ctypes.byref(nr), ctypes.c_void_p())
173 | error = ctypes.get_last_error()
174 | ctypes.set_last_error(old_error)
175 | if not success: raise ctypes.WinError(error)
176 | ERROR_OPERATION_ABORTED = 995
177 | if nr.value == 0 and error == ERROR_OPERATION_ABORTED:
178 | # Apparently this can trigger pending KeyboardInterrupts?
179 | time.sleep(1.0 / (1 << 64))
180 | raise KeyboardInterrupt() # If Python doesn't raise it, we can
181 | return nr.value
182 | def writewchars(self, buf, n):
183 | nw = DWORD(n)
184 | if not WriteConsoleW(self.handle, buf, nw, ctypes.byref(nw), ctypes.c_void_p()):
185 | raise ctypes.WinError()
186 | return nw.value
187 |
188 | class WindowsConsoleRawIO(WindowsConsoleIOMixin, io.RawIOBase):
189 | def readinto(self, b):
190 | wordsize = ctypes.sizeof(ctypes.c_wchar)
191 | return self.readwcharsinto(getbuffer(b, True), len(b) // wordsize) * wordsize
192 | def write(self, b):
193 | wordsize = ctypes.sizeof(ctypes.c_wchar)
194 | return self.writewchars(getbuffer(b, False), len(b) // wordsize) * wordsize
195 |
196 | class WindowsConsoleTextIO(WindowsConsoleIOMixin, io.TextIOBase):
197 | buf = None
198 | buffered = unicode()
199 | translate = True
200 | def getbuf(self, ncodeunits):
201 | buf = self.buf
202 | if buf is None or len(buf) < ncodeunits:
203 | self.buf = buf = ctypes.create_unicode_buffer(ncodeunits)
204 | return buf
205 | @staticmethod # Don't let classes override this... they can override the caller instead
206 | def do_read(self, nchars, translate_newlines):
207 | prenewline = os.linesep[:-1]
208 | newline = os.linesep[-1:]
209 | empty = os.linesep[:0]
210 | if nchars is None or nchars < -1: nchars = -1
211 | ncodeunits = nchars if nchars >= 0 else io.DEFAULT_BUFFER_SIZE # Unit mismatch, but doesn't matter; we'll loop
212 | buf = None
213 | istart = 0
214 | while True:
215 | iend = self.buffered.find(newline, istart, min(istart + nchars, len(self.buffered)) if nchars >= 0 else None) if newline is not None else nchars
216 | if iend >= 0: iend += len(newline) if newline is not None else 0
217 | if 0 <= iend <= len(self.buffered):
218 | break
219 | if buf is None: buf = self.getbuf(ncodeunits)
220 | istart = len(self.buffered)
221 | chunk = buf[:self.readwcharsinto(buf, ncodeunits)]
222 | if translate_newlines: chunk = chunk.replace(prenewline, empty)
223 | if chunk.startswith('\x1A'): # EOF on Windows (Ctrl+Z) at the beginning of a line results in the entire rest of the buffer being discarded
224 | iend = istart
225 | break
226 | # Python 2 and Python 3 behaviors differ on Windows... Python 2's sys.stdin.readline() just deletes the next character if it sees EOF in the middle of a string! I won't emulate that here.
227 | self.buffered += chunk # We're relying on Python's concatenation optimization here... we don't do it ourselves, since we want self.buffered to be valid every iteration in case there is an exception raised
228 | result = self.buffered[:iend]
229 | self.buffered = self.buffered[iend:]
230 | return result
231 | def read(self, nchars=-1): return WindowsConsoleTextIO.do_read(self, nchars, None, self.translate)
232 | def readline(self, nchars=-1): return WindowsConsoleTextIO.do_read(self, nchars, self.translate)
233 | def write(self, text): buf = ctypes.create_unicode_buffer(text); return self.writewchars(buf, max(len(buf) - 1, 0))
234 |
235 | def wrap_windows_console_io(stream, is_output):
236 | fd = None
237 | if stream is not None and sys.version_info[0] < 3 and msvcrt and (is_output or pythonapi) and isatty(stream):
238 | try: fd = stream.fileno()
239 | except io.UnsupportedOperation: pass
240 | result = stream
241 | if fd is not None:
242 | f = GetConsoleOutputCP if is_output else GetConsoleCP
243 | if not f or f() != CP_UTF8:
244 | try:
245 | if True or is_output:
246 | result = WindowsConsoleTextIO(fd)
247 | else:
248 | result = io.TextIOWrapper((io.BufferedWriter if is_output else io.BufferedReader)(WindowsConsoleRawIO(fd)), 'utf-16-le', 'strict', line_buffering=True)
249 | except IOError: pass
250 | return result
251 |
252 | class NonOwningTextIOWrapper(io.TextIOWrapper):
253 | def __init__(self, base_textiowrapper, **kwargs):
254 | assert isinstance(base_textiowrapper, io.TextIOWrapper)
255 | self.base = base_textiowrapper # must keep a reference to this alive so it doesn't get closed
256 | super(NonOwningTextIOWrapper, self).__init__(base_textiowrapper.buffer, **kwargs)
257 | def close(self):
258 | super(NonOwningTextIOWrapper, self).flush()
259 |
260 | def wrap_unicode_stdio(stream, is_writer, encoding): # The underlying stream must NOT be used directly until the stream returned by this function is disposed of
261 | if isinstance(stream, io.TextIOWrapper):
262 | stream.flush() # Make sure nothing is left in the buffer before we re-wrap it
263 | none = object()
264 | kwargs = {}
265 | for key in ['encoding', 'errors', 'newline', 'line_buffering', 'write_through']:
266 | value = getattr(stream, 'newlines' if key == 'newline' else key, none)
267 | if value is not none:
268 | kwargs[key] = value
269 | kwargs['encoding'] = encoding
270 | result = NonOwningTextIOWrapper(stream, **kwargs)
271 | elif 'PYTHONIOENCODING' not in os.environ and str == bytes and stream in (sys.stdin, sys.stdout, sys.stderr):
272 | result = (codecs.getwriter if is_writer else codecs.getreader)(encoding)(stream)
273 | else:
274 | result = stream
275 | return result
276 |
277 | class StringEscapeParser(object):
278 | def __init__(self):
279 | import re
280 | self.pattern = re.compile("\"((?:[^\"\\n]+|\\\\.)*)(?:\"|$)|\'([^\'\\n]*)(?:\'|$)|(\\S+)")
281 | self.escape_pattern = re.compile("\\\\(.)", re.DOTALL)
282 | @staticmethod
283 | def escape_replacement(m):
284 | text = m.group(1)
285 | if text == "\\": text = "\\"
286 | elif text == "/": text = "\n"
287 | elif text == "n": text = "\n"
288 | elif text == "r": text = "\r"
289 | elif text == "t": text = "\t"
290 | elif text == "v": text = "\v"
291 | elif text == "f": text = "\f"
292 | elif text == "a": text = "\a"
293 | elif text == "b": text = "\b"
294 | return text
295 | def __call__(self, s):
296 | escape_pattern = self.escape_pattern
297 | escape_replacement = self.escape_replacement
298 | result = []
299 | for match in self.pattern.finditer(s):
300 | [m1, m2, m3] = match.groups()
301 | if m1 is not None: result.append(escape_pattern.sub(escape_replacement, m1))
302 | if m2 is not None: result.append(m2)
303 | if m3 is not None: result.append(escape_pattern.sub(escape_replacement, m3))
304 | return result
305 |
306 | class Database(object):
307 | def __init__(self, name, *args, **kwargs):
308 | self.connection = sqlite3.connect(name, *args, **kwargs)
309 | self.cursor = self.connection.cursor()
310 | self.name = name # assign name only AFTER cursor is created
311 |
312 | def isatty(file_or_fd):
313 | result = True
314 | method = getattr(file_or_fd, 'isatty', None) if not isinstance(file_or_fd, int) else None # this check is just an optimization
315 | if method is not None:
316 | try: tty = method()
317 | except io.UnsupportedOperation: tty = None
318 | result = result and tty is not None and tty
319 | method = getattr(file_or_fd, 'fileno', None) if not isinstance(file_or_fd, int) else None # this check is just an optimization
320 | if method is not None:
321 | try: fd = method()
322 | except io.UnsupportedOperation: fd = None
323 | result = result and fd is not None and os.isatty(fd) and (not msvcrt or GetConsoleMode(msvcrt.get_osfhandle(fd), ctypes.byref(DWORD(0))) != 0)
324 | return result
325 |
326 | def can_call_input_for_stdio(stream):
327 | return stream == sys.stdin and sys.version_info[0] >= 3
328 |
329 | class StdIOProxy(object):
330 | # Maybe useful later: codecs.StreamRecoder(bytesIO, codec.decode, codec.encode, codec.streamwriter, codec.streamreader, errors='surrogateescape')
331 | def __init__(self, stdin, stdout, stderr, codec, allow_set_code_page):
332 | self.codec = codec
333 | streams = (stdin, stdout, stderr)
334 | for stream in streams:
335 | assert isinstance(stream, io.IOBase) or sys.version_info[0] < 3 and isinstance(stream, file) or hasattr(stream, 'mode'), "unable to determine stream type"
336 | assert not isinstance(stream, io.RawIOBase), "RAW I/O APIs are different and not supported"
337 | self.streaminfos = tuple(map(lambda stream:
338 | (
339 | stream,
340 | isinstance(stream, io.BufferedIOBase) or isinstance(stream, io.RawIOBase) or not isinstance(stream, io.TextIOBase) and 'b' in stream.mode,
341 | isinstance(stream, io.TextIOBase) or not (isinstance(stream, io.BufferedIOBase) or isinstance(stream, io.RawIOBase)) and 'b' not in stream.mode,
342 | allow_set_code_page
343 | ),
344 | streams))
345 | @property
346 | def stdin(self): return self.streaminfos[0][0]
347 | @property
348 | def stdout(self): return self.streaminfos[1][0]
349 | @property
350 | def stderr(self): return self.streaminfos[2][0]
351 | def _coerce(self, streaminfo, codec, arg):
352 | stream = streaminfo[0]
353 | can_binary = streaminfo[1]
354 | can_text = streaminfo[2]
355 | if not isinstance(arg, bytes) and not isinstance(arg, buffer) and not isinstance(arg, unicode):
356 | arg = unicode(arg)
357 | if isinstance(arg, bytes) or isinstance(arg, buffer):
358 | if not can_binary:
359 | arg = codec.decode(arg, 'surrogateescape')[0]
360 | elif isinstance(arg, unicode):
361 | if not can_text:
362 | arg = codec.encode(unicode(arg), 'strict')[0]
363 | return arg
364 | @staticmethod
365 | def _do_readline(stream, allow_set_code_page, *args):
366 | new_code_page = CP_UTF8
367 | old_code_page = GetConsoleCP() if msvcrt and GetConsoleCP and isatty(stream) else None
368 | if old_code_page == new_code_page: old_code_page = None # Don't change code page if it's already correct...
369 | if old_code_page is not None:
370 | if not SetConsoleCP(new_code_page):
371 | old_code_page = None
372 | try:
373 | result = stream.readline(*args)
374 | finally:
375 | if old_code_page is not None:
376 | SetConsoleCP(old_code_page)
377 | return result
378 | @staticmethod
379 | def _do_write(stream, allow_set_code_page, *args):
380 | new_code_page = CP_UTF8
381 | old_code_page = GetConsoleOutputCP() if msvcrt and GetConsoleOutputCP and isatty(stream) else None
382 | if old_code_page == new_code_page: old_code_page = None # Don't change code page if it's already correct...
383 | if old_code_page is not None:
384 | if not SetConsoleOutputCP(new_code_page):
385 | old_code_page = None
386 | try:
387 | result = stream.write(*args)
388 | finally:
389 | if old_code_page is not None:
390 | SetConsoleCP(old_code_page)
391 | return result
392 | def _readln(self, streaminfo, codec, prompt):
393 | stream = streaminfo[0]
394 | can_binary = streaminfo[1]
395 | allow_set_code_page = streaminfo[3]
396 | if can_call_input_for_stdio(stream) and not can_binary: # input() can't work with binary data
397 | result = self._coerce(streaminfo, codec, "")
398 | try:
399 | result = input(*((self._coerce(streaminfo, codec, prompt),) if prompt is not None else ()))
400 | result += self._coerce(streaminfo, codec, "\n")
401 | except EOFError: pass
402 | else:
403 | self.output(*((prompt,) if prompt is not None else ()))
404 | self.error()
405 | result = StdIOProxy._do_readline(stream, allow_set_code_page)
406 | return result
407 | def _writeln(self, streaminfo, codec, *args, **kwargs):
408 | stream = streaminfo[0]
409 | allow_set_code_page = streaminfo[3]
410 | flush = kwargs.pop('flush', True)
411 | kwargs.setdefault('end', '\n')
412 | kwargs.setdefault('sep', ' ')
413 | end = kwargs.get('end')
414 | sep = kwargs.get('sep')
415 | first = True
416 | for arg in args:
417 | if first: first = False
418 | elif sep is not None:
419 | StdIOProxy._do_write(stream, allow_set_code_page, self._coerce(streaminfo, codec, sep))
420 | StdIOProxy._do_write(stream, allow_set_code_page, self._coerce(streaminfo, codec, arg))
421 | if end is not None:
422 | StdIOProxy._do_write(stream, allow_set_code_page, self._coerce(streaminfo, codec, end))
423 | if flush: stream.flush()
424 | def inputln(self, prompt=None): return self._readln(self.streaminfos[0], self.codec, prompt)
425 | def output(self, *args, **kwargs): kwargs.setdefault('end', None); return self._writeln(self.streaminfos[1], self.codec, *args, **kwargs)
426 | def outputln(self, *args, **kwargs): return self._writeln(self.streaminfos[1], self.codec, *args, **kwargs)
427 | def error(self, *args, **kwargs): kwargs.setdefault('end', None); return self._writeln(self.streaminfos[2], self.codec, *args, **kwargs)
428 | def errorln(self, *args, **kwargs): return self._writeln(self.streaminfos[2], self.codec, *args, **kwargs)
429 |
430 | class bytes_comparable_with_unicode(bytes): # For Python 2/3 compatibility, to allow implicit conversion between strings and bytes when it is safe. (Used for strings like literals which we know be safe.)
431 | codec = codecs.lookup('ascii') # MUST be a safe encoding
432 | @classmethod
433 | def coerce(cls, other, for_output=False):
434 | return cls.codec.encode(other)[0] if not isinstance(other, bytes) else bytes_comparable_with_unicode(other) if for_output else other
435 | @classmethod
436 | def translate_if_bytes(cls, value):
437 | if value is not None and isinstance(value, bytes): value = cls(value)
438 | return value
439 | def __hash__(self): return super(bytes_comparable_with_unicode, self).__hash__() # To avoid warning
440 | def __eq__(self, other): return super(bytes_comparable_with_unicode, self).__eq__(self.coerce(other))
441 | def __ne__(self, other): return super(bytes_comparable_with_unicode, self).__ne__(self.coerce(other))
442 | def __lt__(self, other): return super(bytes_comparable_with_unicode, self).__lt__(self.coerce(other))
443 | def __gt__(self, other): return super(bytes_comparable_with_unicode, self).__gt__(self.coerce(other))
444 | def __le__(self, other): return super(bytes_comparable_with_unicode, self).__le__(self.coerce(other))
445 | def __ge__(self, other): return super(bytes_comparable_with_unicode, self).__ge__(self.coerce(other))
446 | def __getitem__(self, index): return self.coerce(super(bytes_comparable_with_unicode, self).__getitem__(index), True)
447 | def __add__(self, other): return self.coerce(super(bytes_comparable_with_unicode, self).__add__(self.coerce(other)), True)
448 | def __iadd__(self, other): return self.coerce(super(bytes_comparable_with_unicode, self).__iadd__(self.coerce(other)), True)
449 | def __radd__(self, other): return self.coerce(self.coerce(other).__add__(self), True)
450 | def find(self, other, *args): return super(bytes_comparable_with_unicode, self).find(self.coerce(other), *args)
451 | def join(self, others): return self.coerce(super(bytes_comparable_with_unicode, self).join(map(self.coerce, others)), True)
452 | def startswith(self, other): return super(bytes_comparable_with_unicode, self).startswith(self.coerce(other))
453 | def __str__(self): return self.codec.decode(self)[0]
454 | if str == bytes:
455 | __unicode__ = __str__
456 | def __str__(self): raise NotImplementedError()
457 |
458 | def wrap_bytes_comparable_with_unicode_readline(readline):
459 | def callback(*args):
460 | line = readline(*args)
461 | line = bytes_comparable_with_unicode.translate_if_bytes(line)
462 | return line
463 | return callback
464 |
465 | def main(program, *args, **kwargs): # **kwargs = dict(stdin=file, stdout=file, stderr=file); useful for callers who import this module
466 | import argparse # slow import (compiles regexes etc.), so don't import it until needed
467 | argparser = argparse.ArgumentParser(
468 | prog=os.path.basename(program),
469 | usage=None,
470 | description=None,
471 | epilog=None,
472 | parents=[],
473 | formatter_class=argparse.RawTextHelpFormatter)
474 | argparser.add_argument('-version', '--version', action='store_true', help="show SQLite version")
475 | argparser.add_argument('-batch', '--batch', action='store_true', help="force batch I/O")
476 | argparser.add_argument('-init', '--init', metavar="FILE", help="read/process named file")
477 | argparser.add_argument('filename', nargs='?', metavar="FILENAME", help="is the name of an SQLite database.\nA new database is created if the file does not previously exist.")
478 | argparser.add_argument('sql', nargs='*', metavar="SQL", help="SQL commnds to execute after opening database")
479 | argparser.add_argument('--readline', action='store', metavar="(true|false)", default="true", choices=("true", "false"), help="whether to import readline if available (default: %(default)s)")
480 | argparser.add_argument('--self-test', action='store_true', help="perform a basic self-test")
481 | argparser.add_argument('--cross-test', action='store_true', help="perform a basic test against the official executable")
482 | argparser.add_argument('--unicode-stdio', action='store', metavar="(true|false)", default="true", choices=("true", "false"), help="whether to enable Unicode wrapper for standard I/O (default: %(default)s)")
483 | argparser.add_argument('--console', action='store', metavar="(true|false)", default="true", choices=("true", "false"), help="whether to auto-detect and use console window APIs (default: %(default)s)")
484 | argparser.add_argument('--encoding', default=ENCODING, help="the default encoding to use (default: %(default)s)")
485 | (stdin, stdout, stderr) = (kwargs.pop('stdin', sys.stdin), kwargs.pop('stdout', sys.stdout), kwargs.pop('stderr', sys.stderr))
486 | parsed_args = argparser.parse_args(args)
487 | codec = codecs.lookup(parsed_args.encoding or argparser.get_default('encoding'))
488 | if parsed_args.self_test: self_test(codec)
489 | if parsed_args.cross_test: cross_test("sqlite3", codec)
490 | parse_escaped_strings = StringEscapeParser()
491 | if parsed_args.unicode_stdio == "true":
492 | stdin = wrap_unicode_stdio(stdin, False, codec.name)
493 | stdout = wrap_unicode_stdio(stdout, True, codec.name)
494 | stderr = wrap_unicode_stdio(stderr, True, codec.name)
495 | if parsed_args.console == "true":
496 | stdin = wrap_windows_console_io(stdin, False)
497 | stdout = wrap_windows_console_io(stdout, True)
498 | stderr = wrap_windows_console_io(stderr, True)
499 | allow_set_code_page = sys.version_info[0] < 3 and False # This is only necessary on Python 2 if we use the default I/O functions instead of bypassing to ReadConsole()/WriteConsole()
500 | stdio = StdIOProxy(stdin, stdout, stderr, codec, allow_set_code_page)
501 | db = None
502 | no_args = len(args) == 0
503 | init_sql = parsed_args.sql
504 | is_nonpipe_input = stdin.isatty() # NOT the same thing as TTY! (NUL and /dev/null are the difference)
505 | init_show_prompt = not parsed_args.batch and is_nonpipe_input
506 | if not parsed_args.batch and isatty(stdin) and (parsed_args.readline == "true" or __name__ == '__main__') and parsed_args.readline != "false":
507 | try:
508 | with warnings.catch_warnings():
509 | warnings.filterwarnings('ignore', category=DeprecationWarning)
510 | import readline
511 | except ImportError: pass
512 | if parsed_args and parsed_args.version:
513 | stdio.outputln(sqlite3.sqlite_version);
514 | else:
515 | filename = parsed_args.filename
516 | if filename is None: filename = ":memory:"
517 | db = Database(filename, isolation_level=None)
518 | def exec_script(db, filename, ignore_io_errors):
519 | try:
520 | with io.open(filename, 'r', encoding=codec.name) as f: # Assume .sql files are text -- any binary data inside them should be X'' encoded, not embedded directly
521 | for command in sql_commands(wrap_bytes_comparable_with_unicode_readline(lambda *args: (lambda s: (s) or None)(f.readline()))):
522 | result = exec_command(db, command, False and ignore_io_errors)
523 | if result is not None:
524 | return result
525 | except IOError as ex:
526 | stdio.errorln(ex)
527 | if not ignore_io_errors: return ex.errno
528 | def raise_invalid_command_error(command):
529 | if isinstance(command, bytes): command = codec.decode(command)[0]
530 | if command.startswith("."): command = command[1:]
531 | raise RuntimeError("Error: unknown command or invalid arguments: \"%s\". Enter \".help\" for help" % (command.rstrip().replace("\\", "\\\\").replace("\"", "\\\""),))
532 | def exec_command(db, command, ignore_io_errors):
533 | results = None
534 | query = None
535 | query_parameters = {}
536 | try:
537 | if command.startswith("."):
538 | args = list(parse_escaped_strings(command))
539 | if args[0] in (".quit", ".exit"):
540 | return 0
541 | elif args[0] == ".help":
542 | stdio.error("""
543 | .cd DIRECTORY Change the working directory to DIRECTORY
544 | .dump Dump the database in an SQL text format
545 | .exit Exit this program
546 | .help Show this message
547 | .open FILE Close existing database and reopen FILE
548 | .print STRING... Print literal STRING
549 | .quit Exit this program
550 | .read FILENAME Execute SQL in FILENAME
551 | .schema ?PATTERN? Show the CREATE statements matching PATTERN
552 | .show Show the current values for various settings
553 | .tables ?TABLE? List names of tables
554 | """.lstrip())
555 | elif args[0] == ".cd":
556 | if len(args) != 2: raise_invalid_command_error(command)
557 | os.chdir(args[1])
558 | elif args[0] == ".dump":
559 | if len(args) != 1: raise_invalid_command_error(command)
560 | foreign_keys = db.cursor.execute("PRAGMA foreign_keys;").fetchone()[0]
561 | if foreign_keys in (0, "0", "off", "OFF"):
562 | stdio.outputln("PRAGMA foreign_keys=OFF;", flush=False)
563 | for line in db.connection.iterdump():
564 | stdio.outputln(line, flush=False)
565 | stdio.output()
566 | elif args[0] == ".open":
567 | if len(args) <= 1: raise_invalid_command_error(command)
568 | filename = args[-1]
569 | for option in args[+1:-1]:
570 | raise ValueError("option %s not supported" % (repr(option),))
571 | try: db.__init__(filename)
572 | except sqlite3.OperationalError as ex:
573 | ex.args = ex.args[:0] + ("Error: unable to open database \"%s\": %s" % (filename, ex.args[0]),) + ex.args[1:]
574 | raise
575 | elif args[0] == ".print":
576 | stdio.outputln(*args[1:])
577 | elif args[0] == ".read":
578 | if len(args) != 2: raise_invalid_command_error(command)
579 | exec_script(db, args[1], ignore_io_errors)
580 | elif args[0] == ".schema":
581 | if len(args) > 2: raise_invalid_command_error(command)
582 | pattern = args[1] if len(args) > 1 else None
583 | query_parameters['type'] = 'table'
584 | if pattern is not None:
585 | query_parameters['pattern'] = pattern
586 | query = "SELECT sql || ';' FROM sqlite_master WHERE type = :type" + (" AND name LIKE :pattern" if pattern is not None else "") + ";"
587 | elif args[0] == ".show":
588 | if len(args) > 2: raise_invalid_command_error(command)
589 | stdio.errorln(" filename:", db.name)
590 | elif args[0] == ".tables":
591 | if len(args) > 2: raise_invalid_command_error(command)
592 | pattern = args[1] if len(args) > 1 else None
593 | query_parameters['type'] = 'table'
594 | if pattern is not None:
595 | query_parameters['pattern'] = pattern
596 | query = "SELECT name FROM sqlite_master WHERE type = :type" + (" AND name LIKE :pattern" if pattern is not None else "") + ";"
597 | else:
598 | raise_invalid_command_error(args[0])
599 | else:
600 | query = command
601 | if query is not None:
602 | results = db.cursor.execute(query if isinstance(query, unicode) else codec.decode(query, 'surrogatereplace')[0], query_parameters)
603 | except (RuntimeError, OSError, FileNotFoundError, sqlite3.OperationalError) as ex:
604 | stdio.errorln(exception_encode(ex, codec))
605 | if results is not None:
606 | for row in results:
607 | stdio.outputln(*tuple(map(lambda item: item if item is not None else "", row)), sep="|", flush=False)
608 | stdio.output()
609 | if db:
610 | if parsed_args and parsed_args.init:
611 | if is_nonpipe_input: stdio.errorln("-- Loading resources from", parsed_args.init)
612 | exec_script(db, parsed_args.init, False)
613 | def read_stdin(index, not_in_the_middle_of_any_input, prev_line):
614 | show_prompt = init_show_prompt
615 | to_write = []
616 | if index < len(init_sql):
617 | line = init_sql[index]
618 | if not line.startswith(".") and not line.rstrip().endswith(";"):
619 | line += ";"
620 | elif index == len(init_sql) and len(init_sql) > 0:
621 | line = None
622 | else:
623 | if show_prompt:
624 | if not_in_the_middle_of_any_input:
625 | show_prompt = False
626 | if index == 0:
627 | to_write.append("SQLite version %s (adapter version %s)\nEnter \".help\" for usage hints.\n" % (sqlite3.sqlite_version, sqlite3.version))
628 | if no_args:
629 | to_write.append("Connected to a transient in-memory database.\nUse \".open FILENAME\" to reopen on a persistent database.\n")
630 | if index > 0 and not prev_line:
631 | to_write.append("\n")
632 | to_write.append("%7s " % ("sqlite%s>" % ("",) if not_in_the_middle_of_any_input else "...>",))
633 | try:
634 | line = stdio.inputln("".join(to_write))
635 | except KeyboardInterrupt:
636 | line = ""
637 | raise # just kidding, don't handle it for now...
638 | return line
639 | for command in sql_commands(wrap_bytes_comparable_with_unicode_readline(read_stdin)):
640 | result = exec_command(db, command, True)
641 | if result is not None:
642 | return result
643 | if init_show_prompt and len(init_sql) == 0:
644 | stdio.outputln()
645 |
646 | def call_program(cmdline, input_text):
647 | import subprocess
648 | return subprocess.Popen(cmdline, bufsize=0, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=False).communicate(input_text)
649 |
650 | def test_query():
651 | hexcodec = codecs.lookup('hex_codec')
652 | ascii = 'ascii'
653 | data1 = b"\xD8\xA2"
654 | data2 = b"\x01\x02\xFF\x01\xFF\xFE\xFD"
655 | values = [data1, data2]
656 | query_bytes = b'SELECT %s;' % (b", ".join(map(lambda b: b"X'%s'" % (hexcodec.encode(b)[0].upper(),), values)),)
657 | expected_bytes = b"%s\n" % (b"|".join(values),)
658 | return query_bytes, expected_bytes
659 |
660 | def cross_test(sqlite_cmdline, codec):
661 | (query_bytes, expected_bytes) = test_query()
662 | (official_output, official_error) = call_program(sqlite_cmdline, query_bytes)
663 | # We can't use os.linesep here since binaries may belong to different platforms (Win32/MinGW vs. MSYS/Cygwin vs. WSL...)
664 | official_output = official_output.replace(b"\r\n", b"\n")
665 | official_error = official_error.replace(b"\r\n", b"\n")
666 | if official_output != expected_bytes:
667 | raise sqlite3.ProgrammingError("expected bytes are wrong: official %s != expected %s" % (repr(official_output), repr(expected_bytes)))
668 | if official_error:
669 | raise sqlite3.ProgrammingError("did not expect errors from official binary")
670 |
671 | def self_test(codec):
672 | (query_bytes, expected_bytes) = test_query()
673 | if not (lambda stdin, stdout, stderr: not main(sys.argv[0], stdin=stdin, stdout=stdout, stderr=stderr) and stdout.getvalue() == expected_bytes)(io.BytesIO(query_bytes), io.BytesIO(), io.BytesIO()):
674 | raise sqlite3.ProgrammingError("byte I/O is broken")
675 | if not (lambda stdin, stdout, stderr: not main(sys.argv[0], stdin=stdin, stdout=stdout, stderr=stderr) and stdout.getvalue() == codec.decode(expected_bytes, 'surrogateescape'))(io.StringIO(query_bytes.decode(ascii)), io.StringIO(), io.StringIO()):
676 | raise sqlite3.ProgrammingError("string I/O is broken")
677 |
678 | if __name__ == '__main__':
679 | import sys
680 | exit_code = main(*sys.argv)
681 | if exit_code not in (None, 0): raise SystemExit(exit_code)
682 |
--------------------------------------------------------------------------------
/lec26/lec26-tables.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JacyCui/sicp-lectures/0d8e574f3886ec8807937b482e97779fb8918b8b/lec26/lec26-tables.pdf
--------------------------------------------------------------------------------
/lec27/demo/demo.sql:
--------------------------------------------------------------------------------
1 | .open
2 |
3 | --new
4 |
5 | create table animals as
6 | select "dog" as kind, 4 as legs, 20 as weight union
7 | select "cat" , 4 , 10 union
8 | select "ferret" , 4 , 10 union
9 | select "parrot" , 2 , 6 union
10 | select "penguin" , 2 , 10 union
11 | select "t-rex" , 2 , 12000;
12 |
--------------------------------------------------------------------------------
/lec27/demo/sqlite_shell.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | # Licensed under the MIT license
4 |
5 | # A simple SQLite shell that uses the built-in Python adapter.
6 |
7 | import codecs
8 | import io
9 | import os
10 | import sys
11 | import sqlite3
12 | import time
13 | import warnings
14 |
15 | try: FileNotFoundError
16 | except NameError: FileNotFoundError = OSError
17 |
18 | if str != bytes: buffer = bytes
19 | if str != bytes: unicode = str
20 |
21 | try: import msvcrt
22 | except ImportError: msvcrt = None
23 |
24 | CP_UTF8 = 65001
25 | pythonapi = None
26 | if msvcrt:
27 | import ctypes
28 | (BOOL, DWORD, HANDLE, UINT) = (ctypes.c_long, ctypes.c_ulong, ctypes.c_void_p, ctypes.c_uint)
29 | GetConsoleCP = ctypes.WINFUNCTYPE(UINT)(('GetConsoleCP', ctypes.windll.kernel32))
30 | SetConsoleCP = ctypes.WINFUNCTYPE(BOOL, UINT)(('SetConsoleCP', ctypes.windll.kernel32))
31 | GetConsoleOutputCP = ctypes.WINFUNCTYPE(UINT)(('GetConsoleOutputCP', ctypes.windll.kernel32))
32 | SetConsoleOutputCP = ctypes.WINFUNCTYPE(BOOL, UINT)(('SetConsoleOutputCP', ctypes.windll.kernel32))
33 | GetConsoleMode = ctypes.WINFUNCTYPE(BOOL, HANDLE, ctypes.POINTER(DWORD), use_last_error=True)(('GetConsoleMode', ctypes.windll.kernel32))
34 | GetNumberOfConsoleInputEvents = ctypes.WINFUNCTYPE(BOOL, HANDLE, ctypes.POINTER(DWORD), use_last_error=True)(('GetNumberOfConsoleInputEvents', ctypes.windll.kernel32))
35 | ReadConsoleW = ctypes.WINFUNCTYPE(BOOL, HANDLE, ctypes.c_void_p, DWORD, ctypes.POINTER(DWORD), ctypes.c_void_p, use_last_error=True)(('ReadConsoleW', ctypes.windll.kernel32))
36 | WriteConsoleW = ctypes.WINFUNCTYPE(BOOL, HANDLE, ctypes.c_void_p, DWORD, ctypes.POINTER(DWORD), ctypes.c_void_p, use_last_error=True)(('WriteConsoleW', ctypes.windll.kernel32))
37 | class Py_buffer(ctypes.Structure): _fields_ = [('buf', ctypes.c_void_p), ('obj', ctypes.py_object), ('len', ctypes.c_ssize_t), ('itemsize', ctypes.c_ssize_t), ('readonly', ctypes.c_int), ('ndim', ctypes.c_int), ('format', ctypes.c_char_p), ('shape', ctypes.POINTER(ctypes.c_ssize_t)), ('strides', ctypes.POINTER(ctypes.c_ssize_t)), ('suboffsets', ctypes.POINTER(ctypes.c_ssize_t))] + ([('smalltable', ctypes.c_ssize_t * 2)] if sys.version_info[0] <= 2 else []) + [('internal', ctypes.c_void_p)]
38 | try: from ctypes import pythonapi
39 | except ImportError: pass
40 | if pythonapi:
41 | def getbuffer(b, writable):
42 | arr = Py_buffer()
43 | pythonapi.PyObject_GetBuffer(ctypes.py_object(b), ctypes.byref(arr), ctypes.c_int(writable))
44 | try: buf = (ctypes.c_ubyte * arr.len).from_address(arr.buf)
45 | finally: pythonapi.PyBuffer_Release(ctypes.byref(arr))
46 | return buf
47 |
48 | ENCODING = 'utf-8'
49 |
50 | if sys.version_info[0] < 3:
51 | class NotASurrogateError(Exception): pass
52 | def surrogateescape_handler(exc):
53 | # Source: https://github.com/PythonCharmers/python-future/blob/aef57391c0cd58bf840dff5e2bc2c8c0f5b0a1b4/src/future/utils/surrogateescape.py
54 | mystring = exc.object[exc.start:exc.end]
55 | try:
56 | if isinstance(exc, UnicodeDecodeError):
57 | decoded = []
58 | for ch in mystring:
59 | if isinstance(ch, int):
60 | code = ch
61 | else:
62 | code = ord(ch)
63 | if 0x80 <= code <= 0xFF:
64 | decoded.append(unichr(0xDC00 + code))
65 | elif code <= 0x7F:
66 | decoded.append(unichr(code))
67 | else:
68 | raise NotASurrogateError()
69 | decoded = str().join(decoded)
70 | elif isinstance(exc, UnicodeEncodeError):
71 | decoded = []
72 | for ch in mystring:
73 | code = ord(ch)
74 | if not 0xD800 <= code <= 0xDCFF:
75 | raise NotASurrogateError()
76 | if 0xDC00 <= code <= 0xDC7F:
77 | decoded.append(unichr(code - 0xDC00))
78 | elif code <= 0xDCFF:
79 | decoded.append(unichr(code - 0xDC00))
80 | else:
81 | raise NotASurrogateError()
82 | decoded = str().join(decoded)
83 | else:
84 | raise exc
85 | except NotASurrogateError:
86 | raise exc
87 | return (decoded, exc.end)
88 | codecs.register_error('surrogateescape', surrogateescape_handler)
89 |
90 | def exception_encode(ex, codec):
91 | if str == bytes:
92 | reduced = ex.__reduce__()
93 | ex = reduced[0](*tuple(map(lambda arg: codec.decode(arg)[0] if isinstance(arg, bytes) else arg, reduced[1])))
94 | return ex
95 |
96 | def sql_commands(read_line):
97 | delims = ['"', "'", ';', '--']
98 | counter = 0
99 | in_string = None
100 | j = i = 0
101 | prev_line = None
102 | line = None
103 | concat = []
104 | while True:
105 | if line is None:
106 | while True: # process preprocessor directives
107 | counter += 1
108 | not_in_the_middle_of_any_input = not in_string and i == j and all(map(lambda chunk_: len(chunk_) == 0, concat))
109 | line = read_line(counter - 1, not_in_the_middle_of_any_input, prev_line)
110 | empty_string = line[:0] if line is not None else line
111 | prev_line = line
112 | if not line:
113 | break
114 | if not_in_the_middle_of_any_input and line.startswith("."):
115 | yield line
116 | line = None
117 | else:
118 | break
119 | if not line:
120 | break
121 | j = i = 0
122 | if j < len(line):
123 | (j, delim) = min(map(lambda pair: pair if pair[0] >= 0 else (len(line), pair[1]), map(lambda d: (line.find(d, j), d), in_string or delims if in_string != '--' else "\n")))
124 | if i < j: concat.append(line[i:j]); i = j
125 | if not in_string:
126 | if j < len(line):
127 | j += len(delim)
128 | if delim == ';':
129 | i = j
130 | concat.append(line[j : j + len(delim)]) # ensure delimeter is the same type as the string (it may not be due to implicit conversion)
131 | # Eat up any further spaces until a newline
132 | while j < len(line):
133 | delim = line[j:j+1]
134 | if not delim.isspace(): break
135 | j += 1
136 | if delim == "\n": break
137 | if i < j: concat.append(line[i:j]); i = j
138 | yield empty_string.join(concat)
139 | del concat[:]
140 | else:
141 | in_string = delim
142 | else:
143 | if j < len(line):
144 | ch = line[j:j+1]
145 | assert ch == in_string or in_string == '--'
146 | j += 1
147 | i = j
148 | concat.append(ch)
149 | in_string = None
150 | else:
151 | if i < j: concat.append(line[i:j]); i = j
152 | line = None
153 |
154 | class WindowsConsoleIOMixin(object):
155 | # Ctrl+C handling with ReadFile() is messed up on Windows starting on Windows 8... here's some background reading:
156 | # https://stackoverflow.com/a/43260436
157 | # https://github.com/microsoft/terminal/issues/334
158 | # We use ReadConsole when we can, so it doesn't affect us, but it's good info to know regardless.
159 | def __init__(self, fd):
160 | assert isatty(fd), "file descriptor must refer to a console (note that on Windows, NUL satisfies isatty(), but is not a console)"
161 | self.fd = fd
162 | self.handle = msvcrt.get_osfhandle(fd)
163 | def fileno(self): return self.fd
164 | def isatty(self): return isatty(self.fd)
165 | def seekable(self): return False
166 | def readable(self): return GetNumberOfConsoleInputEvents(self.handle, ctypes.byref(DWORD(0))) != 0
167 | def writable(self): n = DWORD(0); return WriteConsoleW(self.handle, ctypes.c_void_p(), n, ctypes.byref(n), ctypes.c_void_p()) != 0
168 | def readwcharsinto(self, buf, n):
169 | nr = DWORD(n)
170 | old_error = ctypes.get_last_error()
171 | ctypes.set_last_error(0)
172 | success = ReadConsoleW(self.handle, buf, nr, ctypes.byref(nr), ctypes.c_void_p())
173 | error = ctypes.get_last_error()
174 | ctypes.set_last_error(old_error)
175 | if not success: raise ctypes.WinError(error)
176 | ERROR_OPERATION_ABORTED = 995
177 | if nr.value == 0 and error == ERROR_OPERATION_ABORTED:
178 | # Apparently this can trigger pending KeyboardInterrupts?
179 | time.sleep(1.0 / (1 << 64))
180 | raise KeyboardInterrupt() # If Python doesn't raise it, we can
181 | return nr.value
182 | def writewchars(self, buf, n):
183 | nw = DWORD(n)
184 | if not WriteConsoleW(self.handle, buf, nw, ctypes.byref(nw), ctypes.c_void_p()):
185 | raise ctypes.WinError()
186 | return nw.value
187 |
188 | class WindowsConsoleRawIO(WindowsConsoleIOMixin, io.RawIOBase):
189 | def readinto(self, b):
190 | wordsize = ctypes.sizeof(ctypes.c_wchar)
191 | return self.readwcharsinto(getbuffer(b, True), len(b) // wordsize) * wordsize
192 | def write(self, b):
193 | wordsize = ctypes.sizeof(ctypes.c_wchar)
194 | return self.writewchars(getbuffer(b, False), len(b) // wordsize) * wordsize
195 |
196 | class WindowsConsoleTextIO(WindowsConsoleIOMixin, io.TextIOBase):
197 | buf = None
198 | buffered = unicode()
199 | translate = True
200 | def getbuf(self, ncodeunits):
201 | buf = self.buf
202 | if buf is None or len(buf) < ncodeunits:
203 | self.buf = buf = ctypes.create_unicode_buffer(ncodeunits)
204 | return buf
205 | @staticmethod # Don't let classes override this... they can override the caller instead
206 | def do_read(self, nchars, translate_newlines):
207 | prenewline = os.linesep[:-1]
208 | newline = os.linesep[-1:]
209 | empty = os.linesep[:0]
210 | if nchars is None or nchars < -1: nchars = -1
211 | ncodeunits = nchars if nchars >= 0 else io.DEFAULT_BUFFER_SIZE # Unit mismatch, but doesn't matter; we'll loop
212 | buf = None
213 | istart = 0
214 | while True:
215 | iend = self.buffered.find(newline, istart, min(istart + nchars, len(self.buffered)) if nchars >= 0 else None) if newline is not None else nchars
216 | if iend >= 0: iend += len(newline) if newline is not None else 0
217 | if 0 <= iend <= len(self.buffered):
218 | break
219 | if buf is None: buf = self.getbuf(ncodeunits)
220 | istart = len(self.buffered)
221 | chunk = buf[:self.readwcharsinto(buf, ncodeunits)]
222 | if translate_newlines: chunk = chunk.replace(prenewline, empty)
223 | if chunk.startswith('\x1A'): # EOF on Windows (Ctrl+Z) at the beginning of a line results in the entire rest of the buffer being discarded
224 | iend = istart
225 | break
226 | # Python 2 and Python 3 behaviors differ on Windows... Python 2's sys.stdin.readline() just deletes the next character if it sees EOF in the middle of a string! I won't emulate that here.
227 | self.buffered += chunk # We're relying on Python's concatenation optimization here... we don't do it ourselves, since we want self.buffered to be valid every iteration in case there is an exception raised
228 | result = self.buffered[:iend]
229 | self.buffered = self.buffered[iend:]
230 | return result
231 | def read(self, nchars=-1): return WindowsConsoleTextIO.do_read(self, nchars, None, self.translate)
232 | def readline(self, nchars=-1): return WindowsConsoleTextIO.do_read(self, nchars, self.translate)
233 | def write(self, text): buf = ctypes.create_unicode_buffer(text); return self.writewchars(buf, max(len(buf) - 1, 0))
234 |
235 | def wrap_windows_console_io(stream, is_output):
236 | fd = None
237 | if stream is not None and sys.version_info[0] < 3 and msvcrt and (is_output or pythonapi) and isatty(stream):
238 | try: fd = stream.fileno()
239 | except io.UnsupportedOperation: pass
240 | result = stream
241 | if fd is not None:
242 | f = GetConsoleOutputCP if is_output else GetConsoleCP
243 | if not f or f() != CP_UTF8:
244 | try:
245 | if True or is_output:
246 | result = WindowsConsoleTextIO(fd)
247 | else:
248 | result = io.TextIOWrapper((io.BufferedWriter if is_output else io.BufferedReader)(WindowsConsoleRawIO(fd)), 'utf-16-le', 'strict', line_buffering=True)
249 | except IOError: pass
250 | return result
251 |
252 | class NonOwningTextIOWrapper(io.TextIOWrapper):
253 | def __init__(self, base_textiowrapper, **kwargs):
254 | assert isinstance(base_textiowrapper, io.TextIOWrapper)
255 | self.base = base_textiowrapper # must keep a reference to this alive so it doesn't get closed
256 | super(NonOwningTextIOWrapper, self).__init__(base_textiowrapper.buffer, **kwargs)
257 | def close(self):
258 | super(NonOwningTextIOWrapper, self).flush()
259 |
260 | def wrap_unicode_stdio(stream, is_writer, encoding): # The underlying stream must NOT be used directly until the stream returned by this function is disposed of
261 | if isinstance(stream, io.TextIOWrapper):
262 | stream.flush() # Make sure nothing is left in the buffer before we re-wrap it
263 | none = object()
264 | kwargs = {}
265 | for key in ['encoding', 'errors', 'newline', 'line_buffering', 'write_through']:
266 | value = getattr(stream, 'newlines' if key == 'newline' else key, none)
267 | if value is not none:
268 | kwargs[key] = value
269 | kwargs['encoding'] = encoding
270 | result = NonOwningTextIOWrapper(stream, **kwargs)
271 | elif 'PYTHONIOENCODING' not in os.environ and str == bytes and stream in (sys.stdin, sys.stdout, sys.stderr):
272 | result = (codecs.getwriter if is_writer else codecs.getreader)(encoding)(stream)
273 | else:
274 | result = stream
275 | return result
276 |
277 | class StringEscapeParser(object):
278 | def __init__(self):
279 | import re
280 | self.pattern = re.compile("\"((?:[^\"\\n]+|\\\\.)*)(?:\"|$)|\'([^\'\\n]*)(?:\'|$)|(\\S+)")
281 | self.escape_pattern = re.compile("\\\\(.)", re.DOTALL)
282 | @staticmethod
283 | def escape_replacement(m):
284 | text = m.group(1)
285 | if text == "\\": text = "\\"
286 | elif text == "/": text = "\n"
287 | elif text == "n": text = "\n"
288 | elif text == "r": text = "\r"
289 | elif text == "t": text = "\t"
290 | elif text == "v": text = "\v"
291 | elif text == "f": text = "\f"
292 | elif text == "a": text = "\a"
293 | elif text == "b": text = "\b"
294 | return text
295 | def __call__(self, s):
296 | escape_pattern = self.escape_pattern
297 | escape_replacement = self.escape_replacement
298 | result = []
299 | for match in self.pattern.finditer(s):
300 | [m1, m2, m3] = match.groups()
301 | if m1 is not None: result.append(escape_pattern.sub(escape_replacement, m1))
302 | if m2 is not None: result.append(m2)
303 | if m3 is not None: result.append(escape_pattern.sub(escape_replacement, m3))
304 | return result
305 |
306 | class Database(object):
307 | def __init__(self, name, *args, **kwargs):
308 | self.connection = sqlite3.connect(name, *args, **kwargs)
309 | self.cursor = self.connection.cursor()
310 | self.name = name # assign name only AFTER cursor is created
311 |
312 | def isatty(file_or_fd):
313 | result = True
314 | method = getattr(file_or_fd, 'isatty', None) if not isinstance(file_or_fd, int) else None # this check is just an optimization
315 | if method is not None:
316 | try: tty = method()
317 | except io.UnsupportedOperation: tty = None
318 | result = result and tty is not None and tty
319 | method = getattr(file_or_fd, 'fileno', None) if not isinstance(file_or_fd, int) else None # this check is just an optimization
320 | if method is not None:
321 | try: fd = method()
322 | except io.UnsupportedOperation: fd = None
323 | result = result and fd is not None and os.isatty(fd) and (not msvcrt or GetConsoleMode(msvcrt.get_osfhandle(fd), ctypes.byref(DWORD(0))) != 0)
324 | return result
325 |
326 | def can_call_input_for_stdio(stream):
327 | return stream == sys.stdin and sys.version_info[0] >= 3
328 |
329 | class StdIOProxy(object):
330 | # Maybe useful later: codecs.StreamRecoder(bytesIO, codec.decode, codec.encode, codec.streamwriter, codec.streamreader, errors='surrogateescape')
331 | def __init__(self, stdin, stdout, stderr, codec, allow_set_code_page):
332 | self.codec = codec
333 | streams = (stdin, stdout, stderr)
334 | for stream in streams:
335 | assert isinstance(stream, io.IOBase) or sys.version_info[0] < 3 and isinstance(stream, file) or hasattr(stream, 'mode'), "unable to determine stream type"
336 | assert not isinstance(stream, io.RawIOBase), "RAW I/O APIs are different and not supported"
337 | self.streaminfos = tuple(map(lambda stream:
338 | (
339 | stream,
340 | isinstance(stream, io.BufferedIOBase) or isinstance(stream, io.RawIOBase) or not isinstance(stream, io.TextIOBase) and 'b' in stream.mode,
341 | isinstance(stream, io.TextIOBase) or not (isinstance(stream, io.BufferedIOBase) or isinstance(stream, io.RawIOBase)) and 'b' not in stream.mode,
342 | allow_set_code_page
343 | ),
344 | streams))
345 | @property
346 | def stdin(self): return self.streaminfos[0][0]
347 | @property
348 | def stdout(self): return self.streaminfos[1][0]
349 | @property
350 | def stderr(self): return self.streaminfos[2][0]
351 | def _coerce(self, streaminfo, codec, arg):
352 | stream = streaminfo[0]
353 | can_binary = streaminfo[1]
354 | can_text = streaminfo[2]
355 | if not isinstance(arg, bytes) and not isinstance(arg, buffer) and not isinstance(arg, unicode):
356 | arg = unicode(arg)
357 | if isinstance(arg, bytes) or isinstance(arg, buffer):
358 | if not can_binary:
359 | arg = codec.decode(arg, 'surrogateescape')[0]
360 | elif isinstance(arg, unicode):
361 | if not can_text:
362 | arg = codec.encode(unicode(arg), 'strict')[0]
363 | return arg
364 | @staticmethod
365 | def _do_readline(stream, allow_set_code_page, *args):
366 | new_code_page = CP_UTF8
367 | old_code_page = GetConsoleCP() if msvcrt and GetConsoleCP and isatty(stream) else None
368 | if old_code_page == new_code_page: old_code_page = None # Don't change code page if it's already correct...
369 | if old_code_page is not None:
370 | if not SetConsoleCP(new_code_page):
371 | old_code_page = None
372 | try:
373 | result = stream.readline(*args)
374 | finally:
375 | if old_code_page is not None:
376 | SetConsoleCP(old_code_page)
377 | return result
378 | @staticmethod
379 | def _do_write(stream, allow_set_code_page, *args):
380 | new_code_page = CP_UTF8
381 | old_code_page = GetConsoleOutputCP() if msvcrt and GetConsoleOutputCP and isatty(stream) else None
382 | if old_code_page == new_code_page: old_code_page = None # Don't change code page if it's already correct...
383 | if old_code_page is not None:
384 | if not SetConsoleOutputCP(new_code_page):
385 | old_code_page = None
386 | try:
387 | result = stream.write(*args)
388 | finally:
389 | if old_code_page is not None:
390 | SetConsoleCP(old_code_page)
391 | return result
392 | def _readln(self, streaminfo, codec, prompt):
393 | stream = streaminfo[0]
394 | can_binary = streaminfo[1]
395 | allow_set_code_page = streaminfo[3]
396 | if can_call_input_for_stdio(stream) and not can_binary: # input() can't work with binary data
397 | result = self._coerce(streaminfo, codec, "")
398 | try:
399 | result = input(*((self._coerce(streaminfo, codec, prompt),) if prompt is not None else ()))
400 | result += self._coerce(streaminfo, codec, "\n")
401 | except EOFError: pass
402 | else:
403 | self.output(*((prompt,) if prompt is not None else ()))
404 | self.error()
405 | result = StdIOProxy._do_readline(stream, allow_set_code_page)
406 | return result
407 | def _writeln(self, streaminfo, codec, *args, **kwargs):
408 | stream = streaminfo[0]
409 | allow_set_code_page = streaminfo[3]
410 | flush = kwargs.pop('flush', True)
411 | kwargs.setdefault('end', '\n')
412 | kwargs.setdefault('sep', ' ')
413 | end = kwargs.get('end')
414 | sep = kwargs.get('sep')
415 | first = True
416 | for arg in args:
417 | if first: first = False
418 | elif sep is not None:
419 | StdIOProxy._do_write(stream, allow_set_code_page, self._coerce(streaminfo, codec, sep))
420 | StdIOProxy._do_write(stream, allow_set_code_page, self._coerce(streaminfo, codec, arg))
421 | if end is not None:
422 | StdIOProxy._do_write(stream, allow_set_code_page, self._coerce(streaminfo, codec, end))
423 | if flush: stream.flush()
424 | def inputln(self, prompt=None): return self._readln(self.streaminfos[0], self.codec, prompt)
425 | def output(self, *args, **kwargs): kwargs.setdefault('end', None); return self._writeln(self.streaminfos[1], self.codec, *args, **kwargs)
426 | def outputln(self, *args, **kwargs): return self._writeln(self.streaminfos[1], self.codec, *args, **kwargs)
427 | def error(self, *args, **kwargs): kwargs.setdefault('end', None); return self._writeln(self.streaminfos[2], self.codec, *args, **kwargs)
428 | def errorln(self, *args, **kwargs): return self._writeln(self.streaminfos[2], self.codec, *args, **kwargs)
429 |
430 | class bytes_comparable_with_unicode(bytes): # For Python 2/3 compatibility, to allow implicit conversion between strings and bytes when it is safe. (Used for strings like literals which we know be safe.)
431 | codec = codecs.lookup('ascii') # MUST be a safe encoding
432 | @classmethod
433 | def coerce(cls, other, for_output=False):
434 | return cls.codec.encode(other)[0] if not isinstance(other, bytes) else bytes_comparable_with_unicode(other) if for_output else other
435 | @classmethod
436 | def translate_if_bytes(cls, value):
437 | if value is not None and isinstance(value, bytes): value = cls(value)
438 | return value
439 | def __hash__(self): return super(bytes_comparable_with_unicode, self).__hash__() # To avoid warning
440 | def __eq__(self, other): return super(bytes_comparable_with_unicode, self).__eq__(self.coerce(other))
441 | def __ne__(self, other): return super(bytes_comparable_with_unicode, self).__ne__(self.coerce(other))
442 | def __lt__(self, other): return super(bytes_comparable_with_unicode, self).__lt__(self.coerce(other))
443 | def __gt__(self, other): return super(bytes_comparable_with_unicode, self).__gt__(self.coerce(other))
444 | def __le__(self, other): return super(bytes_comparable_with_unicode, self).__le__(self.coerce(other))
445 | def __ge__(self, other): return super(bytes_comparable_with_unicode, self).__ge__(self.coerce(other))
446 | def __getitem__(self, index): return self.coerce(super(bytes_comparable_with_unicode, self).__getitem__(index), True)
447 | def __add__(self, other): return self.coerce(super(bytes_comparable_with_unicode, self).__add__(self.coerce(other)), True)
448 | def __iadd__(self, other): return self.coerce(super(bytes_comparable_with_unicode, self).__iadd__(self.coerce(other)), True)
449 | def __radd__(self, other): return self.coerce(self.coerce(other).__add__(self), True)
450 | def find(self, other, *args): return super(bytes_comparable_with_unicode, self).find(self.coerce(other), *args)
451 | def join(self, others): return self.coerce(super(bytes_comparable_with_unicode, self).join(map(self.coerce, others)), True)
452 | def startswith(self, other): return super(bytes_comparable_with_unicode, self).startswith(self.coerce(other))
453 | def __str__(self): return self.codec.decode(self)[0]
454 | if str == bytes:
455 | __unicode__ = __str__
456 | def __str__(self): raise NotImplementedError()
457 |
458 | def wrap_bytes_comparable_with_unicode_readline(readline):
459 | def callback(*args):
460 | line = readline(*args)
461 | line = bytes_comparable_with_unicode.translate_if_bytes(line)
462 | return line
463 | return callback
464 |
465 | def main(program, *args, **kwargs): # **kwargs = dict(stdin=file, stdout=file, stderr=file); useful for callers who import this module
466 | import argparse # slow import (compiles regexes etc.), so don't import it until needed
467 | argparser = argparse.ArgumentParser(
468 | prog=os.path.basename(program),
469 | usage=None,
470 | description=None,
471 | epilog=None,
472 | parents=[],
473 | formatter_class=argparse.RawTextHelpFormatter)
474 | argparser.add_argument('-version', '--version', action='store_true', help="show SQLite version")
475 | argparser.add_argument('-batch', '--batch', action='store_true', help="force batch I/O")
476 | argparser.add_argument('-init', '--init', metavar="FILE", help="read/process named file")
477 | argparser.add_argument('filename', nargs='?', metavar="FILENAME", help="is the name of an SQLite database.\nA new database is created if the file does not previously exist.")
478 | argparser.add_argument('sql', nargs='*', metavar="SQL", help="SQL commnds to execute after opening database")
479 | argparser.add_argument('--readline', action='store', metavar="(true|false)", default="true", choices=("true", "false"), help="whether to import readline if available (default: %(default)s)")
480 | argparser.add_argument('--self-test', action='store_true', help="perform a basic self-test")
481 | argparser.add_argument('--cross-test', action='store_true', help="perform a basic test against the official executable")
482 | argparser.add_argument('--unicode-stdio', action='store', metavar="(true|false)", default="true", choices=("true", "false"), help="whether to enable Unicode wrapper for standard I/O (default: %(default)s)")
483 | argparser.add_argument('--console', action='store', metavar="(true|false)", default="true", choices=("true", "false"), help="whether to auto-detect and use console window APIs (default: %(default)s)")
484 | argparser.add_argument('--encoding', default=ENCODING, help="the default encoding to use (default: %(default)s)")
485 | (stdin, stdout, stderr) = (kwargs.pop('stdin', sys.stdin), kwargs.pop('stdout', sys.stdout), kwargs.pop('stderr', sys.stderr))
486 | parsed_args = argparser.parse_args(args)
487 | codec = codecs.lookup(parsed_args.encoding or argparser.get_default('encoding'))
488 | if parsed_args.self_test: self_test(codec)
489 | if parsed_args.cross_test: cross_test("sqlite3", codec)
490 | parse_escaped_strings = StringEscapeParser()
491 | if parsed_args.unicode_stdio == "true":
492 | stdin = wrap_unicode_stdio(stdin, False, codec.name)
493 | stdout = wrap_unicode_stdio(stdout, True, codec.name)
494 | stderr = wrap_unicode_stdio(stderr, True, codec.name)
495 | if parsed_args.console == "true":
496 | stdin = wrap_windows_console_io(stdin, False)
497 | stdout = wrap_windows_console_io(stdout, True)
498 | stderr = wrap_windows_console_io(stderr, True)
499 | allow_set_code_page = sys.version_info[0] < 3 and False # This is only necessary on Python 2 if we use the default I/O functions instead of bypassing to ReadConsole()/WriteConsole()
500 | stdio = StdIOProxy(stdin, stdout, stderr, codec, allow_set_code_page)
501 | db = None
502 | no_args = len(args) == 0
503 | init_sql = parsed_args.sql
504 | is_nonpipe_input = stdin.isatty() # NOT the same thing as TTY! (NUL and /dev/null are the difference)
505 | init_show_prompt = not parsed_args.batch and is_nonpipe_input
506 | if not parsed_args.batch and isatty(stdin) and (parsed_args.readline == "true" or __name__ == '__main__') and parsed_args.readline != "false":
507 | try:
508 | with warnings.catch_warnings():
509 | warnings.filterwarnings('ignore', category=DeprecationWarning)
510 | import readline
511 | except ImportError: pass
512 | if parsed_args and parsed_args.version:
513 | stdio.outputln(sqlite3.sqlite_version);
514 | else:
515 | filename = parsed_args.filename
516 | if filename is None: filename = ":memory:"
517 | db = Database(filename, isolation_level=None)
518 | def exec_script(db, filename, ignore_io_errors):
519 | try:
520 | with io.open(filename, 'r', encoding=codec.name) as f: # Assume .sql files are text -- any binary data inside them should be X'' encoded, not embedded directly
521 | for command in sql_commands(wrap_bytes_comparable_with_unicode_readline(lambda *args: (lambda s: (s) or None)(f.readline()))):
522 | result = exec_command(db, command, False and ignore_io_errors)
523 | if result is not None:
524 | return result
525 | except IOError as ex:
526 | stdio.errorln(ex)
527 | if not ignore_io_errors: return ex.errno
528 | def raise_invalid_command_error(command):
529 | if isinstance(command, bytes): command = codec.decode(command)[0]
530 | if command.startswith("."): command = command[1:]
531 | raise RuntimeError("Error: unknown command or invalid arguments: \"%s\". Enter \".help\" for help" % (command.rstrip().replace("\\", "\\\\").replace("\"", "\\\""),))
532 | def exec_command(db, command, ignore_io_errors):
533 | results = None
534 | query = None
535 | query_parameters = {}
536 | try:
537 | if command.startswith("."):
538 | args = list(parse_escaped_strings(command))
539 | if args[0] in (".quit", ".exit"):
540 | return 0
541 | elif args[0] == ".help":
542 | stdio.error("""
543 | .cd DIRECTORY Change the working directory to DIRECTORY
544 | .dump Dump the database in an SQL text format
545 | .exit Exit this program
546 | .help Show this message
547 | .open FILE Close existing database and reopen FILE
548 | .print STRING... Print literal STRING
549 | .quit Exit this program
550 | .read FILENAME Execute SQL in FILENAME
551 | .schema ?PATTERN? Show the CREATE statements matching PATTERN
552 | .show Show the current values for various settings
553 | .tables ?TABLE? List names of tables
554 | """.lstrip())
555 | elif args[0] == ".cd":
556 | if len(args) != 2: raise_invalid_command_error(command)
557 | os.chdir(args[1])
558 | elif args[0] == ".dump":
559 | if len(args) != 1: raise_invalid_command_error(command)
560 | foreign_keys = db.cursor.execute("PRAGMA foreign_keys;").fetchone()[0]
561 | if foreign_keys in (0, "0", "off", "OFF"):
562 | stdio.outputln("PRAGMA foreign_keys=OFF;", flush=False)
563 | for line in db.connection.iterdump():
564 | stdio.outputln(line, flush=False)
565 | stdio.output()
566 | elif args[0] == ".open":
567 | if len(args) <= 1: raise_invalid_command_error(command)
568 | filename = args[-1]
569 | for option in args[+1:-1]:
570 | raise ValueError("option %s not supported" % (repr(option),))
571 | try: db.__init__(filename)
572 | except sqlite3.OperationalError as ex:
573 | ex.args = ex.args[:0] + ("Error: unable to open database \"%s\": %s" % (filename, ex.args[0]),) + ex.args[1:]
574 | raise
575 | elif args[0] == ".print":
576 | stdio.outputln(*args[1:])
577 | elif args[0] == ".read":
578 | if len(args) != 2: raise_invalid_command_error(command)
579 | exec_script(db, args[1], ignore_io_errors)
580 | elif args[0] == ".schema":
581 | if len(args) > 2: raise_invalid_command_error(command)
582 | pattern = args[1] if len(args) > 1 else None
583 | query_parameters['type'] = 'table'
584 | if pattern is not None:
585 | query_parameters['pattern'] = pattern
586 | query = "SELECT sql || ';' FROM sqlite_master WHERE type = :type" + (" AND name LIKE :pattern" if pattern is not None else "") + ";"
587 | elif args[0] == ".show":
588 | if len(args) > 2: raise_invalid_command_error(command)
589 | stdio.errorln(" filename:", db.name)
590 | elif args[0] == ".tables":
591 | if len(args) > 2: raise_invalid_command_error(command)
592 | pattern = args[1] if len(args) > 1 else None
593 | query_parameters['type'] = 'table'
594 | if pattern is not None:
595 | query_parameters['pattern'] = pattern
596 | query = "SELECT name FROM sqlite_master WHERE type = :type" + (" AND name LIKE :pattern" if pattern is not None else "") + ";"
597 | else:
598 | raise_invalid_command_error(args[0])
599 | else:
600 | query = command
601 | if query is not None:
602 | results = db.cursor.execute(query if isinstance(query, unicode) else codec.decode(query, 'surrogatereplace')[0], query_parameters)
603 | except (RuntimeError, OSError, FileNotFoundError, sqlite3.OperationalError) as ex:
604 | stdio.errorln(exception_encode(ex, codec))
605 | if results is not None:
606 | for row in results:
607 | stdio.outputln(*tuple(map(lambda item: item if item is not None else "", row)), sep="|", flush=False)
608 | stdio.output()
609 | if db:
610 | if parsed_args and parsed_args.init:
611 | if is_nonpipe_input: stdio.errorln("-- Loading resources from", parsed_args.init)
612 | exec_script(db, parsed_args.init, False)
613 | def read_stdin(index, not_in_the_middle_of_any_input, prev_line):
614 | show_prompt = init_show_prompt
615 | to_write = []
616 | if index < len(init_sql):
617 | line = init_sql[index]
618 | if not line.startswith(".") and not line.rstrip().endswith(";"):
619 | line += ";"
620 | elif index == len(init_sql) and len(init_sql) > 0:
621 | line = None
622 | else:
623 | if show_prompt:
624 | if not_in_the_middle_of_any_input:
625 | show_prompt = False
626 | if index == 0:
627 | to_write.append("SQLite version %s (adapter version %s)\nEnter \".help\" for usage hints.\n" % (sqlite3.sqlite_version, sqlite3.version))
628 | if no_args:
629 | to_write.append("Connected to a transient in-memory database.\nUse \".open FILENAME\" to reopen on a persistent database.\n")
630 | if index > 0 and not prev_line:
631 | to_write.append("\n")
632 | to_write.append("%7s " % ("sqlite%s>" % ("",) if not_in_the_middle_of_any_input else "...>",))
633 | try:
634 | line = stdio.inputln("".join(to_write))
635 | except KeyboardInterrupt:
636 | line = ""
637 | raise # just kidding, don't handle it for now...
638 | return line
639 | for command in sql_commands(wrap_bytes_comparable_with_unicode_readline(read_stdin)):
640 | result = exec_command(db, command, True)
641 | if result is not None:
642 | return result
643 | if init_show_prompt and len(init_sql) == 0:
644 | stdio.outputln()
645 |
646 | def call_program(cmdline, input_text):
647 | import subprocess
648 | return subprocess.Popen(cmdline, bufsize=0, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=False).communicate(input_text)
649 |
650 | def test_query():
651 | hexcodec = codecs.lookup('hex_codec')
652 | ascii = 'ascii'
653 | data1 = b"\xD8\xA2"
654 | data2 = b"\x01\x02\xFF\x01\xFF\xFE\xFD"
655 | values = [data1, data2]
656 | query_bytes = b'SELECT %s;' % (b", ".join(map(lambda b: b"X'%s'" % (hexcodec.encode(b)[0].upper(),), values)),)
657 | expected_bytes = b"%s\n" % (b"|".join(values),)
658 | return query_bytes, expected_bytes
659 |
660 | def cross_test(sqlite_cmdline, codec):
661 | (query_bytes, expected_bytes) = test_query()
662 | (official_output, official_error) = call_program(sqlite_cmdline, query_bytes)
663 | # We can't use os.linesep here since binaries may belong to different platforms (Win32/MinGW vs. MSYS/Cygwin vs. WSL...)
664 | official_output = official_output.replace(b"\r\n", b"\n")
665 | official_error = official_error.replace(b"\r\n", b"\n")
666 | if official_output != expected_bytes:
667 | raise sqlite3.ProgrammingError("expected bytes are wrong: official %s != expected %s" % (repr(official_output), repr(expected_bytes)))
668 | if official_error:
669 | raise sqlite3.ProgrammingError("did not expect errors from official binary")
670 |
671 | def self_test(codec):
672 | (query_bytes, expected_bytes) = test_query()
673 | if not (lambda stdin, stdout, stderr: not main(sys.argv[0], stdin=stdin, stdout=stdout, stderr=stderr) and stdout.getvalue() == expected_bytes)(io.BytesIO(query_bytes), io.BytesIO(), io.BytesIO()):
674 | raise sqlite3.ProgrammingError("byte I/O is broken")
675 | if not (lambda stdin, stdout, stderr: not main(sys.argv[0], stdin=stdin, stdout=stdout, stderr=stderr) and stdout.getvalue() == codec.decode(expected_bytes, 'surrogateescape'))(io.StringIO(query_bytes.decode(ascii)), io.StringIO(), io.StringIO()):
676 | raise sqlite3.ProgrammingError("string I/O is broken")
677 |
678 | if __name__ == '__main__':
679 | import sys
680 | exit_code = main(*sys.argv)
681 | if exit_code not in (None, 0): raise SystemExit(exit_code)
682 |
--------------------------------------------------------------------------------
/lec27/lec27-aggregation.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JacyCui/sicp-lectures/0d8e574f3886ec8807937b482e97779fb8918b8b/lec27/lec27-aggregation.pdf
--------------------------------------------------------------------------------
/lec28/lec28-database.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JacyCui/sicp-lectures/0d8e574f3886ec8807937b482e97779fb8918b8b/lec28/lec28-database.pdf
--------------------------------------------------------------------------------
/lec29/lec29-tail-calls.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JacyCui/sicp-lectures/0d8e574f3886ec8807937b482e97779fb8918b8b/lec29/lec29-tail-calls.pdf
--------------------------------------------------------------------------------
/lec30/lec30-macros.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JacyCui/sicp-lectures/0d8e574f3886ec8807937b482e97779fb8918b8b/lec30/lec30-macros.pdf
--------------------------------------------------------------------------------