├── requirements.txt ├── airtight ├── __init__.py ├── errors.py ├── tests │ ├── type_checker_test.py │ └── ast_rewriter_test.py ├── top_env.py ├── ast_rewriter.py ├── ll_ast.py ├── converter.py ├── c_generator.py └── hindley_milner_ast.py ├── .gitignore ├── requirements ├── production.txt ├── test.txt └── development.txt ├── examples ├── add4.py ├── template.py ├── sum_while.py ├── sum.py ├── m.py ├── task2.py ├── add4.py.c ├── template.py.c ├── sum.py.c ├── sum_while.py.c └── m.py.c ├── core ├── bool.c ├── number.c ├── str.py ├── collections.c ├── string.c └── list.c ├── LICENSE ├── bin └── airtight └── README.md /requirements.txt: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /airtight/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | *.a 3 | -------------------------------------------------------------------------------- /requirements/production.txt: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /requirements/test.txt: -------------------------------------------------------------------------------- 1 | nose==1.3.4 2 | -------------------------------------------------------------------------------- /requirements/development.txt: -------------------------------------------------------------------------------- 1 | nose==1.3.4 2 | -------------------------------------------------------------------------------- /examples/add4.py: -------------------------------------------------------------------------------- 1 | def add4(value: Integer) -> Integer: 2 | return value + 4 3 | 4 | print(add4(2)) 5 | -------------------------------------------------------------------------------- /core/bool.c: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | #include "bool.h" 5 | ABool* a_bool(bool value) { 6 | return value ? ATrue : AFalse; 7 | } 8 | -------------------------------------------------------------------------------- /examples/template.py: -------------------------------------------------------------------------------- 1 | @template(y, z) 2 | def a(arg: y, other: z) -> z: 3 | return other 4 | 5 | a(2, 2.2) 6 | 7 | a(2, 4) 8 | 9 | -------------------------------------------------------------------------------- /airtight/errors.py: -------------------------------------------------------------------------------- 1 | class AirtightError(Exception): 2 | pass 3 | 4 | class NotSupportedError(AirtightError): 5 | pass 6 | 7 | class TypeError(AirtightError): 8 | pass 9 | 10 | -------------------------------------------------------------------------------- /examples/sum_while.py: -------------------------------------------------------------------------------- 1 | def sum_while(n: Integer) -> Integer: 2 | result, i = 0, 0 3 | while i < n: 4 | i += 1 5 | result += i 6 | return result 7 | 8 | print(sum_while(2000)) 9 | -------------------------------------------------------------------------------- /examples/sum.py: -------------------------------------------------------------------------------- 1 | def sum(n: Integer) -> Integer: 2 | '''sum of the numbers from 0 to n inclusively''' 3 | result = 0 4 | for i in range(0, n + 1): 5 | result += i 6 | return result 7 | 8 | print(sum(2000)) 9 | -------------------------------------------------------------------------------- /examples/m.py: -------------------------------------------------------------------------------- 1 | @template(y, z) 2 | def f_map(f: y >> z, s: [y]) -> [z]: 3 | out = [] 4 | for i in s: 5 | out = append(out, f(i)) 6 | return out 7 | 8 | def nope(a: Integer) -> Integer: 9 | return a + 4 10 | 11 | print(f_map(nope, [2, 4])) 12 | 13 | -------------------------------------------------------------------------------- /airtight/tests/type_checker_test.py: -------------------------------------------------------------------------------- 1 | from nose.tools import assert_equal, assert_raises, raises 2 | from airtight.type_checker import TypeChecker 3 | import ast 4 | 5 | class TestTypeChecker: 6 | def test_accepts_ast(self): 7 | checker = TypeChecker(ast.parse('def php():pass')) 8 | assert_equal(checker.tree.body[0].name, 'php') 9 | 10 | def test_checks_a_list(self): 11 | checker = TypeChecker(ast.parse('')) 12 | assert_equal(checker.type_check(), []) 13 | 14 | -------------------------------------------------------------------------------- /airtight/tests/ast_rewriter_test.py: -------------------------------------------------------------------------------- 1 | from nose.tools import assert_equal, assert_raises, raises 2 | from airtight.ast_rewriter import ASTRewriter 3 | from airtight import errors 4 | 5 | class TestASTRewriter: 6 | @raises(errors.NotSupportedError) 7 | def test_validate_varargs(self): 8 | self.rewrite('def a(*a):pass') 9 | 10 | @raises(errors.NotSupportedError) 11 | def test_validate_kwargs(self): 12 | self.rewrite('def z(**z):pass') 13 | 14 | 15 | def rewrite(self, source): 16 | return ASTRewriter(source).rewrite() 17 | -------------------------------------------------------------------------------- /core/number.c: -------------------------------------------------------------------------------- 1 | int to_int_AString_int(AString value) { 2 | 3 | int i = 0; 4 | for(int j = 0; j <= value.length;j++) { 5 | i = 10 * i + (value.chars[i] - '0'); 6 | } 7 | return i; 8 | } 9 | 10 | int to_int_int_int(int value) { 11 | return value; 12 | } 13 | 14 | float to_float_int_float(int value) { 15 | return (float)value; 16 | } 17 | 18 | float to_float_float_float(float value) { 19 | return value; 20 | } 21 | 22 | bool a_a__lte___int_int_bool(int a, int b) { 23 | return a <= b; 24 | } 25 | 26 | bool a_a__lte___float_float_bool(float a, float b) { 27 | return a <= b; 28 | } 29 | -------------------------------------------------------------------------------- /core/str.py: -------------------------------------------------------------------------------- 1 | @native 2 | def str(value: Integer) -> String: 3 | pass 4 | 5 | def str(value: String) -> String: 6 | return value 7 | 8 | def str(value: Boolean) -> String: 9 | if value: 10 | return 'True' 11 | else: 12 | return 'False' 13 | 14 | @native 15 | def str(value: Float) -> String: 16 | pass 17 | 18 | @gen(t) 19 | def str(values: [t]) -> String: 20 | parts = map(str, values) 21 | return '[' + join(', ', parts) + ']' 22 | 23 | @gen(k, v) 24 | def str(values: {k: v}) -> String: 25 | out = [str(k) + ': ' + str(v) for k, v in values] 26 | return '{' + join(', ', out) + '}' 27 | 28 | @gen(t) 29 | def join(separator: String, values: [t]) -> String: 30 | out = '' 31 | for value in values[:-1]: 32 | out += str(value) + separator 33 | return out + str(values[-1]) 34 | 35 | 36 | -------------------------------------------------------------------------------- /core/collections.c: -------------------------------------------------------------------------------- 1 | AList_int a_range_int_int_AList_int(int from, int to) { 2 | AList_int result; 3 | result.length = to - from; 4 | result.capacity = result.length + 1; 5 | result.values = (int*)malloc(sizeof(int) * result.capacity); 6 | for(int j = 0;j < to - from;j ++) { result.values[j] = j + from; } 7 | return result; 8 | } 9 | 10 | AList_AString a_split_w_AString_AList_AString(AString from) { 11 | AList_AString z = AList_AStringOf(0); 12 | char current[256]; 13 | int k = -1; 14 | for(int j = 0;j < from.length; j++) { 15 | if (from.chars[j] != ' ' && from.chars[j] != '\n') { 16 | k++; 17 | current[k] = from.chars[j]; 18 | } 19 | else { 20 | if (k > -1) { 21 | current[k + 1] = '\0'; 22 | z = a_append_AList_AString_AString_AList_AString(z, AStringFrom(current)); 23 | } 24 | k = -1; 25 | } 26 | } 27 | if (k > -1) { 28 | current[k + 1] = '\0'; 29 | z = a_append_AList_AString_AString_AList_AString(z, AStringFrom(current)); 30 | } 31 | 32 | return z; 33 | } 34 | 35 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016-2017 Alexander Ivanov 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /examples/task2.py: -------------------------------------------------------------------------------- 1 | #def fact(n: Integer) -> Integer: 2 | # if n <= 1: 3 | # return 1 4 | # else: 5 | # result = 1 6 | # for i in range(1, n): 7 | # result = result * i 8 | # 0 9 | # return result 10 | 11 | #def is_male_name(name: String) -> Bool: 12 | # return ends_with(name, 'ss') 13 | 14 | #def is_female_name(name: String) -> Bool: 15 | # return ends_with(name, 'tta') 16 | 17 | #def chance(known_male: Integer, known_female: Integer, names: [String]) -> Integer: # Float: 18 | # male_names_count = count(names, is_male_name) 19 | # female_names_count = count(names, is_female_name) 20 | # unknown_male = male_names_count - known_male 21 | # unknown_female = female_names_count - known_female 22 | # return unknown_female 23 | #male_chance = 1.0 / to_float(fact(unknown_male)) 24 | #female_chance = 1.0 / to_float(fact(unknown_female)) 25 | #return male_chance * female_chance 26 | 27 | #z = split_w(read("2")) # not list_string 28 | z = map(lambda e: to_int(e), ["e"]) 29 | print(str(z)) 30 | #known = map(to_int, split_w(read("\n"))) 31 | #known_male = known[0] 32 | #known_female = known[1] 33 | #names = split_w(read("\n")) 34 | #z = chance(known_male, known_female, names) 35 | #print(add(str(to_int(z * 100.0)), '%')) 36 | -------------------------------------------------------------------------------- /bin/airtight: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import os, sys 4 | sys.path.append(os.path.dirname(os.path.dirname(os.path.realpath(__file__)))) 5 | import ast 6 | from airtight import hindley_milner_ast 7 | from airtight import converter 8 | from airtight import top_env 9 | from airtight import ll_ast 10 | from airtight import c_generator 11 | 12 | def main(filename, args): 13 | show_hm_ast = '--hm-ast' in args 14 | show_typed_hm_ast = '--typed-hm-ast' in args 15 | show_typed_c_ast = '--typed-c-ast' in args 16 | generate_binary = '--to-binary' in args 17 | 18 | with open(filename, 'r') as f: 19 | code = f.read() 20 | 21 | python_ast = ast.parse(code) 22 | hm_ast = converter.PythonConverter().convert(python_ast) 23 | if show_hm_ast: 24 | print(str(hm_ast)) 25 | try: 26 | hindley_milner_ast.analyse(hm_ast, top_env.TOP_ENV) 27 | if show_typed_hm_ast: 28 | print(str(hm_ast)) 29 | typed_ast = ll_ast.convert_ast(hm_ast) 30 | if show_typed_c_ast: 31 | print(typed_ast) 32 | c = c_generator.CGenerator(typed_ast).generate() 33 | with open(filename + '.c', 'w') as f: 34 | f.write(c) 35 | if generate_binary: 36 | os.system('c99 -o {0} {0}.py.c -O3'.format(filename.split('.')[0])) 37 | except (hindley_milner_ast.ParseError, hindley_milner_ast.TypeError, hindley_milner_ast.NotUnifiedError) as a: 38 | print('ERROR {a}'.format(a=a)) 39 | 40 | if __name__ == '__main__': 41 | if len(sys.argv) == 1: 42 | print('bin/airtight (generates .c)') 43 | print('bin/airtight --to-binary (generates .c and compiles it with c99)') 44 | print('display options: --hm-ast --typed-hm-ast --typed-c-ast') 45 | else: 46 | args = sys.argv[2:] 47 | main(sys.argv[1], args) 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /core/string.c: -------------------------------------------------------------------------------- 1 | typedef struct AString { 2 | char* chars; 3 | size_t length; 4 | size_t capacity; 5 | } AString; 6 | 7 | AString AStringFrom(char* s) { 8 | AString string; 9 | string.capacity = strlen(s) + 1; 10 | string.chars = (char*)malloc(sizeof(char) * string.capacity); 11 | string.length = string.capacity - 1; 12 | for(int j = 0;j < string.length;j ++) { string.chars[j] = s[j]; } 13 | string.chars[string.length] = '\0'; 14 | return string; 15 | } 16 | 17 | AString a_add_AString_AString(AString z, AString a) { 18 | if (a.length + z.length + 1 >= z.capacity) { 19 | z.capacity *= 2; 20 | z.chars = (char*)(realloc(z.chars, sizeof(char) * z.capacity)); 21 | } 22 | for(int j = 0;j < a.length;j ++) { z.chars[z.length + j] = a.chars[j]; } 23 | z.chars[z.length + a.length] = '\0'; 24 | z.length += a.length; 25 | 26 | return z; 27 | } 28 | int a_length_AString_int(AString string) { 29 | return string.length; 30 | } 31 | 32 | AString a_prints_AString_AString(AString string) { 33 | printf("%s\n", string.chars); 34 | return string; 35 | } 36 | 37 | AString a_str_int_AString(int value) { 38 | char buffer[22]; 39 | snprintf(buffer, 22, "%d", value); 40 | AString z = AStringFrom(buffer); 41 | return z; 42 | } 43 | 44 | AString a_str_AString_AString(AString string) { 45 | return string; 46 | } 47 | 48 | AString a_print_int_AString(int value) { 49 | return a_prints_AString_AString(a_str_int_AString(value)); 50 | } 51 | 52 | AString a_print_AString_AString(AString string) { 53 | return a_prints_AString_AString(string); 54 | } 55 | 56 | AString a_read_AString_AString(AString z) { 57 | char buffer[256]; 58 | int count = scanf("%s\n", buffer); 59 | AString z2 = AStringFrom(buffer); 60 | return z2; 61 | } 62 | 63 | bool a_ends_wita_AString_AString_bool(AString z, AString with) { 64 | if (z.length < with.length) { return false; } 65 | for(int j = 0; j < with.length; j++) { 66 | if(z.chars[z.length - with.length + j] != with.chars[j]) { 67 | return false; 68 | } 69 | } 70 | return true; 71 | } 72 | -------------------------------------------------------------------------------- /core/list.c: -------------------------------------------------------------------------------- 1 | typedef struct %{list_type} { 2 | %{elem_type}* values; 3 | size_t length; 4 | size_t capacity; 5 | } %{list_type}; 6 | 7 | %{list_type} a_append_%{list_type}_%{elem_type}_%{list_type}(%{list_type} list, %{elem_type} elem) { 8 | if(list.length + 1 > list.capacity) { 9 | list.capacity *= 2; 10 | list.values = (%{elem_type}*)realloc(list.values, sizeof(%{elem_type}) * list.capacity); 11 | } 12 | list.values[list.length] = elem; 13 | list.length++; 14 | return list; 15 | } 16 | 17 | %{list_type} a_pop_%{list_type}_%{elem_type}_%{list_type}(%{list_type} list, %{elem_type} elem) { 18 | list.length--; 19 | return list; 20 | } 21 | 22 | int a_length_%{list_type}_int(%{list_type} list) { 23 | return list.length; 24 | } 25 | 26 | %{elem_type} a_index_%{list_type}_int_%{elem_type}(%{list_type} list, int index) { 27 | return list.values[index]; 28 | } 29 | 30 | %{list_type} a_slice_%{list_type}_int_int_%{list_type}(%{list_type} z, int from, int to) { 31 | %{list_type} list; 32 | list.values = (%{elem_type}*)malloc(sizeof(%{elem_type}) * (to - from)); 33 | list.length = to - from; 34 | list.capacity = to - from; 35 | for(int j = from;j < to;j ++) { 36 | list.values[j - from] = z.values[j]; 37 | } 38 | return list; 39 | } 40 | 41 | %{list_type} %{list_type}Of(size_t count, ...) { 42 | va_list ap; 43 | %{list_type} list; 44 | list.values = (%{elem_type}*)(malloc(sizeof(%{elem_type}) * (count + 1))); 45 | list.length = count; 46 | list.capacity = count + 1; 47 | va_start(ap, count); 48 | for(int j = 0;j < count;j ++) { list.values[j] = va_arg(ap, %{elem_type}); } 49 | return list; 50 | } 51 | 52 | AString a_str_%{list_type}_AString(%{list_type} list) { 53 | AString z = AStringFrom("["); 54 | 55 | for(int j = 0;j < list.length - 1;j ++) { 56 | z = a_add_AString_AString(z, a_str_%{elem_type}_AString(list.values[j])); 57 | z = a_add_AString_AString(z, AStringFrom(" ")); 58 | } 59 | z = a_add_AString_AString(z, a_str_%{elem_type}_AString(list.values[list.length - 1])); 60 | z = a_add_AString_AString(z, AStringFrom("]")); 61 | return z; 62 | } 63 | 64 | AString a_print_%{list_type}_AString(%{list_type} list) { 65 | return a_prints_AString_AString(a_str_%{list_type}_AString(list)); 66 | } 67 | 68 | int a_count_%{list_type}_%{elem_type}REFbool_int(%{list_type} list, bool(*z)(%{elem_type})) { 69 | int count = 0; 70 | for(int j = 0;j < list.length;j ++) { 71 | if((*z)(list.values[j])) { 72 | count++; 73 | } 74 | } 75 | return count; 76 | } 77 | 78 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /airtight/top_env.py: -------------------------------------------------------------------------------- 1 | import airtight.hindley_milner_ast as hm_ast 2 | from airtight.hindley_milner_ast import * 3 | 4 | Z = 8 5 | vars_type = [hm_ast.TypeVariable() for i in range(Z)] 6 | 7 | TOP_ENV = { 8 | 'map': Multi_Function([Function(vars_type[1], vars_type[2]), List(vars_type[1]), List(vars_type[2])]), 9 | # map : (g -> h) -> [g] -> [h] 10 | 'a__add__': Union( 11 | Multi_Function([Integer, Integer, Integer]), 12 | # + : Integer -> Integer -> Integer 13 | Multi_Function([Float, Float, Float]) 14 | # + : Float -> Float -> Float 15 | ), 16 | 'a__substract__': Multi_Function([Integer, Integer, Integer]), 17 | # - : Integer -> Integer -> Integer 18 | 'a__divide__': Multi_Function([Integer, Integer, Integer]), 19 | # / : Integer -> Integer -> Integer 20 | 'a__mult__': Multi_Function([Integer, Integer, Integer]), 21 | # * : Integer -> Integer -> Integer 22 | 'filter': Multi_Function([Function(vars_type[3], Bool), List(vars_type[3]), List(vars_type[3])]), 23 | # filter : (g -> Bool) -> [g] -> [g] 24 | 'print' : Union( 25 | Function(Integer, String), 26 | Function(String, String), 27 | Function(List(Integer), String), 28 | Function(List(String), String)), 29 | 'prints': Function(String, String), 30 | 31 | # prints : String -> String 32 | 'range': Multi_Function([Integer, Integer, [Integer]]), 33 | # range: Integer -> Integer -> [Integer] 34 | 35 | 'str': Union( 36 | Function(Integer, String), 37 | Function(String, String), 38 | Function(List(Integer), String), 39 | Function(List(String), String)), 40 | 'a__gt__': Union( 41 | Multi_Function([Integer, Integer, Bool]), 42 | # > : Integer -> Integer -> Bool 43 | Multi_Function([Float, Float, Bool]) 44 | # > : Float -> Float -> Bool 45 | ), 46 | 'a__lt__': Union( 47 | Multi_Function([Integer, Integer, Bool]), 48 | # < : Integer -> Integer -> Bool 49 | Multi_Function([Float, Float, Bool]) 50 | # < : Float -> Float -> Bool 51 | ), 52 | 'a__lte__': Union( 53 | Multi_Function([Integer, Integer, Bool]), 54 | # <= : Integer -> Integer -> Bool 55 | Multi_Function([Float, Float, Bool]) 56 | # <= : Float -> Float -> Bool 57 | ), 58 | 'a__index__': Multi_Function([List(vars_type[4]), Integer, vars_type[4]]), 59 | # [] : [h] -> Integer -> h 60 | 'a__slice__': Multi_Function([List(vars_type[5]), Integer, Integer, vars_type[5]]), 61 | # [] : [h] -> Integer -> Integer -> [h] 62 | 'add': Multi_Function([String, String, String]), 63 | # add: String -> String -> String 64 | 'to_int': Union( 65 | Function(String, Integer), 66 | Function(Integer, Integer)), 67 | # to_int: String -> Integer | Integer -> Integer 68 | 'to_float': Union( 69 | Function(Integer, Float), 70 | Function(Float, Float)), 71 | # to_float: Integer -> Float | Float -> Float, 72 | 'read': Function(String, String), 73 | # read: String -> String 74 | 'ends_with': Multi_Function([String, String, Bool]), 75 | # ends_with: String -> String -> Bool 76 | 'split_w': Function(String, List(String)), 77 | # split_w: String -> [String] 78 | 'count': Multi_Function([List(vars_type[7]), Function(vars_type[7], Bool), Integer]), 79 | # count: [h] -> (h -> Bool) -> Integer 80 | 'append': Multi_Function([List(vars_type[6]), vars_type[6], List(vars_type[6])]) 81 | # append : [h] -> h -> [h] 82 | } 83 | 84 | 85 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # airtight 2 | 3 | an experimental frankenstein 4 | 5 | what if guido was a type theory fan? 6 | what if we went to an alternate dimension, where python starts like a typed functional language, 7 | instead of a class-based oop one? 8 | 9 | a python-like language with hindley-milner-like type system, which is compiled to c. 10 | 11 | With the help of 12 | --------- 13 | code by [Robert Smallshire](http://smallshire.org.uk/sufficientlysmall/2010/04/11/a-hindley-milner-type-inference-implementation-in-python/comment-page-1/) 14 | (see below) 15 | 16 | Language 17 | --------------- 18 | 19 | We use the new syntax for annotations in Python3 20 | 21 | You can add optional annotations to args and returns: 22 | 23 | ```python 24 | @template(y, z) 25 | def f_map(f: y >> z, s: [y]) -> [z]: 26 | out = [] 27 | for i in s: 28 | out = append(out, f(i)) 29 | return out 30 | 31 | def nope(a: Integer) -> Integer: 32 | return a + 4 33 | 34 | print(f_map(nope, [2, 4])) 35 | ``` 36 | 37 | ```python 38 | def sum(n: Integer) -> Integer: 39 | '''sum of the numbers from 0 to n inclusively''' 40 | result = 0 41 | for i in range(0, n + 1): 42 | result += i 43 | return result 44 | 45 | print(sum(2000)) 46 | ``` 47 | 48 | owever we just reuse Python3's syntax. We try to preserve the spirit and semantics 49 | in many cases, but look at it like a different language. 50 | 51 | you can install it and use it like that 52 | ```bash 53 | git clone https://github.com/alehander42/airtight.git 54 | cd airtight 55 | bin/airtight # and 56 | ``` 57 | 58 | Implementation 59 | --------------- 60 | 61 | * airtight code -> python3 ast (using python3 ast module) 62 | * python3 ast -> hindley milner ast (easier for type inference, taken from [Robert Smallshire](http://smallshire.org.uk/sufficientlysmall/2010/04/11/a-hindley-milner-type-inference-implementation-in-python/comment-page-1/), with some more Airtight-specific stuff 63 | * hindley milner ast -> hindley milner typed ast (running type inference and annotating the tree with types) 64 | * hindley milner typed ast -> lower level python-like typed ast (converting back to python/c like multiarg functions and assignment nodes) 65 | * lower level python-like typed ast -> c code (generate c code recursively for each node and based on the core library) 66 | 67 | The core library contains some predefined functions, accessible from airtight and 68 | some template files which implement generic airtight functions in c 69 | (for example `core/list.c` has placeholders like `%{list_type}` and `%{elem_type}` and 70 | different copies of the functions are generated for each `%{elem_type}` in a program}) 71 | 72 | options for the compiler: 73 | 74 | ```bash 75 | bin/airtight filename.py # compiles it to filename.py.c 76 | bin/airtight filename.py --to-binary # compiles it to filename.py.c and then invokes 77 | # c99 and generates a binary filename 78 | bin/airtight filename.py --hm-ast # show the hindley milner ast 79 | bin/airtight filename.py --typed-hm-ast # show the typed hindley milner ast 80 | bin/airtight filename.py --typed-c-ast # show the lower level typed ast 81 | ``` 82 | 83 | the resulting c code is quite amusing: 84 | ```c 85 | a_print_AList_AString_AString( 86 | a_f_map_intREFAString_AList_int_AList_AString( 87 | &a_wtf_int_AString, 88 | AList_intOf(2, 2, 4))); 89 | ``` 90 | yeah, you need a shower now, doncha 91 | 92 | syntactic sugar : `[Integer]` for list types, `y >> z` for function types, 93 | `Integer | Float` for union types. we have some extensions to the hindley milner 94 | inference algorithm, so the current type system is probably unsound and weird, but 95 | you have haskell/idris for that 96 | 97 | airtight is just a little kid now and its quite buggy and a proof of concept 98 | however if you're interested, you have some ideas/questions, or you just wanna 99 | hack on it, feel free to use the issues tab here 100 | 101 | Alexander Ivanov 102 | -------------------------------------------------------------------------------- /airtight/ast_rewriter.py: -------------------------------------------------------------------------------- 1 | import ast 2 | from . import errors 3 | 4 | class ASTRewriter: 5 | ''' 6 | Rewrites builtin ast to airtight ast 7 | Checks for unsupported python syntax 8 | ''' 9 | 10 | NOT_SUPPORTED = set([ 11 | ast.With, 12 | ast.ListComp]) 13 | 14 | OPERATOR_MAGIC_FUNCTIONS = { 15 | ast.Add: '__add__', 16 | ast.Sub: '__substract__', 17 | ast.Mult: '__multiply__', 18 | ast.FloorDiv: '__real_divide__', 19 | ast.Div: '__divide__', 20 | ast.Mod: '__percent__', 21 | ast.Pow: '__power__', 22 | ast.Eq: '__equals__', 23 | ast.NotEq: '__not_equals__', 24 | ast.Lt: '__lt__', 25 | ast.LtE: '__lte__', 26 | ast.Gt: '__gt__', 27 | ast.GtE: '__gte', 28 | ast.And: '__and__', 29 | ast.Or: '__or__', 30 | ast.Not: '__not__' 31 | } 32 | def __init__(self, source): 33 | self.source = source 34 | 35 | def rewrite(self): 36 | tree = ast.parse(self.source) 37 | return self._rewrite_node(tree) 38 | 39 | def _rewrite_node(self, node): 40 | if isinstance(node, (list, tuple)): 41 | return list(map(self._rewrite_node, node)) 42 | elif hasattr(self, '_rewrite_' + type(node).__name__.lower()): 43 | node = getattr(self, '_rewrite_' + type(node).__name__.lower())(node) 44 | elif not isinstance(node, ast.AST): 45 | return node 46 | elif type(node) in self.NOT_SUPPORTED: 47 | raise errors.NotSupportedError( 48 | '%s is not supported in airtight' % type(node).__name__) 49 | 50 | for c, child in node.__dict__.items(): 51 | setattr(node, c, self._rewrite_node(child)) 52 | return node 53 | 54 | def _rewrite_arguments(self, node): 55 | if node.vararg: 56 | raise errors.NotSupportedError( 57 | "vararg *%s is not supported in airtight" % node.vararg.arg) 58 | elif node.kwarg: 59 | raise errors.NotSupportedError( 60 | "kwarg **%s is not supported in airtight" % node.kwarg.arg) 61 | else: 62 | return node 63 | 64 | def _rewrite_unaryop(self, node): 65 | ''' 66 | rewrite not var as __not__(var) etc 67 | ''' 68 | return ast.Call( 69 | func=ast.Name(self.OPERATOR_MAGIC_FUNCTIONS[type(node.op)], None), 70 | args=[node.operand], 71 | keywords=[], 72 | starargs=None, 73 | kwargs=None) 74 | 75 | def _rewrite_binop(self, node): 76 | ''' 77 | rewrite a + b as __add__(a, b) etc 78 | ''' 79 | return ast.Call( 80 | func=ast.Name(self.OPERATOR_MAGIC_FUNCTIONS[type(node.op)], None), 81 | args=[node.left, node.right], 82 | keywords=[], 83 | starargs=None, 84 | kwargs=None) 85 | 86 | def _rewrite_compare(self, node): 87 | ''' 88 | rewrite a == b as __eq__(a, b) etc 89 | ''' 90 | if len(node.comparators) > 1: 91 | raise errors.NotSupportedError( 92 | "compare is supported only for 2 elements") 93 | return ast.Call( 94 | func=ast.Name(self.OPERATOR_MAGIC_FUNCTIONS[type(node.ops[0])], None), 95 | args=[node.left, node.comparators[0]], 96 | keywords=[], 97 | starargs=None, 98 | kwargs=None) 99 | 100 | def _rewrite_classdef(self, node): 101 | ''' 102 | rewrite compile-time decorators to annotations 103 | 104 | is_native 105 | ''' 106 | node.a_typeclass = [] 107 | if node.decorator_list: 108 | decorators = [] 109 | for m in node.decorator_list: 110 | if isinstance(m, ast.Call) and m.func.id == 'typeclass': 111 | r = [arg.id for arg in m.args] 112 | node.a_typeclass += r 113 | else: 114 | decorators.append(m) 115 | node.decorator_list = decorators 116 | return node 117 | 118 | def _rewrite_functiondef(self, node): 119 | ''' 120 | rewrite compile-time decorators to annotations 121 | 122 | native a_native, a flag specifying if the body should be visited or 123 | the function has a native implementation 124 | template a_template, a list of tuples with the type and its typeclass 125 | 126 | ''' 127 | node.a_native = False 128 | node.a_template = [] 129 | if node.decorator_list: 130 | decorators = [] 131 | for m in node.decorator_list: 132 | if isinstance(m, ast.Name) and m.id == 'native': 133 | node.a_native = True 134 | elif isinstance(m, ast.Call) and m.func.id == 'template': 135 | for child in m.args: 136 | typeclass, q = child.comparators[0].func.id, [ 137 | arg.id for arg in child.comparators[0].args] 138 | node.a_template.append((child.left.id, [typeclass, q])) 139 | else: 140 | decorators.append(m) 141 | node.decorator_list = decorators 142 | return node 143 | 144 | def _rewrite_nameconstant(self, node): 145 | if node.id == 'None': 146 | raise self.NotSupportedError('None isn\'t used in airtight') 147 | return node 148 | -------------------------------------------------------------------------------- /airtight/ll_ast.py: -------------------------------------------------------------------------------- 1 | from airtight.hindley_milner_ast import * 2 | 3 | def convert_ast(hm_ast): 4 | '''converts hindley-milner typed scheme-like ast to a 5 | lower level ast 6 | ''' 7 | return LLAstGenerator(hm_ast).generate() 8 | 9 | OPS = { 10 | '_add__': '+', 11 | '_substract__': '-', 12 | '_mult__': '*', 13 | '_divide__': '/', 14 | '_gt__': '>', 15 | '_lt__': '<', 16 | } 17 | 18 | class LLAstGenerator: 19 | def __init__(self, hm_ast): 20 | self.hm_ast = hm_ast 21 | 22 | def generate(self): 23 | node = self.generate_node(self.hm_ast) 24 | if isinstance(node, list): 25 | return LLAst(type='source',expressions=node, a_type=node[-1].a_type) 26 | else: 27 | return node 28 | 29 | def generate_node(self, node): 30 | return getattr(self, 'generate_%s' % str(type(node).__name__).lower())(node) 31 | 32 | def generate_body(self, node): 33 | other = self.generate_node(node.other) 34 | if isinstance(other, list): 35 | return [self.generate_node(node.expression)] + other 36 | else: 37 | return [self.generate_node(node.expression), other] 38 | 39 | 40 | def generate_let(self, node): 41 | if isinstance(node.defn, Lambda): 42 | let_ast = LLAst( 43 | type = 'method', 44 | label = LLAst(type='ident', label=node.v, a_type=node.defn.a_type), 45 | body = self.generate_lambda(node.defn), 46 | a_type = node.defn.a_type, 47 | a_native = node.a_native, 48 | a_vars = node.a_vars, 49 | a_return_type = node.defn.a_return_type) 50 | else: 51 | let_ast = LLAst( 52 | type = 'assignment', 53 | label = LLAst(type='ident', label=node.v, a_type=node.defn.a_type), 54 | right = self.generate_node(node.defn), 55 | a_type = node.a_type) 56 | body_ast = self.generate_node(node.body) 57 | return [let_ast] + body_ast if isinstance(body_ast, list) else [let_ast, body_ast] 58 | 59 | def generate_lambda(self, node): 60 | if isinstance(node.body, Lambda): 61 | lambda_ast = self.generate_lambda(node.body) 62 | else: 63 | lambda_ast = LLAst( 64 | type = 'lambda', 65 | args = [], 66 | body = self.generate_node(node.body), 67 | a_type = node.a_type, 68 | a_return_type = node.a_return_type) 69 | lambda_ast.args = [LLAst(type='ident', label=node.v, a_type=node.a_type.types[0])] + lambda_ast.args 70 | return lambda_ast 71 | 72 | def generate_ident(self, node): 73 | type_label = str(type(node).__name__) 74 | if type_label[:2] == 'an': 75 | type_label = type_label[2:] 76 | elif type_label[0] == 'a': 77 | type_label = type_label[1:] 78 | return LLAst(type=type_label.lower(), label=node.name, a_type=node.a_type) 79 | 80 | generate_aninteger = generate_afloat = generate_aboolean = generate_astring = generate_ident 81 | 82 | def generate_apply(self, node): 83 | if isinstance(node.fn, Apply): 84 | if self.is_numeric_binop(node.fn.fn): 85 | apply_ast = LLAst( 86 | type='binop', 87 | op=OPS[node.fn.fn.name[2:]], 88 | left=self.generate_node(node.fn.arg), 89 | right=self.generate_node(node.arg)) 90 | else: 91 | apply_ast = self.generate_apply(node.fn) 92 | apply_ast.args.append(self.generate_node(node.arg)) 93 | else: 94 | apply_ast = LLAst(type='apply', function=self.generate_node(node.fn), args=[self.generate_node(node.arg)], a_type=node.a_type, _special=False) 95 | return apply_ast 96 | 97 | def generate_alist(self, node): 98 | return LLAst(type='list', items=[self.generate_node(e) for e in node.items], a_type=node.a_type) 99 | 100 | def generate_if(self, node): 101 | return LLAst(type='if', test=self.generate_node(node.test), body=self.generate_node(node.body), orelse=self.generate_node(node.orelse), a_type=node.a_type) 102 | 103 | def generate_for(self, node): 104 | if isinstance(node.iter, Apply) and\ 105 | isinstance(node.iter.fn, Apply) and\ 106 | hasattr(node.iter.fn.fn, 'name') and node.iter.fn.fn.name == 'range': 107 | return LLAst( 108 | type='for_range', 109 | target=self.generate_node(node.target), 110 | start=self.generate_node(node.iter.fn.arg), 111 | end=self.generate_node(node.iter.arg), 112 | body=self.generate_cons(node.body), 113 | a_type=node.a_type) 114 | else: 115 | return LLAst(type='for', target=self.generate_node(node.target), iter=self.generate_node(node.iter), body=self.generate_cons(node.body), a_type=node.a_type) 116 | 117 | def generate_while(self, node): 118 | return LLAst(type='while', test=self.generate_node(node.test), body=self.generate_cons(node.body), a_type=node.a_type) 119 | 120 | def generate_cons(self, node): 121 | body = self.generate_node(node) 122 | if isinstance(body, list) and len(body) > 1 and body[-1].type == 'ident': 123 | return body[:-1] 124 | else: 125 | return body 126 | 127 | def is_numeric_binop(self, node): 128 | return hasattr(node, 'name') and node.name[:2] == 'a_' and node.name[-2:] == '__' and node.name[2:] in OPS 129 | 130 | class LLAst: 131 | def __init__(self, **kwargs): 132 | for label, arg in kwargs.items(): 133 | setattr(self, label, arg) 134 | self.data = kwargs 135 | 136 | def __str__(self): 137 | return self.render(0) 138 | 139 | def render(self, depth=0): 140 | if self.type == 'method': 141 | return '{offset}method {label}({args}) @ {a_type}\n{body}'.format( 142 | offset=self.offset(depth), 143 | label=str(self.label), 144 | args=', '.join(str(arg) for arg in self.body.args), 145 | a_type=self.a_type, 146 | body=str(self.body.body)) 147 | elif self.type == 'assignment': 148 | return '{offset}assignment[{label} : {value}] @ {a_type}'.format( 149 | offset=self.offset(depth), 150 | label=str(self.label), 151 | value=str(self.right), 152 | a_type=self.a_type) 153 | elif len(self.data) == 3 and 'label' in self.data: 154 | return '{offset}{type}[{label}] @ {a_type}'.format( 155 | offset=self.offset(depth), 156 | type=self.type, 157 | label=str(self.label), 158 | a_type=self.a_type) 159 | elif self.type == 'source': 160 | return '{offset}source: @ {a_type}\n{body}'.format( 161 | offset=self.offset(depth), 162 | a_type=self.a_type, 163 | body='\n'.join(s.render(depth + 1) for s in self.expressions)) 164 | elif self.type == 'apply': 165 | return '{offset}call {label}({args}) @ {a_type}'.format( 166 | offset=self.offset(depth), 167 | label=str(self.function), 168 | args=', '.join(str(arg) for arg in self.args), 169 | a_type=self.a_type) 170 | else: 171 | return '{offset}{label}: @{a_type}\n'.format(offset=self.offset(depth), label=self.type, a_type=self.a_type) +\ 172 | '\n'.join('{offset}{label} = {value}'.format(offset=self.offset(depth + 1), label=label, value=self.render_value(value)) for label, value in self.data.items() if label != 'type') 173 | 174 | def render_value(self, value, depth=0): 175 | if isinstance(value, LLAst): 176 | return value.render(depth + 1) 177 | elif isinstance(value, list): 178 | return '[' + ', '.join(self.render_value(v, depth + 1) for v in value) + ']' 179 | else: 180 | return str(value) 181 | 182 | def offset(self, depth): 183 | return ' ' * depth 184 | -------------------------------------------------------------------------------- /examples/add4.py.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | typedef struct AString { 10 | char* chars; 11 | size_t length; 12 | size_t capacity; 13 | } AString; 14 | 15 | AString AStringFrom(char* s) { 16 | AString string; 17 | string.capacity = strlen(s) + 1; 18 | string.chars = (char*)malloc(sizeof(char) * string.capacity); 19 | string.length = string.capacity - 1; 20 | for(int j = 0;j < string.length;j ++) { string.chars[j] = s[j]; } 21 | string.chars[string.length] = '\0'; 22 | return string; 23 | } 24 | 25 | AString a_add_AString_AString(AString z, AString a) { 26 | if (a.length + z.length + 1 >= z.capacity) { 27 | z.capacity *= 2; 28 | z.chars = (char*)(realloc(z.chars, sizeof(char) * z.capacity)); 29 | } 30 | for(int j = 0;j < a.length;j ++) { z.chars[z.length + j] = a.chars[j]; } 31 | z.chars[z.length + a.length] = '\0'; 32 | z.length += a.length; 33 | 34 | return z; 35 | } 36 | int a_length_AString_int(AString string) { 37 | return string.length; 38 | } 39 | 40 | AString a_prints_AString_AString(AString string) { 41 | printf("%s\n", string.chars); 42 | return string; 43 | } 44 | 45 | AString a_str_int_AString(int value) { 46 | char buffer[22]; 47 | snprintf(buffer, 22, "%d", value); 48 | AString z = AStringFrom(buffer); 49 | return z; 50 | } 51 | 52 | AString a_str_AString_AString(AString string) { 53 | return string; 54 | } 55 | 56 | AString a_print_int_AString(int value) { 57 | return a_prints_AString_AString(a_str_int_AString(value)); 58 | } 59 | 60 | AString a_print_AString_AString(AString string) { 61 | return a_prints_AString_AString(string); 62 | } 63 | 64 | AString a_read_AString_AString(AString z) { 65 | char buffer[256]; 66 | int count = scanf("%s\n", buffer); 67 | AString z2 = AStringFrom(buffer); 68 | return z2; 69 | } 70 | 71 | bool a_ends_wita_AString_AString_bool(AString z, AString with) { 72 | if (z.length < with.length) { return false; } 73 | for(int j = 0; j < with.length; j++) { 74 | if(z.chars[z.length - with.length + j] != with.chars[j]) { 75 | return false; 76 | } 77 | } 78 | return true; 79 | } 80 | 81 | 82 | int to_int_AString_int(AString value) { 83 | 84 | int i = 0; 85 | for(int j = 0; j <= value.length;j++) { 86 | i = 10 * i + (value.chars[i] - '0'); 87 | } 88 | return i; 89 | } 90 | 91 | int to_int_int_int(int value) { 92 | return value; 93 | } 94 | 95 | float to_float_int_float(int value) { 96 | return (float)value; 97 | } 98 | 99 | float to_float_float_float(float value) { 100 | return value; 101 | } 102 | 103 | bool a_a__lte___int_int_bool(int a, int b) { 104 | return a <= b; 105 | } 106 | 107 | bool a_a__lte___float_float_bool(float a, float b) { 108 | return a <= b; 109 | } 110 | 111 | 112 | typedef struct AList_int { 113 | int* values; 114 | size_t length; 115 | size_t capacity; 116 | } AList_int; 117 | 118 | AList_int a_append_AList_int_int_AList_int(AList_int list, int elem) { 119 | if(list.length + 1 > list.capacity) { 120 | list.capacity *= 2; 121 | list.values = (int*)realloc(list.values, sizeof(int) * list.capacity); 122 | } 123 | list.values[list.length] = elem; 124 | list.length++; 125 | return list; 126 | } 127 | 128 | AList_int a_pop_AList_int_int_AList_int(AList_int list, int elem) { 129 | list.length--; 130 | return list; 131 | } 132 | 133 | int a_length_AList_int_int(AList_int list) { 134 | return list.length; 135 | } 136 | 137 | int a_index_AList_int_int_int(AList_int list, int index) { 138 | return list.values[index]; 139 | } 140 | 141 | AList_int a_slice_AList_int_int_int_AList_int(AList_int z, int from, int to) { 142 | AList_int list; 143 | list.values = (int*)malloc(sizeof(int) * (to - from)); 144 | list.length = to - from; 145 | list.capacity = to - from; 146 | for(int j = from;j < to;j ++) { 147 | list.values[j - from] = z.values[j]; 148 | } 149 | return list; 150 | } 151 | 152 | AList_int AList_intOf(size_t count, ...) { 153 | va_list ap; 154 | AList_int list; 155 | list.values = (int*)(malloc(sizeof(int) * (count + 1))); 156 | list.length = count; 157 | list.capacity = count + 1; 158 | va_start(ap, count); 159 | for(int j = 0;j < count;j ++) { list.values[j] = va_arg(ap, int); } 160 | return list; 161 | } 162 | 163 | AString a_str_AList_int_AString(AList_int list) { 164 | AString z = AStringFrom("["); 165 | 166 | for(int j = 0;j < list.length - 1;j ++) { 167 | z = a_add_AString_AString(z, a_str_int_AString(list.values[j])); 168 | z = a_add_AString_AString(z, AStringFrom(" ")); 169 | } 170 | z = a_add_AString_AString(z, a_str_int_AString(list.values[list.length - 1])); 171 | z = a_add_AString_AString(z, AStringFrom("]")); 172 | return z; 173 | } 174 | 175 | AString a_print_AList_int_AString(AList_int list) { 176 | return a_prints_AString_AString(a_str_AList_int_AString(list)); 177 | } 178 | 179 | int a_count_AList_int_intREFbool_int(AList_int list, bool(*z)(int)) { 180 | int count = 0; 181 | for(int j = 0;j < list.length;j ++) { 182 | if((*z)(list.values[j])) { 183 | count++; 184 | } 185 | } 186 | return count; 187 | } 188 | 189 | 190 | 191 | 192 | 193 | typedef struct AList_AString { 194 | AString* values; 195 | size_t length; 196 | size_t capacity; 197 | } AList_AString; 198 | 199 | AList_AString a_append_AList_AString_AString_AList_AString(AList_AString list, AString elem) { 200 | if(list.length + 1 > list.capacity) { 201 | list.capacity *= 2; 202 | list.values = (AString*)realloc(list.values, sizeof(AString) * list.capacity); 203 | } 204 | list.values[list.length] = elem; 205 | list.length++; 206 | return list; 207 | } 208 | 209 | AList_AString a_pop_AList_AString_AString_AList_AString(AList_AString list, AString elem) { 210 | list.length--; 211 | return list; 212 | } 213 | 214 | int a_length_AList_AString_int(AList_AString list) { 215 | return list.length; 216 | } 217 | 218 | AString a_index_AList_AString_int_AString(AList_AString list, int index) { 219 | return list.values[index]; 220 | } 221 | 222 | AList_AString a_slice_AList_AString_int_int_AList_AString(AList_AString z, int from, int to) { 223 | AList_AString list; 224 | list.values = (AString*)malloc(sizeof(AString) * (to - from)); 225 | list.length = to - from; 226 | list.capacity = to - from; 227 | for(int j = from;j < to;j ++) { 228 | list.values[j - from] = z.values[j]; 229 | } 230 | return list; 231 | } 232 | 233 | AList_AString AList_AStringOf(size_t count, ...) { 234 | va_list ap; 235 | AList_AString list; 236 | list.values = (AString*)(malloc(sizeof(AString) * (count + 1))); 237 | list.length = count; 238 | list.capacity = count + 1; 239 | va_start(ap, count); 240 | for(int j = 0;j < count;j ++) { list.values[j] = va_arg(ap, AString); } 241 | return list; 242 | } 243 | 244 | AString a_str_AList_AString_AString(AList_AString list) { 245 | AString z = AStringFrom("["); 246 | 247 | for(int j = 0;j < list.length - 1;j ++) { 248 | z = a_add_AString_AString(z, a_str_AString_AString(list.values[j])); 249 | z = a_add_AString_AString(z, AStringFrom(" ")); 250 | } 251 | z = a_add_AString_AString(z, a_str_AString_AString(list.values[list.length - 1])); 252 | z = a_add_AString_AString(z, AStringFrom("]")); 253 | return z; 254 | } 255 | 256 | AString a_print_AList_AString_AString(AList_AString list) { 257 | return a_prints_AString_AString(a_str_AList_AString_AString(list)); 258 | } 259 | 260 | int a_count_AList_AString_AStringREFbool_int(AList_AString list, bool(*z)(AString)) { 261 | int count = 0; 262 | for(int j = 0;j < list.length;j ++) { 263 | if((*z)(list.values[j])) { 264 | count++; 265 | } 266 | } 267 | return count; 268 | } 269 | 270 | 271 | 272 | 273 | 274 | 275 | AList_int a_range_int_int_AList_int(int from, int to) { 276 | AList_int result; 277 | result.length = to - from; 278 | result.capacity = result.length + 1; 279 | result.values = (int*)malloc(sizeof(int) * result.capacity); 280 | for(int j = 0;j < to - from;j ++) { result.values[j] = j + from; } 281 | return result; 282 | } 283 | 284 | AList_AString a_split_w_AString_AList_AString(AString from) { 285 | AList_AString z = AList_AStringOf(0); 286 | char current[256]; 287 | int k = -1; 288 | for(int j = 0;j < from.length; j++) { 289 | if (from.chars[j] != ' ' && from.chars[j] != '\n') { 290 | k++; 291 | current[k] = from.chars[j]; 292 | } 293 | else { 294 | if (k > -1) { 295 | current[k + 1] = '\0'; 296 | z = a_append_AList_AString_AString_AList_AString(z, AStringFrom(current)); 297 | } 298 | k = -1; 299 | } 300 | } 301 | if (k > -1) { 302 | current[k + 1] = '\0'; 303 | z = a_append_AList_AString_AString_AList_AString(z, AStringFrom(current)); 304 | } 305 | 306 | return z; 307 | } 308 | 309 | 310 | int a_add4_int_int(int a_value){ 311 | return (a_value + 4); 312 | } 313 | 314 | int main(){ 315 | a_print_int_AString(a_add4_int_int(2)); 316 | return 0; 317 | } 318 | -------------------------------------------------------------------------------- /examples/template.py.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | typedef struct AString { 10 | char* chars; 11 | size_t length; 12 | size_t capacity; 13 | } AString; 14 | 15 | AString AStringFrom(char* s) { 16 | AString string; 17 | string.capacity = strlen(s) + 1; 18 | string.chars = (char*)malloc(sizeof(char) * string.capacity); 19 | string.length = string.capacity - 1; 20 | for(int j = 0;j < string.length;j ++) { string.chars[j] = s[j]; } 21 | string.chars[string.length] = '\0'; 22 | return string; 23 | } 24 | 25 | AString a_add_AString_AString(AString z, AString a) { 26 | if (a.length + z.length + 1 >= z.capacity) { 27 | z.capacity *= 2; 28 | z.chars = (char*)(realloc(z.chars, sizeof(char) * z.capacity)); 29 | } 30 | for(int j = 0;j < a.length;j ++) { z.chars[z.length + j] = a.chars[j]; } 31 | z.chars[z.length + a.length] = '\0'; 32 | z.length += a.length; 33 | 34 | return z; 35 | } 36 | int a_length_AString_int(AString string) { 37 | return string.length; 38 | } 39 | 40 | AString a_prints_AString_AString(AString string) { 41 | printf("%s\n", string.chars); 42 | return string; 43 | } 44 | 45 | AString a_str_int_AString(int value) { 46 | char buffer[22]; 47 | snprintf(buffer, 22, "%d", value); 48 | AString z = AStringFrom(buffer); 49 | return z; 50 | } 51 | 52 | AString a_str_AString_AString(AString string) { 53 | return string; 54 | } 55 | 56 | AString a_print_int_AString(int value) { 57 | return a_prints_AString_AString(a_str_int_AString(value)); 58 | } 59 | 60 | AString a_print_AString_AString(AString string) { 61 | return a_prints_AString_AString(string); 62 | } 63 | 64 | AString a_read_AString_AString(AString z) { 65 | char buffer[256]; 66 | int count = scanf("%s\n", buffer); 67 | AString z2 = AStringFrom(buffer); 68 | return z2; 69 | } 70 | 71 | bool a_ends_wita_AString_AString_bool(AString z, AString with) { 72 | if (z.length < with.length) { return false; } 73 | for(int j = 0; j < with.length; j++) { 74 | if(z.chars[z.length - with.length + j] != with.chars[j]) { 75 | return false; 76 | } 77 | } 78 | return true; 79 | } 80 | 81 | 82 | int to_int_AString_int(AString value) { 83 | 84 | int i = 0; 85 | for(int j = 0; j <= value.length;j++) { 86 | i = 10 * i + (value.chars[i] - '0'); 87 | } 88 | return i; 89 | } 90 | 91 | int to_int_int_int(int value) { 92 | return value; 93 | } 94 | 95 | float to_float_int_float(int value) { 96 | return (float)value; 97 | } 98 | 99 | float to_float_float_float(float value) { 100 | return value; 101 | } 102 | 103 | bool a_a__lte___int_int_bool(int a, int b) { 104 | return a <= b; 105 | } 106 | 107 | bool a_a__lte___float_float_bool(float a, float b) { 108 | return a <= b; 109 | } 110 | 111 | 112 | typedef struct AList_AString { 113 | AString* values; 114 | size_t length; 115 | size_t capacity; 116 | } AList_AString; 117 | 118 | AList_AString a_append_AList_AString_AString_AList_AString(AList_AString list, AString elem) { 119 | if(list.length + 1 > list.capacity) { 120 | list.capacity *= 2; 121 | list.values = (AString*)realloc(list.values, sizeof(AString) * list.capacity); 122 | } 123 | list.values[list.length] = elem; 124 | list.length++; 125 | return list; 126 | } 127 | 128 | AList_AString a_pop_AList_AString_AString_AList_AString(AList_AString list, AString elem) { 129 | list.length--; 130 | return list; 131 | } 132 | 133 | int a_length_AList_AString_int(AList_AString list) { 134 | return list.length; 135 | } 136 | 137 | AString a_index_AList_AString_int_AString(AList_AString list, int index) { 138 | return list.values[index]; 139 | } 140 | 141 | AList_AString a_slice_AList_AString_int_int_AList_AString(AList_AString z, int from, int to) { 142 | AList_AString list; 143 | list.values = (AString*)malloc(sizeof(AString) * (to - from)); 144 | list.length = to - from; 145 | list.capacity = to - from; 146 | for(int j = from;j < to;j ++) { 147 | list.values[j - from] = z.values[j]; 148 | } 149 | return list; 150 | } 151 | 152 | AList_AString AList_AStringOf(size_t count, ...) { 153 | va_list ap; 154 | AList_AString list; 155 | list.values = (AString*)(malloc(sizeof(AString) * (count + 1))); 156 | list.length = count; 157 | list.capacity = count + 1; 158 | va_start(ap, count); 159 | for(int j = 0;j < count;j ++) { list.values[j] = va_arg(ap, AString); } 160 | return list; 161 | } 162 | 163 | AString a_str_AList_AString_AString(AList_AString list) { 164 | AString z = AStringFrom("["); 165 | 166 | for(int j = 0;j < list.length - 1;j ++) { 167 | z = a_add_AString_AString(z, a_str_AString_AString(list.values[j])); 168 | z = a_add_AString_AString(z, AStringFrom(" ")); 169 | } 170 | z = a_add_AString_AString(z, a_str_AString_AString(list.values[list.length - 1])); 171 | z = a_add_AString_AString(z, AStringFrom("]")); 172 | return z; 173 | } 174 | 175 | AString a_print_AList_AString_AString(AList_AString list) { 176 | return a_prints_AString_AString(a_str_AList_AString_AString(list)); 177 | } 178 | 179 | int a_count_AList_AString_AStringREFbool_int(AList_AString list, bool(*z)(AString)) { 180 | int count = 0; 181 | for(int j = 0;j < list.length;j ++) { 182 | if((*z)(list.values[j])) { 183 | count++; 184 | } 185 | } 186 | return count; 187 | } 188 | 189 | 190 | 191 | 192 | 193 | typedef struct AList_int { 194 | int* values; 195 | size_t length; 196 | size_t capacity; 197 | } AList_int; 198 | 199 | AList_int a_append_AList_int_int_AList_int(AList_int list, int elem) { 200 | if(list.length + 1 > list.capacity) { 201 | list.capacity *= 2; 202 | list.values = (int*)realloc(list.values, sizeof(int) * list.capacity); 203 | } 204 | list.values[list.length] = elem; 205 | list.length++; 206 | return list; 207 | } 208 | 209 | AList_int a_pop_AList_int_int_AList_int(AList_int list, int elem) { 210 | list.length--; 211 | return list; 212 | } 213 | 214 | int a_length_AList_int_int(AList_int list) { 215 | return list.length; 216 | } 217 | 218 | int a_index_AList_int_int_int(AList_int list, int index) { 219 | return list.values[index]; 220 | } 221 | 222 | AList_int a_slice_AList_int_int_int_AList_int(AList_int z, int from, int to) { 223 | AList_int list; 224 | list.values = (int*)malloc(sizeof(int) * (to - from)); 225 | list.length = to - from; 226 | list.capacity = to - from; 227 | for(int j = from;j < to;j ++) { 228 | list.values[j - from] = z.values[j]; 229 | } 230 | return list; 231 | } 232 | 233 | AList_int AList_intOf(size_t count, ...) { 234 | va_list ap; 235 | AList_int list; 236 | list.values = (int*)(malloc(sizeof(int) * (count + 1))); 237 | list.length = count; 238 | list.capacity = count + 1; 239 | va_start(ap, count); 240 | for(int j = 0;j < count;j ++) { list.values[j] = va_arg(ap, int); } 241 | return list; 242 | } 243 | 244 | AString a_str_AList_int_AString(AList_int list) { 245 | AString z = AStringFrom("["); 246 | 247 | for(int j = 0;j < list.length - 1;j ++) { 248 | z = a_add_AString_AString(z, a_str_int_AString(list.values[j])); 249 | z = a_add_AString_AString(z, AStringFrom(" ")); 250 | } 251 | z = a_add_AString_AString(z, a_str_int_AString(list.values[list.length - 1])); 252 | z = a_add_AString_AString(z, AStringFrom("]")); 253 | return z; 254 | } 255 | 256 | AString a_print_AList_int_AString(AList_int list) { 257 | return a_prints_AString_AString(a_str_AList_int_AString(list)); 258 | } 259 | 260 | int a_count_AList_int_intREFbool_int(AList_int list, bool(*z)(int)) { 261 | int count = 0; 262 | for(int j = 0;j < list.length;j ++) { 263 | if((*z)(list.values[j])) { 264 | count++; 265 | } 266 | } 267 | return count; 268 | } 269 | 270 | 271 | 272 | 273 | 274 | 275 | AList_int a_range_int_int_AList_int(int from, int to) { 276 | AList_int result; 277 | result.length = to - from; 278 | result.capacity = result.length + 1; 279 | result.values = (int*)malloc(sizeof(int) * result.capacity); 280 | for(int j = 0;j < to - from;j ++) { result.values[j] = j + from; } 281 | return result; 282 | } 283 | 284 | AList_AString a_split_w_AString_AList_AString(AString from) { 285 | AList_AString z = AList_AStringOf(0); 286 | char current[256]; 287 | int k = -1; 288 | for(int j = 0;j < from.length; j++) { 289 | if (from.chars[j] != ' ' && from.chars[j] != '\n') { 290 | k++; 291 | current[k] = from.chars[j]; 292 | } 293 | else { 294 | if (k > -1) { 295 | current[k + 1] = '\0'; 296 | z = a_append_AList_AString_AString_AList_AString(z, AStringFrom(current)); 297 | } 298 | k = -1; 299 | } 300 | } 301 | if (k > -1) { 302 | current[k + 1] = '\0'; 303 | z = a_append_AList_AString_AString_AList_AString(z, AStringFrom(current)); 304 | } 305 | 306 | return z; 307 | } 308 | 309 | 310 | int a_a_int_int_int(int a_arg, int a_other){ 311 | return a_other; 312 | } 313 | float a_a_int_float_float(int a_arg, float a_other){ 314 | return a_other; 315 | } 316 | 317 | int main(){ 318 | a_a_int_float_float(2, 2.2); 319 | a_a_int_int_int(2, 4); 320 | return 0; 321 | } 322 | -------------------------------------------------------------------------------- /examples/sum.py.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | typedef struct AString { 10 | char* chars; 11 | size_t length; 12 | size_t capacity; 13 | } AString; 14 | 15 | AString AStringFrom(char* s) { 16 | AString string; 17 | string.capacity = strlen(s) + 1; 18 | string.chars = (char*)malloc(sizeof(char) * string.capacity); 19 | string.length = string.capacity - 1; 20 | for(int j = 0;j < string.length;j ++) { string.chars[j] = s[j]; } 21 | string.chars[string.length] = '\0'; 22 | return string; 23 | } 24 | 25 | AString a_add_AString_AString(AString z, AString a) { 26 | if (a.length + z.length + 1 >= z.capacity) { 27 | z.capacity *= 2; 28 | z.chars = (char*)(realloc(z.chars, sizeof(char) * z.capacity)); 29 | } 30 | for(int j = 0;j < a.length;j ++) { z.chars[z.length + j] = a.chars[j]; } 31 | z.chars[z.length + a.length] = '\0'; 32 | z.length += a.length; 33 | 34 | return z; 35 | } 36 | int a_length_AString_int(AString string) { 37 | return string.length; 38 | } 39 | 40 | AString a_prints_AString_AString(AString string) { 41 | printf("%s\n", string.chars); 42 | return string; 43 | } 44 | 45 | AString a_str_int_AString(int value) { 46 | char buffer[22]; 47 | snprintf(buffer, 22, "%d", value); 48 | AString z = AStringFrom(buffer); 49 | return z; 50 | } 51 | 52 | AString a_str_AString_AString(AString string) { 53 | return string; 54 | } 55 | 56 | AString a_print_int_AString(int value) { 57 | return a_prints_AString_AString(a_str_int_AString(value)); 58 | } 59 | 60 | AString a_print_AString_AString(AString string) { 61 | return a_prints_AString_AString(string); 62 | } 63 | 64 | AString a_read_AString_AString(AString z) { 65 | char buffer[256]; 66 | int count = scanf("%s\n", buffer); 67 | AString z2 = AStringFrom(buffer); 68 | return z2; 69 | } 70 | 71 | bool a_ends_wita_AString_AString_bool(AString z, AString with) { 72 | if (z.length < with.length) { return false; } 73 | for(int j = 0; j < with.length; j++) { 74 | if(z.chars[z.length - with.length + j] != with.chars[j]) { 75 | return false; 76 | } 77 | } 78 | return true; 79 | } 80 | 81 | 82 | int to_int_AString_int(AString value) { 83 | 84 | int i = 0; 85 | for(int j = 0; j <= value.length;j++) { 86 | i = 10 * i + (value.chars[i] - '0'); 87 | } 88 | return i; 89 | } 90 | 91 | int to_int_int_int(int value) { 92 | return value; 93 | } 94 | 95 | float to_float_int_float(int value) { 96 | return (float)value; 97 | } 98 | 99 | float to_float_float_float(float value) { 100 | return value; 101 | } 102 | 103 | bool a_a__lte___int_int_bool(int a, int b) { 104 | return a <= b; 105 | } 106 | 107 | bool a_a__lte___float_float_bool(float a, float b) { 108 | return a <= b; 109 | } 110 | 111 | 112 | typedef struct AList_int { 113 | int* values; 114 | size_t length; 115 | size_t capacity; 116 | } AList_int; 117 | 118 | AList_int a_append_AList_int_int_AList_int(AList_int list, int elem) { 119 | if(list.length + 1 > list.capacity) { 120 | list.capacity *= 2; 121 | list.values = (int*)realloc(list.values, sizeof(int) * list.capacity); 122 | } 123 | list.values[list.length] = elem; 124 | list.length++; 125 | return list; 126 | } 127 | 128 | AList_int a_pop_AList_int_int_AList_int(AList_int list, int elem) { 129 | list.length--; 130 | return list; 131 | } 132 | 133 | int a_length_AList_int_int(AList_int list) { 134 | return list.length; 135 | } 136 | 137 | int a_index_AList_int_int_int(AList_int list, int index) { 138 | return list.values[index]; 139 | } 140 | 141 | AList_int a_slice_AList_int_int_int_AList_int(AList_int z, int from, int to) { 142 | AList_int list; 143 | list.values = (int*)malloc(sizeof(int) * (to - from)); 144 | list.length = to - from; 145 | list.capacity = to - from; 146 | for(int j = from;j < to;j ++) { 147 | list.values[j - from] = z.values[j]; 148 | } 149 | return list; 150 | } 151 | 152 | AList_int AList_intOf(size_t count, ...) { 153 | va_list ap; 154 | AList_int list; 155 | list.values = (int*)(malloc(sizeof(int) * (count + 1))); 156 | list.length = count; 157 | list.capacity = count + 1; 158 | va_start(ap, count); 159 | for(int j = 0;j < count;j ++) { list.values[j] = va_arg(ap, int); } 160 | return list; 161 | } 162 | 163 | AString a_str_AList_int_AString(AList_int list) { 164 | AString z = AStringFrom("["); 165 | 166 | for(int j = 0;j < list.length - 1;j ++) { 167 | z = a_add_AString_AString(z, a_str_int_AString(list.values[j])); 168 | z = a_add_AString_AString(z, AStringFrom(" ")); 169 | } 170 | z = a_add_AString_AString(z, a_str_int_AString(list.values[list.length - 1])); 171 | z = a_add_AString_AString(z, AStringFrom("]")); 172 | return z; 173 | } 174 | 175 | AString a_print_AList_int_AString(AList_int list) { 176 | return a_prints_AString_AString(a_str_AList_int_AString(list)); 177 | } 178 | 179 | int a_count_AList_int_intREFbool_int(AList_int list, bool(*z)(int)) { 180 | int count = 0; 181 | for(int j = 0;j < list.length;j ++) { 182 | if((*z)(list.values[j])) { 183 | count++; 184 | } 185 | } 186 | return count; 187 | } 188 | 189 | 190 | 191 | 192 | 193 | typedef struct AList_AString { 194 | AString* values; 195 | size_t length; 196 | size_t capacity; 197 | } AList_AString; 198 | 199 | AList_AString a_append_AList_AString_AString_AList_AString(AList_AString list, AString elem) { 200 | if(list.length + 1 > list.capacity) { 201 | list.capacity *= 2; 202 | list.values = (AString*)realloc(list.values, sizeof(AString) * list.capacity); 203 | } 204 | list.values[list.length] = elem; 205 | list.length++; 206 | return list; 207 | } 208 | 209 | AList_AString a_pop_AList_AString_AString_AList_AString(AList_AString list, AString elem) { 210 | list.length--; 211 | return list; 212 | } 213 | 214 | int a_length_AList_AString_int(AList_AString list) { 215 | return list.length; 216 | } 217 | 218 | AString a_index_AList_AString_int_AString(AList_AString list, int index) { 219 | return list.values[index]; 220 | } 221 | 222 | AList_AString a_slice_AList_AString_int_int_AList_AString(AList_AString z, int from, int to) { 223 | AList_AString list; 224 | list.values = (AString*)malloc(sizeof(AString) * (to - from)); 225 | list.length = to - from; 226 | list.capacity = to - from; 227 | for(int j = from;j < to;j ++) { 228 | list.values[j - from] = z.values[j]; 229 | } 230 | return list; 231 | } 232 | 233 | AList_AString AList_AStringOf(size_t count, ...) { 234 | va_list ap; 235 | AList_AString list; 236 | list.values = (AString*)(malloc(sizeof(AString) * (count + 1))); 237 | list.length = count; 238 | list.capacity = count + 1; 239 | va_start(ap, count); 240 | for(int j = 0;j < count;j ++) { list.values[j] = va_arg(ap, AString); } 241 | return list; 242 | } 243 | 244 | AString a_str_AList_AString_AString(AList_AString list) { 245 | AString z = AStringFrom("["); 246 | 247 | for(int j = 0;j < list.length - 1;j ++) { 248 | z = a_add_AString_AString(z, a_str_AString_AString(list.values[j])); 249 | z = a_add_AString_AString(z, AStringFrom(" ")); 250 | } 251 | z = a_add_AString_AString(z, a_str_AString_AString(list.values[list.length - 1])); 252 | z = a_add_AString_AString(z, AStringFrom("]")); 253 | return z; 254 | } 255 | 256 | AString a_print_AList_AString_AString(AList_AString list) { 257 | return a_prints_AString_AString(a_str_AList_AString_AString(list)); 258 | } 259 | 260 | int a_count_AList_AString_AStringREFbool_int(AList_AString list, bool(*z)(AString)) { 261 | int count = 0; 262 | for(int j = 0;j < list.length;j ++) { 263 | if((*z)(list.values[j])) { 264 | count++; 265 | } 266 | } 267 | return count; 268 | } 269 | 270 | 271 | 272 | 273 | 274 | 275 | AList_int a_range_int_int_AList_int(int from, int to) { 276 | AList_int result; 277 | result.length = to - from; 278 | result.capacity = result.length + 1; 279 | result.values = (int*)malloc(sizeof(int) * result.capacity); 280 | for(int j = 0;j < to - from;j ++) { result.values[j] = j + from; } 281 | return result; 282 | } 283 | 284 | AList_AString a_split_w_AString_AList_AString(AString from) { 285 | AList_AString z = AList_AStringOf(0); 286 | char current[256]; 287 | int k = -1; 288 | for(int j = 0;j < from.length; j++) { 289 | if (from.chars[j] != ' ' && from.chars[j] != '\n') { 290 | k++; 291 | current[k] = from.chars[j]; 292 | } 293 | else { 294 | if (k > -1) { 295 | current[k + 1] = '\0'; 296 | z = a_append_AList_AString_AString_AList_AString(z, AStringFrom(current)); 297 | } 298 | k = -1; 299 | } 300 | } 301 | if (k > -1) { 302 | current[k + 1] = '\0'; 303 | z = a_append_AList_AString_AString_AList_AString(z, AStringFrom(current)); 304 | } 305 | 306 | return z; 307 | } 308 | 309 | 310 | int a_sum_int_int(int a_n){ 311 | int a_result = 0; 312 | for(int a_i=0;a_i<(a_n + 1);a_i++){ 313 | a_result = (a_result + a_i); 314 | } 315 | ; 316 | return a_result; 317 | } 318 | 319 | int main(){ 320 | a_print_int_AString(a_sum_int_int(2000)); 321 | return 0; 322 | } 323 | -------------------------------------------------------------------------------- /examples/sum_while.py.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | typedef struct AString { 10 | char* chars; 11 | size_t length; 12 | size_t capacity; 13 | } AString; 14 | 15 | AString AStringFrom(char* s) { 16 | AString string; 17 | string.capacity = strlen(s) + 1; 18 | string.chars = (char*)malloc(sizeof(char) * string.capacity); 19 | string.length = string.capacity - 1; 20 | for(int j = 0;j < string.length;j ++) { string.chars[j] = s[j]; } 21 | string.chars[string.length] = '\0'; 22 | return string; 23 | } 24 | 25 | AString a_add_AString_AString(AString z, AString a) { 26 | if (a.length + z.length + 1 >= z.capacity) { 27 | z.capacity *= 2; 28 | z.chars = (char*)(realloc(z.chars, sizeof(char) * z.capacity)); 29 | } 30 | for(int j = 0;j < a.length;j ++) { z.chars[z.length + j] = a.chars[j]; } 31 | z.chars[z.length + a.length] = '\0'; 32 | z.length += a.length; 33 | 34 | return z; 35 | } 36 | int a_length_AString_int(AString string) { 37 | return string.length; 38 | } 39 | 40 | AString a_prints_AString_AString(AString string) { 41 | printf("%s\n", string.chars); 42 | return string; 43 | } 44 | 45 | AString a_str_int_AString(int value) { 46 | char buffer[22]; 47 | snprintf(buffer, 22, "%d", value); 48 | AString z = AStringFrom(buffer); 49 | return z; 50 | } 51 | 52 | AString a_str_AString_AString(AString string) { 53 | return string; 54 | } 55 | 56 | AString a_print_int_AString(int value) { 57 | return a_prints_AString_AString(a_str_int_AString(value)); 58 | } 59 | 60 | AString a_print_AString_AString(AString string) { 61 | return a_prints_AString_AString(string); 62 | } 63 | 64 | AString a_read_AString_AString(AString z) { 65 | char buffer[256]; 66 | int count = scanf("%s\n", buffer); 67 | AString z2 = AStringFrom(buffer); 68 | return z2; 69 | } 70 | 71 | bool a_ends_wita_AString_AString_bool(AString z, AString with) { 72 | if (z.length < with.length) { return false; } 73 | for(int j = 0; j < with.length; j++) { 74 | if(z.chars[z.length - with.length + j] != with.chars[j]) { 75 | return false; 76 | } 77 | } 78 | return true; 79 | } 80 | 81 | 82 | int to_int_AString_int(AString value) { 83 | 84 | int i = 0; 85 | for(int j = 0; j <= value.length;j++) { 86 | i = 10 * i + (value.chars[i] - '0'); 87 | } 88 | return i; 89 | } 90 | 91 | int to_int_int_int(int value) { 92 | return value; 93 | } 94 | 95 | float to_float_int_float(int value) { 96 | return (float)value; 97 | } 98 | 99 | float to_float_float_float(float value) { 100 | return value; 101 | } 102 | 103 | bool a_a__lte___int_int_bool(int a, int b) { 104 | return a <= b; 105 | } 106 | 107 | bool a_a__lte___float_float_bool(float a, float b) { 108 | return a <= b; 109 | } 110 | 111 | 112 | typedef struct AList_AString { 113 | AString* values; 114 | size_t length; 115 | size_t capacity; 116 | } AList_AString; 117 | 118 | AList_AString a_append_AList_AString_AString_AList_AString(AList_AString list, AString elem) { 119 | if(list.length + 1 > list.capacity) { 120 | list.capacity *= 2; 121 | list.values = (AString*)realloc(list.values, sizeof(AString) * list.capacity); 122 | } 123 | list.values[list.length] = elem; 124 | list.length++; 125 | return list; 126 | } 127 | 128 | AList_AString a_pop_AList_AString_AString_AList_AString(AList_AString list, AString elem) { 129 | list.length--; 130 | return list; 131 | } 132 | 133 | int a_length_AList_AString_int(AList_AString list) { 134 | return list.length; 135 | } 136 | 137 | AString a_index_AList_AString_int_AString(AList_AString list, int index) { 138 | return list.values[index]; 139 | } 140 | 141 | AList_AString a_slice_AList_AString_int_int_AList_AString(AList_AString z, int from, int to) { 142 | AList_AString list; 143 | list.values = (AString*)malloc(sizeof(AString) * (to - from)); 144 | list.length = to - from; 145 | list.capacity = to - from; 146 | for(int j = from;j < to;j ++) { 147 | list.values[j - from] = z.values[j]; 148 | } 149 | return list; 150 | } 151 | 152 | AList_AString AList_AStringOf(size_t count, ...) { 153 | va_list ap; 154 | AList_AString list; 155 | list.values = (AString*)(malloc(sizeof(AString) * (count + 1))); 156 | list.length = count; 157 | list.capacity = count + 1; 158 | va_start(ap, count); 159 | for(int j = 0;j < count;j ++) { list.values[j] = va_arg(ap, AString); } 160 | return list; 161 | } 162 | 163 | AString a_str_AList_AString_AString(AList_AString list) { 164 | AString z = AStringFrom("["); 165 | 166 | for(int j = 0;j < list.length - 1;j ++) { 167 | z = a_add_AString_AString(z, a_str_AString_AString(list.values[j])); 168 | z = a_add_AString_AString(z, AStringFrom(" ")); 169 | } 170 | z = a_add_AString_AString(z, a_str_AString_AString(list.values[list.length - 1])); 171 | z = a_add_AString_AString(z, AStringFrom("]")); 172 | return z; 173 | } 174 | 175 | AString a_print_AList_AString_AString(AList_AString list) { 176 | return a_prints_AString_AString(a_str_AList_AString_AString(list)); 177 | } 178 | 179 | int a_count_AList_AString_AStringREFbool_int(AList_AString list, bool(*z)(AString)) { 180 | int count = 0; 181 | for(int j = 0;j < list.length;j ++) { 182 | if((*z)(list.values[j])) { 183 | count++; 184 | } 185 | } 186 | return count; 187 | } 188 | 189 | 190 | 191 | 192 | 193 | typedef struct AList_int { 194 | int* values; 195 | size_t length; 196 | size_t capacity; 197 | } AList_int; 198 | 199 | AList_int a_append_AList_int_int_AList_int(AList_int list, int elem) { 200 | if(list.length + 1 > list.capacity) { 201 | list.capacity *= 2; 202 | list.values = (int*)realloc(list.values, sizeof(int) * list.capacity); 203 | } 204 | list.values[list.length] = elem; 205 | list.length++; 206 | return list; 207 | } 208 | 209 | AList_int a_pop_AList_int_int_AList_int(AList_int list, int elem) { 210 | list.length--; 211 | return list; 212 | } 213 | 214 | int a_length_AList_int_int(AList_int list) { 215 | return list.length; 216 | } 217 | 218 | int a_index_AList_int_int_int(AList_int list, int index) { 219 | return list.values[index]; 220 | } 221 | 222 | AList_int a_slice_AList_int_int_int_AList_int(AList_int z, int from, int to) { 223 | AList_int list; 224 | list.values = (int*)malloc(sizeof(int) * (to - from)); 225 | list.length = to - from; 226 | list.capacity = to - from; 227 | for(int j = from;j < to;j ++) { 228 | list.values[j - from] = z.values[j]; 229 | } 230 | return list; 231 | } 232 | 233 | AList_int AList_intOf(size_t count, ...) { 234 | va_list ap; 235 | AList_int list; 236 | list.values = (int*)(malloc(sizeof(int) * (count + 1))); 237 | list.length = count; 238 | list.capacity = count + 1; 239 | va_start(ap, count); 240 | for(int j = 0;j < count;j ++) { list.values[j] = va_arg(ap, int); } 241 | return list; 242 | } 243 | 244 | AString a_str_AList_int_AString(AList_int list) { 245 | AString z = AStringFrom("["); 246 | 247 | for(int j = 0;j < list.length - 1;j ++) { 248 | z = a_add_AString_AString(z, a_str_int_AString(list.values[j])); 249 | z = a_add_AString_AString(z, AStringFrom(" ")); 250 | } 251 | z = a_add_AString_AString(z, a_str_int_AString(list.values[list.length - 1])); 252 | z = a_add_AString_AString(z, AStringFrom("]")); 253 | return z; 254 | } 255 | 256 | AString a_print_AList_int_AString(AList_int list) { 257 | return a_prints_AString_AString(a_str_AList_int_AString(list)); 258 | } 259 | 260 | int a_count_AList_int_intREFbool_int(AList_int list, bool(*z)(int)) { 261 | int count = 0; 262 | for(int j = 0;j < list.length;j ++) { 263 | if((*z)(list.values[j])) { 264 | count++; 265 | } 266 | } 267 | return count; 268 | } 269 | 270 | 271 | 272 | 273 | 274 | 275 | AList_int a_range_int_int_AList_int(int from, int to) { 276 | AList_int result; 277 | result.length = to - from; 278 | result.capacity = result.length + 1; 279 | result.values = (int*)malloc(sizeof(int) * result.capacity); 280 | for(int j = 0;j < to - from;j ++) { result.values[j] = j + from; } 281 | return result; 282 | } 283 | 284 | AList_AString a_split_w_AString_AList_AString(AString from) { 285 | AList_AString z = AList_AStringOf(0); 286 | char current[256]; 287 | int k = -1; 288 | for(int j = 0;j < from.length; j++) { 289 | if (from.chars[j] != ' ' && from.chars[j] != '\n') { 290 | k++; 291 | current[k] = from.chars[j]; 292 | } 293 | else { 294 | if (k > -1) { 295 | current[k + 1] = '\0'; 296 | z = a_append_AList_AString_AString_AList_AString(z, AStringFrom(current)); 297 | } 298 | k = -1; 299 | } 300 | } 301 | if (k > -1) { 302 | current[k + 1] = '\0'; 303 | z = a_append_AList_AString_AString_AList_AString(z, AStringFrom(current)); 304 | } 305 | 306 | return z; 307 | } 308 | 309 | 310 | int a_sum_while_int_int(int a_n){ 311 | int a_result = 0; 312 | int a_i = 0; 313 | while((a_i < a_n)){ 314 | a_i = (a_i + 1); 315 | a_result = (a_result + a_i); 316 | } 317 | ; 318 | return a_result; 319 | } 320 | 321 | int main(){ 322 | a_print_int_AString(a_sum_while_int_int(2000)); 323 | return 0; 324 | } 325 | -------------------------------------------------------------------------------- /examples/m.py.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | typedef struct AString { 10 | char* chars; 11 | size_t length; 12 | size_t capacity; 13 | } AString; 14 | 15 | AString AStringFrom(char* s) { 16 | AString string; 17 | string.capacity = strlen(s) + 1; 18 | string.chars = (char*)malloc(sizeof(char) * string.capacity); 19 | string.length = string.capacity - 1; 20 | for(int j = 0;j < string.length;j ++) { string.chars[j] = s[j]; } 21 | string.chars[string.length] = '\0'; 22 | return string; 23 | } 24 | 25 | AString a_add_AString_AString(AString z, AString a) { 26 | if (a.length + z.length + 1 >= z.capacity) { 27 | z.capacity *= 2; 28 | z.chars = (char*)(realloc(z.chars, sizeof(char) * z.capacity)); 29 | } 30 | for(int j = 0;j < a.length;j ++) { z.chars[z.length + j] = a.chars[j]; } 31 | z.chars[z.length + a.length] = '\0'; 32 | z.length += a.length; 33 | 34 | return z; 35 | } 36 | int a_length_AString_int(AString string) { 37 | return string.length; 38 | } 39 | 40 | AString a_prints_AString_AString(AString string) { 41 | printf("%s\n", string.chars); 42 | return string; 43 | } 44 | 45 | AString a_str_int_AString(int value) { 46 | char buffer[22]; 47 | snprintf(buffer, 22, "%d", value); 48 | AString z = AStringFrom(buffer); 49 | return z; 50 | } 51 | 52 | AString a_str_AString_AString(AString string) { 53 | return string; 54 | } 55 | 56 | AString a_print_int_AString(int value) { 57 | return a_prints_AString_AString(a_str_int_AString(value)); 58 | } 59 | 60 | AString a_print_AString_AString(AString string) { 61 | return a_prints_AString_AString(string); 62 | } 63 | 64 | AString a_read_AString_AString(AString z) { 65 | char buffer[256]; 66 | int count = scanf("%s\n", buffer); 67 | AString z2 = AStringFrom(buffer); 68 | return z2; 69 | } 70 | 71 | bool a_ends_wita_AString_AString_bool(AString z, AString with) { 72 | if (z.length < with.length) { return false; } 73 | for(int j = 0; j < with.length; j++) { 74 | if(z.chars[z.length - with.length + j] != with.chars[j]) { 75 | return false; 76 | } 77 | } 78 | return true; 79 | } 80 | 81 | 82 | int to_int_AString_int(AString value) { 83 | 84 | int i = 0; 85 | for(int j = 0; j <= value.length;j++) { 86 | i = 10 * i + (value.chars[i] - '0'); 87 | } 88 | return i; 89 | } 90 | 91 | int to_int_int_int(int value) { 92 | return value; 93 | } 94 | 95 | float to_float_int_float(int value) { 96 | return (float)value; 97 | } 98 | 99 | float to_float_float_float(float value) { 100 | return value; 101 | } 102 | 103 | bool a_a__lte___int_int_bool(int a, int b) { 104 | return a <= b; 105 | } 106 | 107 | bool a_a__lte___float_float_bool(float a, float b) { 108 | return a <= b; 109 | } 110 | 111 | 112 | typedef struct AList_int { 113 | int* values; 114 | size_t length; 115 | size_t capacity; 116 | } AList_int; 117 | 118 | AList_int a_append_AList_int_int_AList_int(AList_int list, int elem) { 119 | if(list.length + 1 > list.capacity) { 120 | list.capacity *= 2; 121 | list.values = (int*)realloc(list.values, sizeof(int) * list.capacity); 122 | } 123 | list.values[list.length] = elem; 124 | list.length++; 125 | return list; 126 | } 127 | 128 | AList_int a_pop_AList_int_int_AList_int(AList_int list, int elem) { 129 | list.length--; 130 | return list; 131 | } 132 | 133 | int a_length_AList_int_int(AList_int list) { 134 | return list.length; 135 | } 136 | 137 | int a_index_AList_int_int_int(AList_int list, int index) { 138 | return list.values[index]; 139 | } 140 | 141 | AList_int a_slice_AList_int_int_int_AList_int(AList_int z, int from, int to) { 142 | AList_int list; 143 | list.values = (int*)malloc(sizeof(int) * (to - from)); 144 | list.length = to - from; 145 | list.capacity = to - from; 146 | for(int j = from;j < to;j ++) { 147 | list.values[j - from] = z.values[j]; 148 | } 149 | return list; 150 | } 151 | 152 | AList_int AList_intOf(size_t count, ...) { 153 | va_list ap; 154 | AList_int list; 155 | list.values = (int*)(malloc(sizeof(int) * (count + 1))); 156 | list.length = count; 157 | list.capacity = count + 1; 158 | va_start(ap, count); 159 | for(int j = 0;j < count;j ++) { list.values[j] = va_arg(ap, int); } 160 | return list; 161 | } 162 | 163 | AString a_str_AList_int_AString(AList_int list) { 164 | AString z = AStringFrom("["); 165 | 166 | for(int j = 0;j < list.length - 1;j ++) { 167 | z = a_add_AString_AString(z, a_str_int_AString(list.values[j])); 168 | z = a_add_AString_AString(z, AStringFrom(" ")); 169 | } 170 | z = a_add_AString_AString(z, a_str_int_AString(list.values[list.length - 1])); 171 | z = a_add_AString_AString(z, AStringFrom("]")); 172 | return z; 173 | } 174 | 175 | AString a_print_AList_int_AString(AList_int list) { 176 | return a_prints_AString_AString(a_str_AList_int_AString(list)); 177 | } 178 | 179 | int a_count_AList_int_intREFbool_int(AList_int list, bool(*z)(int)) { 180 | int count = 0; 181 | for(int j = 0;j < list.length;j ++) { 182 | if((*z)(list.values[j])) { 183 | count++; 184 | } 185 | } 186 | return count; 187 | } 188 | 189 | 190 | 191 | 192 | 193 | typedef struct AList_AString { 194 | AString* values; 195 | size_t length; 196 | size_t capacity; 197 | } AList_AString; 198 | 199 | AList_AString a_append_AList_AString_AString_AList_AString(AList_AString list, AString elem) { 200 | if(list.length + 1 > list.capacity) { 201 | list.capacity *= 2; 202 | list.values = (AString*)realloc(list.values, sizeof(AString) * list.capacity); 203 | } 204 | list.values[list.length] = elem; 205 | list.length++; 206 | return list; 207 | } 208 | 209 | AList_AString a_pop_AList_AString_AString_AList_AString(AList_AString list, AString elem) { 210 | list.length--; 211 | return list; 212 | } 213 | 214 | int a_length_AList_AString_int(AList_AString list) { 215 | return list.length; 216 | } 217 | 218 | AString a_index_AList_AString_int_AString(AList_AString list, int index) { 219 | return list.values[index]; 220 | } 221 | 222 | AList_AString a_slice_AList_AString_int_int_AList_AString(AList_AString z, int from, int to) { 223 | AList_AString list; 224 | list.values = (AString*)malloc(sizeof(AString) * (to - from)); 225 | list.length = to - from; 226 | list.capacity = to - from; 227 | for(int j = from;j < to;j ++) { 228 | list.values[j - from] = z.values[j]; 229 | } 230 | return list; 231 | } 232 | 233 | AList_AString AList_AStringOf(size_t count, ...) { 234 | va_list ap; 235 | AList_AString list; 236 | list.values = (AString*)(malloc(sizeof(AString) * (count + 1))); 237 | list.length = count; 238 | list.capacity = count + 1; 239 | va_start(ap, count); 240 | for(int j = 0;j < count;j ++) { list.values[j] = va_arg(ap, AString); } 241 | return list; 242 | } 243 | 244 | AString a_str_AList_AString_AString(AList_AString list) { 245 | AString z = AStringFrom("["); 246 | 247 | for(int j = 0;j < list.length - 1;j ++) { 248 | z = a_add_AString_AString(z, a_str_AString_AString(list.values[j])); 249 | z = a_add_AString_AString(z, AStringFrom(" ")); 250 | } 251 | z = a_add_AString_AString(z, a_str_AString_AString(list.values[list.length - 1])); 252 | z = a_add_AString_AString(z, AStringFrom("]")); 253 | return z; 254 | } 255 | 256 | AString a_print_AList_AString_AString(AList_AString list) { 257 | return a_prints_AString_AString(a_str_AList_AString_AString(list)); 258 | } 259 | 260 | int a_count_AList_AString_AStringREFbool_int(AList_AString list, bool(*z)(AString)) { 261 | int count = 0; 262 | for(int j = 0;j < list.length;j ++) { 263 | if((*z)(list.values[j])) { 264 | count++; 265 | } 266 | } 267 | return count; 268 | } 269 | 270 | 271 | 272 | 273 | 274 | 275 | AList_int a_range_int_int_AList_int(int from, int to) { 276 | AList_int result; 277 | result.length = to - from; 278 | result.capacity = result.length + 1; 279 | result.values = (int*)malloc(sizeof(int) * result.capacity); 280 | for(int j = 0;j < to - from;j ++) { result.values[j] = j + from; } 281 | return result; 282 | } 283 | 284 | AList_AString a_split_w_AString_AList_AString(AString from) { 285 | AList_AString z = AList_AStringOf(0); 286 | char current[256]; 287 | int k = -1; 288 | for(int j = 0;j < from.length; j++) { 289 | if (from.chars[j] != ' ' && from.chars[j] != '\n') { 290 | k++; 291 | current[k] = from.chars[j]; 292 | } 293 | else { 294 | if (k > -1) { 295 | current[k + 1] = '\0'; 296 | z = a_append_AList_AString_AString_AList_AString(z, AStringFrom(current)); 297 | } 298 | k = -1; 299 | } 300 | } 301 | if (k > -1) { 302 | current[k + 1] = '\0'; 303 | z = a_append_AList_AString_AString_AList_AString(z, AStringFrom(current)); 304 | } 305 | 306 | return z; 307 | } 308 | 309 | 310 | AList_int a_f_map_intREFint_AList_int_AList_int(int (*a_f)(int), AList_int a_s){ 311 | AList_int a_out = AList_intOf(0); 312 | for(int i=0;i 57 | Let('var' 58 | String('a'), 59 | context) 60 | 61 | var, z = 'a', 'b' 62 | ..context 63 | => 64 | Letmany(['var', 'z'], 65 | [String('a'), String('b')], 66 | context) 67 | ''' 68 | 69 | 70 | if len(targets) == 1 and hasattr(targets[0], 'id'): 71 | return hm_ast.Let( 72 | targets[0].id, 73 | self.convert_node(value), 74 | context) 75 | else: 76 | return hm_ast.Letmany( 77 | [t.id for t in targets[0].elts], 78 | [self.convert_node(node) for node in value.elts], 79 | context) 80 | 81 | def convert_augassign(self, target, op, value, context): 82 | return self.convert_assign( 83 | [target], 84 | ast.BinOp( 85 | target, 86 | op, 87 | value), 88 | context) 89 | 90 | 91 | def convert_str(self, s, context): 92 | return hm_ast.aString(s) 93 | 94 | def convert_num(self, n, context): 95 | if type(n) == float: 96 | return hm_ast.aFloat(n) 97 | else: 98 | return hm_ast.anInteger(n) 99 | 100 | def convert_functiondef(self, name, args, body, decorator_list, returns, context, docstring=None): 101 | ''' 102 | def name(arg, arg2): 103 | return arg 104 | ..context 105 | => 106 | Let('name', 107 | Multi_Lambda(['arg', 'arg2'], 108 | [Ident('arg')]), 109 | context) 110 | ''' 111 | expected = [] 112 | vars = {} 113 | for arg in args.args: 114 | expected.append(self.convert_annotation(arg.annotation, vars)) 115 | expected.append(self.convert_annotation(returns, vars)) 116 | result = hm_ast.Let( 117 | name, 118 | hm_ast.Multi_Lambda( 119 | [arg.arg for arg in args.args], 120 | self.convert_body(body, None), 121 | expected=expected), 122 | context) 123 | result.a_native = False 124 | result.a_vars = [] 125 | if decorator_list: 126 | if isinstance(decorator_list[0], ast.Name) and decorator_list[0].id == 'native': 127 | result.a_native = True 128 | if isinstance(decorator_list[-1], ast.Call) and decorator_list[-1].func.id == 'template': 129 | # result.a_vars = vars.keys 130 | result.a_vars = [vars[arg.id] for arg in decorator_list[-1].args] # vars.keys() 131 | return result 132 | 133 | def convert_annotation(self, annotation, vars): 134 | if isinstance(annotation, ast.Name) and annotation.id.islower(): 135 | if annotation.id not in vars: 136 | vars[annotation.id] = hm_ast.TypeVariable() 137 | return vars[annotation.id] 138 | elif isinstance(annotation, ast.Name): 139 | return hm_ast.TypeOperator(annotation.id, []) 140 | elif isinstance(annotation, ast.BinOp) and isinstance(annotation.op, ast.RShift): 141 | if isinstance(annotation.left, ast.Name): 142 | # A >> B 143 | left = [annotation.left, annotation.right] 144 | else: 145 | # (A, Z) >> B 146 | left = annotation.left.elts + [annotation.right] 147 | return hm_ast.Multi_Function([self.convert_annotation(l, vars) for l in left]) 148 | elif isinstance(annotation, ast.BinOp) and isinstance(annotation.op, ast.BinOr): 149 | # A | B 150 | left, right = [self.convert_annotation(a, vars) for a in [annotation.left, annotation.right]] 151 | return hm_ast.Union(left, right) 152 | elif isinstance(annotation, ast.List): 153 | # [A] 154 | return hm_ast.List(self.convert_annotation(annotation.elts[0], vars)) 155 | else: 156 | return None 157 | 158 | def convert_expr(self, value, context): 159 | return self.convert_node(value, context) 160 | 161 | def convert_body(self, body, context): 162 | if len(body) == 1: 163 | converted = self.convert_node(body[0], context) 164 | if not isinstance(converted, (hm_ast.Let, hm_ast.Letrec)): 165 | return converted 166 | elif context is None: 167 | converted.body = hm_ast.Ident(converted.v) 168 | return converted 169 | else: 170 | return converted 171 | else: 172 | current = len(body) - 1 173 | # context = context or hm_ast.anInteger(2) 174 | while current >= 0: 175 | next_node = self.convert_node(body[current], context) 176 | if isinstance(next_node, (hm_ast.Let, hm_ast.Letrec)): 177 | context = next_node 178 | elif context: 179 | context = hm_ast.Body(next_node, context) 180 | else: 181 | context = next_node 182 | current -= 1 183 | return context 184 | 185 | def convert_return(self, value, context): 186 | return self.convert_node(value, context) 187 | 188 | def convert_binop(self, left, right, op, context): 189 | ''' 190 | 2 / 2 191 | => 192 | Multi_Apply( 193 | Ident('a_divide'), 194 | [Integer(2), Integer(2)]) 195 | ''' 196 | return hm_ast.Multi_Apply( 197 | hm_ast.Ident('a' + self.OPERATOR_MAGIC_FUNCTIONS[type(op)]), 198 | [self.convert_node(left, context), self.convert_node(right, context)]) 199 | 200 | def convert_compare(self, ops, left, comparators, context): 201 | return hm_ast.Multi_Apply( 202 | hm_ast.Ident('a' + self.OPERATOR_MAGIC_FUNCTIONS[type(ops[0])]), 203 | [self.convert_node(left, context), self.convert_node(comparators[0], context)]) 204 | 205 | def convert_if(self, test, body, orelse, context): 206 | return hm_ast.If( 207 | self.convert_node(test, context), 208 | self.convert_body(body, context), 209 | self.convert_body(orelse, context)) 210 | 211 | def convert_for(self, target, body, iter, orelse, context): 212 | return hm_ast.For( 213 | self.convert_node(iter, context), 214 | self.convert_node(target, context), 215 | self.convert_body(body, context)) 216 | 217 | def convert_while(self, test, body, orelse, context): 218 | if orelse: 219 | raise NotSupportedError("else not supported after while") 220 | return hm_ast.While( 221 | self.convert_node(test, context), 222 | self.convert_body(body, context)) 223 | 224 | def convert_subscript(self, value, slice, ctx, context): 225 | if isinstance(slice, ast.Index): 226 | return hm_ast.Multi_Apply( 227 | hm_ast.Ident('a' + self.OPERATOR_MAGIC_FUNCTIONS[type(slice)]), [ 228 | self.convert_node(value, context), 229 | self.convert_node(slice.value)]) 230 | else: 231 | return hm_ast.Multi_Apply( 232 | hm_ast.Ident('a' + self.OPERATOR_MAGIC_FUNCTIONS[type(slice)]), [ 233 | self.convert_node(value, context), 234 | self.convert_node(slice.lower) if slice.lower else hm_ast.anInteger(0), 235 | self.convert_node(slice.upper) if slice.upper else hm_ast.Multi_Apply( 236 | self.OPERATOR_MAGIC_FUNCTIONS[ast.Sub], [ 237 | hm_ast.Apply(hm_ast.Ident('len'), self.convert_node(value, context)), 238 | hm_ast.anInteger(1)])]) 239 | 240 | def convert_name(self, id, ctx, context): 241 | ''' 242 | alexander 243 | => 244 | Ident("alexander") 245 | ''' 246 | return hm_ast.Ident(id) 247 | 248 | def convert_nameconstant(self, value, context): 249 | if value in [True, False]: 250 | return hm_ast.aBoolean(value) 251 | else: 252 | return hm_ast.Ident(str(value)) 253 | 254 | def convert_list(self, elts, ctx, context): 255 | ''' 256 | [e] 257 | => 258 | aList(Ident("e")) 259 | ''' 260 | return hm_ast.aList([self.convert_node(elt) for elt in elts]) 261 | 262 | def convert_call(self, func, args, keywords, context): 263 | ''' 264 | a(2) 265 | => 266 | Apply(Ident("a"), anInteger(2)) 267 | ''' 268 | return hm_ast.Multi_Apply(self.convert_node(func), [self.convert_node(arg) for arg in args]) 269 | 270 | def convert_lambda(self, args, body, context): 271 | ''' 272 | lambda s: s 273 | => 274 | Lambda(Ident("s"), Ident("s")) 275 | ''' 276 | return hm_ast.Multi_Lambda([arg.arg for arg in args.args], self.convert_node(body)) 277 | 278 | 279 | -------------------------------------------------------------------------------- /airtight/c_generator.py: -------------------------------------------------------------------------------- 1 | import copy 2 | import os 3 | from airtight.ll_ast import * 4 | import airtight.hindley_milner_ast as hm_ast 5 | 6 | class Generator: 7 | def __init__(self, ast): 8 | self.ast = ast 9 | self.out = [] 10 | self.scopes = [{}] 11 | self.current_actuals = {} 12 | self.current_function_idents = set() 13 | self.c = set() 14 | self.libs = ['stdlib', 'stdbool', 'stddef', 'string', 'stdarg', 'stdio', 'errno'] 15 | self.functions = {} 16 | 17 | def generate(self): 18 | methods = [node for node in self.ast.expressions if node.type == 'method'] 19 | main = [node for node in self.ast.expressions if node.type != 'method'] 20 | for method in methods: 21 | self.register_func(method) 22 | 23 | for z in self.ast.expressions: 24 | self.register_apply(z) 25 | 26 | for method in self.functions.values(): 27 | method.render_all() 28 | self.nl() 29 | 30 | self.s('int main(){\n') 31 | for e in main: 32 | self.write_node(e, 1) 33 | self.semi() 34 | self.nl() 35 | self.offset(1) 36 | self.s('return 0;\n') 37 | self.rcurly() 38 | self.nl() 39 | 40 | l = self.render_c() 41 | s = ''.join(self.out) 42 | return l + '\n' + s 43 | 44 | def render_c(self): 45 | return self.render_includes() + '\n\n' + self.render_string() + '\n\n' + self.render_number() + '\n\n' + self.render_l() + '\n\n' + self.render_collections() 46 | 47 | def render_string(self): 48 | return self.gen_c('string.c') 49 | 50 | def render_number(self): 51 | return self.gen_c('number.c') 52 | 53 | def render_collections(self): 54 | return self.gen_c('collections.c') 55 | 56 | def render_l(self): 57 | self.c.add('int') 58 | self.c.add('AString') 59 | return '\n'.join(self.gen_c('list.c', list_type='AList_' + elem_type, elem_type=elem_type) for elem_type in self.c) 60 | 61 | def render_includes(self): 62 | return '\n'.join('#include<' + lib_name + '.h>' for lib_name in self.libs) 63 | 64 | def gen_c(self, file, **kwargs): 65 | with open(os.path.join('core', file), 'r') as f: 66 | t = f.read() 67 | for k, v in kwargs.items(): 68 | t = t.replace('%{' + k + '}', v) 69 | return t 70 | 71 | def offset(self, depth): 72 | self.out.append(self.OFFSET * depth) 73 | 74 | def ws(self): 75 | self.out.append(' ') 76 | 77 | def nl(self): 78 | self.out.append('\n') 79 | 80 | def lparen(self): 81 | self.out.append('(') 82 | 83 | def rparen(self): 84 | self.out.append(')') 85 | 86 | def lcurly(self): 87 | self.out.append('{') 88 | 89 | def rcurly(self): 90 | self.out.append('}') 91 | 92 | def s(self, s, depth=0): 93 | self.out.append(s) 94 | 95 | def comma(self): 96 | self.out.append(',') 97 | 98 | def semi(self): 99 | self.out.append(';') 100 | 101 | class CType: 102 | def __init__(self, label): 103 | self.label = label 104 | 105 | class FunctionGenerator: 106 | def __init__(self, ast, c_generator): 107 | self.node, self.arg_types, self.return_type = ast, [a.a_type for a in ast.body.args], ast.a_type 108 | self.c_generator = c_generator 109 | 110 | self.actuals = set() 111 | 112 | def load_registry(self, met, actual_arg_types, actual_return_type): 113 | for a, actual in zip(met.body.args, actual_arg_types): 114 | self.load_arg([h.instance.name if h.instance else h.name for h in met.a_vars], a.a_type, actual) 115 | self.load_arg([h.instance.name if h.instance else h.name for h in met.a_vars], met.body.a_type, actual_return_type) 116 | 117 | def load_arg(self, a_vars, met_type, actual_type): 118 | if isinstance(met_type, TypeVariable) and met_type.name in a_vars: 119 | self.c_generator.registry[met_type.name] = actual_type 120 | elif hasattr(met_type, 'instance') and met_type.instance: 121 | self.load_arg(a_vars, met_type.instance, actual_type) 122 | if hasattr(met_type, 'types') and hasattr(actual_type, 'types'): 123 | for t, z_child in zip(met_type.types, actual_type.types): 124 | self.load_arg(a_vars, t, z_child) 125 | 126 | 127 | def render(self, actual_arg_types, actual_return_type): 128 | m = copy.copy(self.node) 129 | m.body.a_type = m.a_type = actual_return_type 130 | self.c_generator.registry = {} 131 | self.load_registry(m, actual_arg_types, actual_return_type) 132 | 133 | self.c_generator.current_actuals = {v.name : w for v, w in zip(self.node.a_vars, list(actual_arg_types) + [actual_return_type])} 134 | self.c_generator.current_function_idents = set() 135 | 136 | self.c_generator.write_method(m) 137 | self.c_generator.nl() 138 | 139 | def render_all(self): 140 | if self.node.a_vars: 141 | for a in self.actuals: 142 | self.render(a[0], a[1]) 143 | else: 144 | self.render(self.arg_types, self.return_type) 145 | 146 | class CGenerator(Generator): 147 | OFFSET = ' ' 148 | 149 | def ref(self): 150 | self.out.append('*') 151 | 152 | def register_func(self, func): 153 | self.functions[func.label.label] = FunctionGenerator(func, self) 154 | 155 | def register_apply(self, node): 156 | if node.type == 'apply': 157 | if node.function.type == 'ident' and\ 158 | not isinstance(node.a_type, hm_ast.TypeVariable) and\ 159 | not any(isinstance(arg.a_type, hm_ast.TypeVariable) for arg in node.args): 160 | if node.function.label in self.functions: 161 | self.functions[node.function.label].actuals.add((tuple(arg.a_type for arg in node.args), node.a_type)) 162 | node._special = True 163 | # print(node.data, node.__dict__);input() 164 | for k, v in node.data.items(): 165 | if hasattr(v, 'type'): 166 | self.register_apply(v) 167 | elif isinstance(v, list): 168 | for e in v: 169 | if hasattr(e, 'type'): 170 | self.register_apply(e) 171 | 172 | def write_node(self, node, depth=0): 173 | getattr(self, 'write_' + node.type)(node, depth) 174 | 175 | def write_method(self, method, depth=0): 176 | self.offset(depth) 177 | q_label = copy.copy(method.label) 178 | self.write_type(method.body.a_return_type) 179 | self.ws() 180 | q_label.a_type = Multi_Function([arg.a_type for arg in method.body.args] + [method.body.a_return_type]) 181 | 182 | self.write_special_ident(q_label, [arg.a_type for arg in method.body.args], method.body.a_return_type) 183 | 184 | self.lparen() 185 | self.write_m_args(method.body.args, method.a_type) 186 | self.rparen() 187 | self.lcurly() 188 | self.nl() 189 | 190 | body = method.body.body if isinstance(method.body.body, list) else [method.body.body] 191 | for e in body[:-1]: 192 | self.write_node(e, depth + 1) 193 | self.semi() 194 | self.nl() 195 | if body[-1].type != 'if': 196 | self.offset(depth + 1) 197 | self.s('return') 198 | self.ws() 199 | self.write_node(body[-1]) 200 | else: 201 | self.write_if(body[-1], depth + 1, with_return=True) 202 | self.semi() 203 | self.nl() 204 | self.offset(depth) 205 | self.rcurly() 206 | 207 | def write_m_args(self, args, a_type, depth=0): 208 | for arg in args[:-1]: 209 | self.write_wita_type(arg) 210 | self.comma() 211 | self.ws() 212 | self.write_wita_type(args[-1]) 213 | 214 | def write_if(self, node, depth=0, with_return=False): 215 | self.offset(depth) 216 | self.s('if') 217 | self.lparen() 218 | self.write_node(node.test) 219 | self.rparen() 220 | self.lcurly() 221 | self.nl() 222 | body = node.body if isinstance(node.body, list) else [node.body] 223 | for e in body[:-1]: 224 | self.write_node(e, depth + 1) 225 | self.semi() 226 | self.nl() 227 | if with_return: 228 | self.offset(depth + 1) 229 | self.s('return') 230 | self.ws() 231 | self.write_node(body[-1]) 232 | else: 233 | self.write_node(body[-1], depth + 1) 234 | self.semi() 235 | self.nl() 236 | 237 | self.offset(depth) 238 | self.rcurly() 239 | self.nl() 240 | self.offset(depth) 241 | self.s('else') 242 | self.lcurly() 243 | self.nl() 244 | orelse = node.orelse if isinstance(node.orelse, list) else [node.orelse] 245 | for e in orelse[:-1]: 246 | self.write_node(e, depth + 1) 247 | self.semi() 248 | self.nl() 249 | if with_return: 250 | self.offset(depth + 1) 251 | self.s('return') 252 | self.ws() 253 | self.write_node(orelse[-1]) 254 | else: 255 | self.write_node(orelse[-1], depth + 1) 256 | self.semi() 257 | self.nl() 258 | 259 | self.offset(depth) 260 | self.rcurly() 261 | self.nl() 262 | 263 | def write_for(self, node, depth=0): 264 | self.offset(depth) 265 | self.s('for') 266 | self.lparen() 267 | self.s('int') 268 | self.ws() 269 | self.s('i=0;i 0: 360 | arg_types.append(arg.a_type.types[-1]) 361 | else: 362 | arg_types.append(arg.a_type) 363 | 364 | self.write_special_ident(node.function, arg_types[:-1], arg_types[-1]) 365 | else: 366 | self.write_node(node.function) 367 | self.lparen() 368 | self.write_args(node.args) 369 | self.rparen() 370 | 371 | def write_binop(self, node, depth=0): 372 | self.offset(depth) 373 | 374 | self.lparen() 375 | self.write_node(node.left) 376 | self.s(' {0} '.format(node.op)) 377 | self.write_node(node.right) 378 | self.rparen() 379 | 380 | def write_special_ident(self, node, arg_types, return_type, depth=0): 381 | self.offset(depth) 382 | self.s('a_' + node.label) 383 | self.s('_') 384 | for arg_type in arg_types: 385 | self.write_type(arg_type) 386 | self.s('_') 387 | 388 | self.write_type(return_type) 389 | 390 | def write_list(self, node, depth=0): 391 | if not node.items: 392 | self.s('{0}Of(0)'.format(self.to_ctype(node.a_type))) 393 | return 394 | self.s('{0}Of({1}, '.format(self.to_ctype(node.a_type), len(node.items))) 395 | for i in node.items[:-1]: 396 | self.write_node(i) 397 | self.comma() 398 | self.ws() 399 | self.write_node(node.items[-1]) 400 | self.s(')') 401 | 402 | def write_args(self, args, depth=0): 403 | for arg in args[:-1]: 404 | if arg.type == 'ident' and arg.label in self.functions: 405 | self.s('&') 406 | # print([arg.a_type.types[:-1]], arg.a_type.types[-1]);input() 407 | self.write_special_ident(arg, arg.a_type.types[:-1], arg.a_type.types[-1]) 408 | else: 409 | self.write_node(arg) 410 | self.comma() 411 | self.ws() 412 | self.write_node(args[-1]) 413 | 414 | write_integer = write_float = write_n 415 | 416 | def write_bool(self, node, depth=0): 417 | self.offset(depth) 418 | self.s(node.label.lower()) 419 | 420 | def write_string(self, node, depth=0): 421 | self.offset(depth) 422 | self.s('AStringFrom("%s")' % node.label) 423 | 424 | def write_type(self, a_type, depth=0): 425 | self.offset(depth) 426 | self.s(self.to_ctype(a_type)) 427 | 428 | 429 | def write_wita_type(self, node, special=False, arg_types=None, return_type=None, depth=0): 430 | self.offset(depth) 431 | if hasattr(node.a_type, 'instance') and node.a_type.instance: 432 | node.a_type = node.a_type.instance 433 | 434 | if isinstance(node.a_type, hm_ast.Function): 435 | self.write_type(node.a_type.types[-1]) 436 | self.ws() 437 | self.lparen() 438 | self.ref() 439 | if special: 440 | self.write_special_ident(node, arg_types, return_type) 441 | else: 442 | self.write_node(node) 443 | self.rparen() 444 | self.lparen() 445 | for a in node.a_type.types[:-2]: 446 | self.write_type(a) 447 | self.comma() 448 | self.write_type(node.a_type.types[-2]) 449 | self.rparen() 450 | 451 | self.current_function_idents.add(node.label) 452 | elif isinstance(node.a_type, hm_ast.List) or node.a_type.name == 'list': 453 | # print('before write_wita_type:', ''.join(self.out)) 454 | i = len(self.out) 455 | self.s('AList_') 456 | self.write_type(node.a_type.types[0]) 457 | self.c.add(self.to_ctype(node.a_type.types[0])) 458 | self.ws() 459 | # print('after write_wita_type:', ''.join(self.out)) 460 | if special: 461 | self.write_special_ident(node, arg_types, return_type) 462 | else: 463 | self.write_node(node) 464 | else: 465 | self.write_type(node.a_type) 466 | self.ws() 467 | if special: 468 | self.write_special_ident(node, arg_types, return_type) 469 | else: 470 | self.write_node(node) 471 | 472 | 473 | def write_assignment(self, node, depth=0): 474 | self.offset(depth) 475 | if node.label.label not in self.scopes[-1]: 476 | self.write_type(node.label.a_type) 477 | self.ws() 478 | self.scopes[-1][node.label.label] = node.label.a_type 479 | self.write_node(node.label) 480 | self.ws() 481 | self.s('=') 482 | self.ws() 483 | self.write_node(node.right) 484 | 485 | def to_ctype(self, a_type): 486 | native_types = {'Integer': 'int', 'Float': 'float', 'Bool': 'bool'} 487 | 488 | if hasattr(a_type, 'instance') and a_type.instance: 489 | a_type = a_type.instance 490 | if hasattr(a_type, 'types'): 491 | for i in range(len(a_type.types)): 492 | if hasattr(a_type.types[i], 'instance') and a_type.types[i].instance: 493 | a_type.types[i] = a_type.types[i].instance 494 | 495 | if hasattr(a_type, 'name') and a_type.name in native_types: 496 | return native_types[a_type.name] 497 | elif hasattr(a_type, 'instance') and a_type.instance and a_type.instance.name in native_types: 498 | return native_types[a_type.instance.name] 499 | else: 500 | return getattr(self, 'to_ctype_' + type(a_type).__name__.lower())(a_type) 501 | 502 | def to_ctype_function(self, a_type): 503 | return '{0}REF{1}'.format(self.to_ctype(a_type.types[0]), '_'.join(self.to_ctype(h) for h in a_type.types[1:])) 504 | 505 | def to_ctype_typeoperator(self, a_type): 506 | if a_type.name == 'list': 507 | self.c.add(self.to_ctype(a_type.types[0])) 508 | return 'AList_{0}'.format(self.to_ctype(a_type.types[0])) 509 | elif a_type.name == 'function': 510 | return 'AFunction' 511 | elif len(a_type.types) == 0: 512 | return 'A{0}'.format(a_type.name) 513 | elif a_type.name == '->': 514 | return '{0}REF{1}'.format(self.to_ctype(a_type.types[0]), 515 | '_'.join(self.to_ctype(h) for h in a_type.types[1:])) 516 | 517 | def to_ctype_typevariable(self, a_type): 518 | i = a_type.instance 519 | if i: 520 | return self.to_ctype(a_type.instance) 521 | elif a_type.name in self.registry: 522 | return self.to_ctype(self.registry[a_type.name]) 523 | else: 524 | return a_type.name 525 | 526 | def to_ctype_list(self, a_type): 527 | self.c.add(self.to_ctype(a_type.types[0])) 528 | return 'AList_{0}'.format(self.to_ctype(a_type.types[0])) 529 | 530 | def to_ctype_nonetype(self, a_type): 531 | return 'None' 532 | 533 | def to_ctype_ctype(self, c_type): 534 | return c_type.label 535 | 536 | -------------------------------------------------------------------------------- /airtight/hindley_milner_ast.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | ''' 3 | .. module:: hindley_milner 4 | :synopsis: An implementation of the Hindley Milner type checking algorithm 5 | based on the Scala code by Andrew Forrest, the Perl code by 6 | Nikita Borisov and the paper "Basic Polymorphic Typechecking" 7 | by Cardelli. 8 | .. moduleauthor:: Robert Smallshire 9 | ''' 10 | '''adapted for airtight from Alexander Ivanov 11 | original version from 12 | http://smallshire.org.uk/sufficientlysmall/2010/04/11/a-hindley-milner-type-inference-implementation-in-python/comment-page-1/ 13 | ''' 14 | 15 | import copy 16 | 17 | # from __future__ import print_function 18 | 19 | #=======================================================# 20 | # Class definitions for the abstract syntax tree nodes 21 | # which comprise the little language for which types 22 | # will be inferred 23 | 24 | class Top: 25 | a_type = None 26 | a_native = False 27 | def annotate(self, a_type): 28 | self.a_type = a_type 29 | return a_type 30 | 31 | class Lambda(Top): 32 | """Lambda abstraction""" 33 | 34 | def __init__(self, v, body, expected=None, return_expected=None): 35 | self.v = v 36 | self.body = body 37 | self.expected = expected 38 | self.return_expected = return_expected 39 | 40 | def __str__(self): 41 | return "(fn {v}@{t} => {body})".format(v=self.v, 42 | t=self.a_type.types[0] if self.a_type else '', 43 | body=self.body) 44 | 45 | class LambdaNoArgs(Top): 46 | '''Lambda with no args''' 47 | 48 | def __init__(self, body): 49 | self.body = body 50 | 51 | def __str__(self): 52 | return "(fn => {body})".format(body=self.body) 53 | 54 | class aList(Top): 55 | """List""" 56 | 57 | def __init__(self, items): 58 | self.items = items 59 | 60 | def __str__(self): 61 | return "[{items}]".format( 62 | items=', '.join(str(item) for item in self.items)) 63 | 64 | class If(Top): 65 | def __init__(self, test, body, orelse): 66 | self.test = test 67 | self.body = body 68 | self.orelse = orelse 69 | 70 | def __str__(self): 71 | return 'If({0}) {1} {2}'.format(str(self.test), str(self.body), str(self.orelse)) 72 | 73 | class For(Top): 74 | def __init__(self, iter, target, body): 75 | self.iter = iter 76 | self.target = target 77 | self.body = body 78 | 79 | def __str__(self): 80 | return 'For {0} in {1} {2}'.format(str(self.target), str(self.iter), str(self.body)) 81 | 82 | class While(Top): 83 | def __init__(self, test, body): 84 | self.test = test 85 | self.body = body 86 | 87 | def __str__(self): 88 | return 'While {0} {1}'.format(str(self.test), str(self.body)) 89 | 90 | class Body(Top): 91 | """A list of expressions""" 92 | 93 | def __init__(self, expression, other): 94 | self.expression = expression 95 | self.other = other 96 | 97 | def __str__(self): 98 | return "(@{expression}\n {other})".format( 99 | expression=str(self.expression), 100 | other=str(self.other)) 101 | 102 | class Ident(Top): 103 | """Identifier""" 104 | 105 | def __init__(self, name): 106 | self.name = name 107 | 108 | def __str__(self): 109 | return '{name}@{type}'.format(name=str(self.name), type=str(self.a_type)) 110 | 111 | class anInteger(Ident): 112 | pass 113 | 114 | class aString(Ident): 115 | def __init__(self, name): 116 | self.name = "'%s'" % name 117 | 118 | class aBoolean(Ident): 119 | pass 120 | 121 | class aFloat(Ident): 122 | pass 123 | 124 | class Apply(Top): 125 | """Function application""" 126 | 127 | def __init__(self, fn, arg): 128 | self.fn = fn 129 | self.arg = arg 130 | 131 | def __str__(self): 132 | return "({fn} {arg})".format(fn=self.fn, arg=self.arg) 133 | 134 | 135 | class Let(Top): 136 | """Let binding""" 137 | 138 | def __init__(self, v, defn, body): 139 | self.v = v 140 | self.defn = defn 141 | self.body = body 142 | 143 | def __str__(self): 144 | return "(let {v} = {defn} in {body})".format(v=self.v, defn=self.defn, body=self.body) 145 | 146 | def Letmany(vs, defns, body): 147 | if len(vs) == 1: 148 | return Let(vs[0], defns[0], body) 149 | else: 150 | return Let(vs[0], defns[0], Letmany(vs[1:], defns[1:], body)) 151 | 152 | class Letrec(Top): 153 | """Letrec binding""" 154 | 155 | def __init__(self, v, defn, body): 156 | self.v = v 157 | self.defn = defn 158 | self.body = body 159 | 160 | def __str__(self): 161 | return "(letrec {v} = {defn} in {body})".format(v=self.v, defn=self.defn, body=self.body) 162 | 163 | 164 | 165 | #=======================================================# 166 | # Exception types 167 | 168 | class NotUnifiedError(Exception): 169 | """Raised if the unification didn't happen""" 170 | 171 | def __init__(self, message): 172 | self.__message = message 173 | 174 | message = property(lambda self: self.__message) 175 | 176 | def __str__(self): 177 | return str(self.message) 178 | 179 | class TypeError(Exception): 180 | """Raised if the type inference algorithm cannot infer types successfully""" 181 | 182 | def __init__(self, message): 183 | self.__message = message 184 | 185 | message = property(lambda self: self.__message) 186 | 187 | def __str__(self): 188 | return str(self.message) 189 | 190 | 191 | class ParseError(Exception): 192 | """Raised if the type environment supplied for is incomplete""" 193 | def __init__(self, message): 194 | self.__message = message 195 | 196 | message = property(lambda self: self.__message) 197 | 198 | def __str__(self): 199 | return str(self.message) 200 | 201 | 202 | 203 | #=======================================================# 204 | # Types and type constructors 205 | 206 | class TypeVariable(object): 207 | """A type variable standing for an arbitrary type. 208 | 209 | All type variables have a unique id, but names are only assigned lazily, 210 | when required. 211 | """ 212 | 213 | next_variable_id = 0 214 | 215 | def __init__(self): 216 | self.id = TypeVariable.next_variable_id 217 | TypeVariable.next_variable_id += 1 218 | self.instance = None 219 | self.__name = None 220 | 221 | next_variable_name = 'a' 222 | 223 | def _getName(self): 224 | """Names are allocated to TypeVariables lazily, so that only TypeVariables 225 | present 226 | """ 227 | if self.__name is None: 228 | self.__name = TypeVariable.next_variable_name 229 | TypeVariable.next_variable_name = chr(ord(TypeVariable.next_variable_name) + 1) 230 | return self.__name 231 | 232 | name = property(_getName) 233 | 234 | def __str__(self): 235 | if self.instance is not None: 236 | return str(self.instance) 237 | else: 238 | return str(self.name) 239 | 240 | def __repr__(self): 241 | return "TypeVariable(id = {0})".format(self.id) 242 | 243 | 244 | class TypeOperator(object): 245 | """An n-ary type constructor which builds a new type from old""" 246 | 247 | def __init__(self, name, types): 248 | self.name = name 249 | self.types = types 250 | 251 | def __str__(self): 252 | num_types = len(self.types) 253 | # print(self.__class__.__name__, self.postfix, len(self.types)) 254 | 255 | if num_types == 0: 256 | return str(self.name) 257 | elif num_types == 1: 258 | return '[{0}]'.format(str(self.types[0])) 259 | elif num_types == 2: 260 | return "({0} {1} {2})".format(str(self.types[0]), str(self.name), str(self.types[1])) 261 | else: 262 | return "{0} {1}" % (str(self.name), ' '.join(map(str, self.types))) 263 | 264 | 265 | class Function(TypeOperator): 266 | """A binary type constructor which builds function types""" 267 | 268 | def __init__(self, from_type, to_type): 269 | super(Function, self).__init__("->", [from_type, to_type]) 270 | 271 | class Union(object): 272 | def __init__(self, *types): 273 | self.types = types 274 | 275 | def __str__(self): 276 | return ' | '.join(str(t) for t in self.types) 277 | 278 | def Multi_Apply(ident, args): 279 | if len(args) == 1: 280 | return Apply(ident, args[0]) 281 | else: 282 | return Apply(Multi_Apply(ident, args[:-1]), args[-1]) 283 | 284 | def Multi_Lambda(args, body, expected=None): 285 | if not expected: 286 | expected = None 287 | rest_expected = [] 288 | else: 289 | rest_expected = expected[1:] 290 | expected = expected[0] 291 | 292 | if len(args) > 1: 293 | return Lambda( 294 | args[0], 295 | Multi_Lambda(args[1:], body, expected=rest_expected), 296 | expected=expected) 297 | elif len(args) == 0: 298 | return LambdaNoArgs(body) 299 | else: 300 | return Lambda(args[0], body, expected=expected, return_expected=None if rest_expected == [] else rest_expected[0]) 301 | 302 | def Multi_Function(types): 303 | if len(types) == 2: 304 | return Function(types[0], types[1]) 305 | else: 306 | return Function(types[0], Multi_Function(types[1:])) 307 | 308 | class List(TypeOperator): 309 | """Builds list types""" 310 | 311 | def __init__(self, element_type): 312 | super(List, self).__init__("list", [element_type]) 313 | 314 | def __str__(self): 315 | return '[{0}]'.format(str(self.types[0])) 316 | 317 | # Basic types are constructed with a nullary type constructor 318 | Integer = TypeOperator("Integer", []) # Basic integer 319 | Bool = TypeOperator("Bool", []) # Basic bool 320 | Float = TypeOperator("Float", []) # Basic float 321 | String = TypeOperator("String", []) # Basic string 322 | 323 | 324 | 325 | 326 | #=======================================================# 327 | # Type inference machinery 328 | 329 | def analyse(node, env, non_generic=None): 330 | """Computes the type of the expression given by node. 331 | 332 | The type of the node is computed in the context of the context of the 333 | supplied type environment env. Data types can be introduced into the 334 | language simply by having a predefined set of identifiers in the initial 335 | environment. environment; this way there is no need to change the syntax or, more 336 | importantly, the type-checking program when extending the language. 337 | 338 | Args: 339 | node: The root of the abstract syntax tree. 340 | env: The type environment is a mapping of expression identifier names 341 | to type assignments. 342 | to type assignments. 343 | non_generic: A set of non-generic variables, or None 344 | 345 | Returns: 346 | The computed type of the expression. 347 | 348 | Raises: 349 | TypeError: The type of the expression could not be inferred, for example 350 | if it is not possible to unify two types such as Integer and Bool 351 | ParseError: The abstract syntax tree rooted at node could not be parsed 352 | """ 353 | 354 | if non_generic is None: 355 | non_generic = set() 356 | 357 | if isinstance(node, Ident): 358 | return node.annotate(getType(node.name, env, non_generic)) 359 | elif isinstance(node, If): 360 | unify(analyse(node.test, env, non_generic), Bool) 361 | node.test.annotate(Bool) 362 | body_type = analyse(node.body, env, non_generic) 363 | orelse_type = analyse(node.body, env, non_generic) 364 | unify(body_type, orelse_type) 365 | return node.annotate(body_type) 366 | elif isinstance(node, Apply): 367 | fun_type = analyse(node.fn, env, non_generic) 368 | arg_type = analyse(node.arg, env, non_generic) 369 | result_type = TypeVariable() 370 | if not isinstance(fun_type, Union): 371 | fun_types = [fun_type] 372 | else: 373 | fun_types = fun_type.types 374 | backup = Function(arg_type, result_type) 375 | found = False 376 | unify(backup, fun_type) 377 | if isinstance(node.arg, Ident): 378 | if node.arg.name not in env: 379 | env[node.arg.name] = arg_type 380 | else: 381 | unify(env[node.arg.name], arg_type) 382 | node.arg.annotate(arg_type) 383 | node.fn.annotate(backup) 384 | 385 | return node.annotate(result_type) 386 | elif isinstance(node, Body): 387 | analyse(node.expression, env, non_generic) 388 | return node.annotate( 389 | analyse(node.other, env, non_generic)) 390 | elif isinstance(node, For): 391 | iter_type = analyse(node.iter, env, non_generic) 392 | target_type = TypeVariable() 393 | unify(List(target_type), iter_type) 394 | node.target.annotate(target_type) 395 | node.iter.annotate(iter_type) 396 | new_env = env.copy() 397 | new_env[node.target.name] = target_type 398 | new_non_generic = non_generic.copy() 399 | new_non_generic.add(target_type) 400 | return node.annotate(analyse(node.body, new_env, new_non_generic)) 401 | elif isinstance(node, While): 402 | test_type = analyse(node.test, env, non_generic) 403 | unify(Bool, test_type) 404 | node.test.annotate(test_type) 405 | return node.annotate(analyse(node.body, env, non_generic)) 406 | elif isinstance(node, Lambda): 407 | arg_type = TypeVariable() 408 | new_env = env.copy() 409 | new_env[node.v] = arg_type 410 | new_non_generic = non_generic.copy() 411 | new_non_generic.add(arg_type) 412 | if node.expected: 413 | expected_type = find_type(node.expected, env) 414 | # print('UNIFY ARG', node.expected, expected_type, arg_type) 415 | unify(expected_type, arg_type) 416 | # print('UN', expected_type, arg_type) 417 | result_type = analyse(node.body, new_env, new_non_generic) 418 | if node.return_expected: 419 | expected_type = find_type(node.return_expected, env) 420 | # print('UNIFY RET', expected_type, result_type) 421 | unify(expected_type, result_type) 422 | # print('UN', expected_type, result_type) 423 | node.a_return_type = result_type 424 | return node.annotate(Function(arg_type, result_type)) 425 | elif isinstance(node, LambdaNoArgs): 426 | return node.annotate(analyse(node.body, env, non_generic)) 427 | elif isinstance(node, aList): 428 | if not node.items: 429 | item_type = TypeVariable() 430 | else: 431 | item_type = find_type(node.items[0], env) 432 | 433 | node.items[0].annotate(item_type) 434 | for j in node.items[1:]: 435 | unify(item_type, find_type(j, env)) 436 | j.annotate(item_type) 437 | return node.annotate(List(item_type)) 438 | elif isinstance(node, Let): 439 | defn_type = analyse(node.defn, env, non_generic) 440 | new_env = env.copy() 441 | 442 | if node.v in new_env: 443 | if isinstance(new_env[node.v], Function): 444 | new_env[node.v] = Union(new_env[node.v], defn_type) 445 | elif isinstance(new_env[node.v], Union): 446 | new_env[node.v].types.append(defn_type) 447 | else: 448 | new_env[node.v] = defn_type 449 | node.defn.annotate(new_env[node.v]) 450 | return node.annotate(analyse(node.body, new_env, non_generic)) 451 | elif isinstance(node, Letrec): 452 | new_type = TypeVariable() 453 | new_env = env.copy() 454 | new_env[node.v] = new_type 455 | new_non_generic = non_generic.copy() 456 | new_non_generic.add(new_type) 457 | defn_type = analyse(node.defn, new_env, new_non_generic) 458 | unify(new_type, defn_type) 459 | node.defn.annotate(defn_type) 460 | return node.annotate(analyse(node.body, new_env, non_generic)) 461 | assert 0, "Unhandled syntax node {0}".format(node) 462 | 463 | def find_type(expected, env): 464 | if isinstance(expected, Function): 465 | for i in range(len(expected.types)): 466 | expected.types[i] = find_type(expected.types[i], env) 467 | return expected 468 | elif isinstance(expected, Union): 469 | for i in range(len(expected.types)): 470 | expected.types[i] = find_type(expected.types[i], env) 471 | return expected 472 | elif isinstance(expected, List): 473 | expected.types[0] = find_type(expected.types[0], env) 474 | return expected 475 | elif isinstance(expected, Ident): 476 | return getType(expected, env, set()) 477 | elif isinstance(expected, TypeOperator) and not expected.types: 478 | return env.get(expected.name, expected) 479 | elif isinstance(expected, TypeVariable): 480 | return expected 481 | 482 | def getType(name, env, non_generic): 483 | """Get the type of identifier name from the type environment env. 484 | 485 | Args: 486 | name: The identifier name 487 | env: The type environment mapping from identifier names to types 488 | non_generic: A set of non-generic TypeVariables 489 | 490 | Raises: 491 | ParseError: Raised if name is an undefined symbol in the type 492 | environment. 493 | """ 494 | if name in env: 495 | type_ = env[name] 496 | if isinstance(type_, list): 497 | return [fresh(t, non_generic) for t in type_] 498 | else: 499 | return fresh(type_, non_generic) 500 | else: 501 | if isinstance(name, Ident): 502 | name = name.name 503 | types_of_name = { 504 | int: Integer, 505 | float: Float, 506 | bool: Bool 507 | } 508 | if type(name) in types_of_name: 509 | return types_of_name[type(name)] 510 | elif len(name) != 0 and name[0] == "'" and name[-1] == "'": 511 | return String 512 | else: 513 | raise ParseError("Undefined symbol {0}".format(name)) 514 | 515 | 516 | def fresh(t, non_generic): 517 | """Makes a copy of a type expression. 518 | 519 | The type t is copied. The the generic variables are duplicated and the 520 | non_generic variables are shared. 521 | 522 | Args: 523 | t: A type to be copied. 524 | non_generic: A set of non-generic TypeVariables 525 | """ 526 | mappings = {} # A mapping of TypeVariables to TypeVariables 527 | 528 | def freshrec(tp): 529 | p = prune(tp) 530 | if isinstance(p, TypeVariable): 531 | if isGeneric(p, non_generic): 532 | if p not in mappings: 533 | mappings[p] = TypeVariable() 534 | return mappings[p] 535 | else: 536 | return p 537 | elif isinstance(p, TypeOperator): 538 | return TypeOperator(p.name, [freshrec(x) for x in p.types]) 539 | elif isinstance(p, Union): 540 | return Union(*[freshrec(x) for x in p.types]) 541 | return freshrec(t) 542 | 543 | 544 | def unify(t1, t2): 545 | """Unify the two types t1 and t2. 546 | 547 | Makes the types t1 and t2 the same. 548 | 549 | Args: 550 | t1: The first type to be made equivalent 551 | t2: The second type to be be equivalent 552 | 553 | Returns: 554 | None 555 | 556 | Raises: 557 | TypeError: Raised if the types cannot be unified. 558 | """ 559 | 560 | a = prune(t1) 561 | b = prune(t2) 562 | # print('b:', a, type(a), '\t', b, type(b)) 563 | if isinstance(a, TypeVariable): 564 | if a != b: 565 | if occursInType(a, b): 566 | raise TypeError("recursive unification") 567 | a.instance = b 568 | elif isinstance(a, (TypeOperator, Union)) and isinstance(b, TypeVariable): 569 | unify(b, a) 570 | elif isinstance(a, TypeOperator) and isinstance(b, TypeOperator): 571 | if (a.name != b.name or len(a.types) != len(b.types)): 572 | raise TypeError("Type mismatch: {0} != {1}".format(str(a), str(b))) 573 | for p, q in zip(a.types, b.types): 574 | unify(p, q) 575 | elif isinstance(a, TypeOperator) and isinstance(b, Union): 576 | for z in b.types: 577 | try: 578 | unify(a, z) 579 | return 580 | except (NotUnifiedError, TypeError) as e: 581 | continue 582 | raise NotUnifiedError('{0} x {1}'.format(str(a), str(b))) 583 | elif isinstance(a, Union) and isinstance(b, TypeOperator): 584 | for z in a.types: 585 | try: 586 | unify(z, b) 587 | return 588 | except (NotUnifiedError, TypeError) as e: 589 | continue 590 | raise NotUnifiedError('{0} x {1}'.format(str(a), str(b))) 591 | elif isinstance(a, Union) and isinstance(b, Union): 592 | for y in a.types: 593 | for z in b.types: 594 | try: 595 | unify(y, z) 596 | return 597 | except (NotUnifiedError, TypeError) as e: 598 | continue 599 | raise NotUnifiedError('{0} x {1}'.format(str(a), str(b))) 600 | else: 601 | raise NotUnifiedError('{0} x {1}'.format(str(a), str(b))) 602 | 603 | 604 | def prune(t): 605 | """Returns the currently defining instance of t. 606 | 607 | As a side effect, collapses the list of type instances. The function Prune 608 | is used whenever a type expression has to be inspected: it will always 609 | return a type expression which is either an uninstantiated type variable or 610 | a type operator; i.e. it will skip instantiated variables, and will 611 | actually prune them from expressions to remove long chains of instantiated 612 | variables. 613 | 614 | Args: 615 | t: The type to be pruned 616 | 617 | Returns: 618 | An uninstantiated TypeVariable or a TypeOperator 619 | """ 620 | if isinstance(t, TypeVariable): 621 | if t.instance is not None: 622 | t.instance = prune(t.instance) 623 | return t.instance 624 | return t 625 | 626 | 627 | def isGeneric(v, non_generic): 628 | """Checks whether a given variable occurs in a list of non-generic variables 629 | 630 | Note that a variables in such a list may be instantiated to a type term, 631 | in which case the variables contained in the type term are considered 632 | non-generic. 633 | 634 | Note: Must be called with v pre-pruned 635 | 636 | Args: 637 | v: The TypeVariable to be tested for genericity 638 | non_generic: A set of non-generic TypeVariables 639 | 640 | Returns: 641 | True if v is a generic variable, otherwise False 642 | """ 643 | return not occursIn(v, non_generic) 644 | 645 | 646 | def occursInType(v, type2): 647 | """Checks whether a type variable occurs in a type expression. 648 | 649 | Note: Must be called with v pre-pruned 650 | 651 | Args: 652 | v: The TypeVariable to be tested for 653 | type2: The type in which to search 654 | 655 | Returns: 656 | True if v occurs in type2, otherwise False 657 | """ 658 | pruned_type2 = prune(type2) 659 | if pruned_type2 == v: 660 | return True 661 | elif isinstance(pruned_type2, TypeOperator): 662 | return occursIn(v, pruned_type2.types) 663 | return False 664 | 665 | 666 | def occursIn(t, types): 667 | """Checks whether a types variable occurs in any other types. 668 | 669 | Args: 670 | v: The TypeVariable to be tested for 671 | types: The sequence of types in which to search 672 | 673 | Returns: 674 | True if t occurs in any of types, otherwise False 675 | """ 676 | return any(occursInType(t, t2) for t2 in types) 677 | 678 | def isIntegerLiteral(name): 679 | """Checks whether name is an integer literal string. 680 | 681 | Args: 682 | name: The identifier to check 683 | 684 | Returns: 685 | True if name is an integer literal, otherwise False 686 | """ 687 | result = True 688 | try: 689 | int(name) 690 | except ValueError: 691 | result = False 692 | return result 693 | 694 | #==================================================================# 695 | # Example code to exercise the above 696 | 697 | 698 | def tryExp(env, node): 699 | """Try to evaluate a type printing the result or reporting errors. 700 | 701 | Args: 702 | env: The type environment in which to evaluate the expression. 703 | node: The root node of the abstract syntax tree of the expression. 704 | 705 | Returns: 706 | None 707 | """ 708 | try: 709 | t = analyse(node, env) 710 | print(str(t)) 711 | except (ParseError, TypeError) as e: 712 | print(e) 713 | 714 | 715 | def main(): 716 | """The main example program. 717 | 718 | Sets up some predefined types using the type constructors TypeVariable, 719 | TypeOperator and Function. Creates a list of example expressions to be 720 | evaluated. Evaluates the expressions, printing the type or errors arising 721 | from each. 722 | 723 | Returns: 724 | None 725 | """ 726 | 727 | var1 = TypeVariable() 728 | var2 = TypeVariable() 729 | pair_type = TypeOperator("*", (var1, var2)) 730 | var4 = TypeVariable() 731 | var5 = TypeVariable() 732 | var6 = TypeVariable() 733 | var7 = TypeVariable() 734 | # List = TypeOperator("list", (var4,)) 735 | list_type = List(var4) 736 | var3 = TypeVariable() 737 | 738 | my_env = { "pair" : Function(var1, Function(var2, pair_type)), # var1 -> var2 -> (* var1 var2) 739 | "true" : Bool, 740 | "list" : list_type, # (list var4) 741 | "map" : Function(Function(var5, var6), 742 | Function(List(var5), List(var6))), # (var5 -> var6) -> (list var5)-> (list var6) 743 | "append": 744 | Multi_Function([List(var7), var7, List(var7)]), 745 | "cond" : Function(Bool, Function(var3, Function(var3, var3))), 746 | "zero" : Function(Integer, Bool), 747 | "pred" : Function(Integer, Integer), 748 | "times": Function(Integer, Function(Integer, Integer)) } 749 | 750 | pair = Apply(Apply(Ident("pair"), Apply(Ident("f"), Ident("4"))), Apply(Ident("f"), Ident("true"))) 751 | 752 | examples = [ 753 | #list 754 | Let("e0", # let f = [1] 755 | aList([]), 756 | Let("e", 757 | Apply( 758 | Apply(Ident("append"), 759 | Ident("e0")), 760 | Ident("0")), 761 | Ident("e"))), 762 | 763 | Let("e0", # let e0 = (append [0] 0) 764 | Apply( 765 | Apply(Ident("append"), 766 | aList([Ident("0")])), Ident("0")), 767 | Ident("e0")), 768 | 769 | # factorial 770 | Letrec("factorial", # letrec factorial = 771 | Lambda("n", # fn n => 772 | Apply( 773 | Apply( # cond (zero n) 1 774 | Apply(Ident("cond"), # cond (zero n) 775 | Apply(Ident("zero"), Ident("n"))), 776 | Ident("1")), 777 | Apply( # times n 778 | Apply(Ident("times"), Ident("n")), 779 | Apply(Ident("factorial"), 780 | Apply(Ident("pred"), Ident("n"))) 781 | ) 782 | ) 783 | ), # in 784 | Apply(Ident("factorial"), Ident("5")) 785 | ), 786 | 787 | # Should fail: 788 | # fn x => (pair(x(3) (x(true))) 789 | Lambda("x", 790 | Apply( 791 | Apply(Ident("pair"), 792 | Apply(Ident("x"), Ident("3"))), 793 | Apply(Ident("x"), Ident("true")))), 794 | 795 | # pair(f(3), f(true)) 796 | Apply( 797 | Apply(Ident("pair"), Apply(Ident("f"), Ident("4"))), 798 | Apply(Ident("f"), Ident("true"))), 799 | 800 | 801 | # let f = (fn x => x) in ((pair (f 4)) (f true)) 802 | Let("f", Lambda("x", Ident("x")), pair), 803 | 804 | # fn f => f f (fail) 805 | Lambda("f", Apply(Ident("f"), Ident("f"))), 806 | 807 | # let g = fn f => 5 in g g 808 | Let("g", 809 | Lambda("f", Ident("5")), 810 | Apply(Ident("g"), Ident("g"))), 811 | 812 | # example that demonstrates generic and non-generic variables: 813 | # fn g => let f = fn x => g in pair (f 3, f true) 814 | Lambda("g", 815 | Let("f", 816 | Lambda("x", Ident("g")), 817 | Apply( 818 | Apply(Ident("pair"), 819 | Apply(Ident("f"), Ident("3")) 820 | ), 821 | Apply(Ident("f"), Ident("true"))))), 822 | 823 | 824 | # Function composition 825 | # fn f (fn g (fn arg (f g arg))) 826 | Lambda("f", Lambda("g", Lambda("arg", Apply(Ident("g"), Apply(Ident("f"), Ident("arg")))))) 827 | ] 828 | 829 | for example in examples: 830 | tryExp(my_env, example) 831 | 832 | 833 | if __name__ == '__main__': 834 | main() 835 | --------------------------------------------------------------------------------