type-inference #39
6
Makefile
@@ -81,8 +81,8 @@ semen.o: semen.module.scm semen.scm infer.o sex-macros.o sex-modules.o types.o u
|
||||
sex-fmt-c.o: sex-fmt-c.scm
|
||||
$(CHICKEN_C) $(CSC_FLAGS) $(MODULE_FLAGS) sex-fmt-c.scm -o sex-fmt-c.o -unit sex-fmt-c
|
||||
|
||||
fmt-c-writer.o: fmt-c-writer.module.scm fmt-c-writer.scm sex-fmt-c.o utils.o
|
||||
$(CHICKEN_C) $(CSC_FLAGS) $(MODULE_FLAGS) fmt-c-writer.module.scm -o fmt-c-writer.o -unit fmt-c-writer -link sex-fmt-c,utils
|
||||
fmt-c-writer.o: fmt-c-writer.module.scm fmt-c-writer.scm sex-fmt-c.o types.o utils.o
|
||||
$(CHICKEN_C) $(CSC_FLAGS) $(MODULE_FLAGS) fmt-c-writer.module.scm -o fmt-c-writer.o -unit fmt-c-writer -link sex-fmt-c,types,utils
|
||||
|
||||
sexc.o: sexc.module.scm infer.o types.o sexc.scm fmt-c-writer.o sex-macros.o sex-modules.o reader.o semen.o utils.o
|
||||
$(CHICKEN_C) $(CSC_FLAGS) $(MODULE_FLAGS) sexc.module.scm -o sexc.o -unit sexc -link fmt-c-writer,sex-macros,sex-modules,reader,semen,infer,types,utils
|
||||
@@ -98,7 +98,7 @@ sextest:
|
||||
|
||||
SEX_TEST_PROGRAMS = hello-world lists comments unicode serialize features \
|
||||
|
|
||||
feature-flags lambdas compound-literals closures fixpoint \
|
||||
wildcards inference
|
||||
wildcards inference type-shapes
|
||||
|
||||
# Multi-module linking is checked end to end; see tests/modules/Makefile.
|
||||
check-modules: sexc
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
(chicken irregex) ; unkebabify
|
||||
srfi-1 ; lists
|
||||
srfi-13 ; strings
|
||||
types ; array-bound?, named-arg?
|
||||
utils)
|
||||
|
||||
;;; egg `tree' not ported to CHICKEN 6 yet
|
||||
@@ -292,27 +293,6 @@ forms, and what remains."
|
||||
(list)
|
||||
(walk-expr (drop form 3)))))) ; optional init expression
|
||||
|
||||
;;; `(¤ int N)' is N of int
|
||||
;;; `(¤ unsigned int)' is an unsized array of unsigned int
|
||||
;;; An aggregate is the exception -- `(¤ struct point)' ends in a tag,
|
||||
;;; which is part of the type and not a bound.
|
||||
|
||||
(define +c-type-words+
|
||||
'(void char short int long float double signed unsigned
|
||||
bool _Bool complex _Complex _Atomic const volatile restrict))
|
||||
|
||||
(define (array-bound? array-type)
|
||||
(and (> (length array-type) 1)
|
||||
(let ((bound (last array-type))
|
||||
(preceding (last (drop-right array-type 1))))
|
||||
(cond
|
||||
((not (symbol? bound)) #t)
|
||||
((memq bound +c-type-words+) #f)
|
||||
;; a tag always follows its keyword, so `(¤ * struct tt)' ends
|
||||
;; in a name belonging to the type
|
||||
((memq preceding '(struct union enum)) #f)
|
||||
(else #t)))))
|
||||
|
||||
(define (walk-type form)
|
||||
;; int -> int
|
||||
;; (const int) -> const int
|
||||
@@ -323,16 +303,11 @@ forms, and what remains."
|
||||
;; (fn ((int) (float)) void) -> (%fun void ((int) (float)))
|
||||
(match form
|
||||
(('¤ . array-type)
|
||||
(if (array-bound? array-type)
|
||||
;; sized array
|
||||
(let* ((type-list (drop-right array-type 1))
|
||||
(type (maybe-unwrap-type type-list))
|
||||
(size (last array-type)))
|
||||
`(%array ,(walk-type type)
|
||||
,size))
|
||||
(if (array-bound? form)
|
||||
`(%array ,(walk-type (array-element-type form)) ,(last array-type))
|
||||
;; sugar for pointer... Do we really need it? Guess why not,
|
||||
;; it's a strong semantic cue
|
||||
`(%array ,(walk-type (maybe-unwrap-type array-type)))))
|
||||
`(%array ,(walk-type (array-element-type form)))))
|
||||
(('fn arglist ret-type)
|
||||
`(%fun ,(walk-type ret-type) ,(walk-arg-types arglist)))
|
||||
(('fn . _)
|
||||
@@ -398,21 +373,6 @@ forms, and what remains."
|
||||
.
|
||||
,(walk-body maybe-body)))))
|
||||
|
||||
;;; TODO: isn't there a better way?
|
||||
(define (is-probably-type form)
|
||||
(case (car form)
|
||||
((¤ * const volatile struct union) #t)
|
||||
(else #f)))
|
||||
|
||||
;;; Does the parameter name itself?
|
||||
;;; (f1 float) does
|
||||
;;; (float), (const char) and (¤ float 4) do not
|
||||
(define (named-arg? arg)
|
||||
(and (pair? arg)
|
||||
(pair? (cdr arg)) ; 1 element args are always type
|
||||
(not (eq? (car arg) '¤))
|
||||
(not (is-probably-type arg))))
|
||||
|
||||
;;; The type of one parameter
|
||||
(define (arg-type arg)
|
||||
(if (named-arg? arg)
|
||||
|
||||
@@ -276,7 +276,7 @@
|
||||
|
pkulev
commented
When the ranks tie this keeps the left operand, so the declared type depends on the order of the source. With
When the ranks tie this keeps the left operand, so the declared type depends on the order of the source.
With `n` a `size-t` (an unparsed name, rank 1, same as `int`):
```c
int m1 = 1 + n;
size_t m2 = n + 1;
```
`cc` accepts both. C types `1 + n` as `size_t`, and the `int` declaration narrows. The same tie makes `(+ i u)` an `int` and `(+ u i)` an `unsigned int`. `char + char` is declared `char`.
`size-t` never reaches the `long` rank because it is not a typedef Sex has parsed, so the `strlen` example's `(+ 1 n)` lands here.
pkulev
commented
The forms that reach this walk are
The forms that reach this walk are `&&`, `||`, `|`, `&`, `<<`, `>>`, `++`. `c-and` and `c-or` are the names from before those spellings. The writer already emits `a && b` from `(&& a b)`.
`(var ok _ (&& a b))` and `(var x _ (<< a b))` and `(var x _ (++ a))` all stop with `cannot infer the type of`.
pkulev
commented
An array operand is returned as the array. emits An array operand is returned as the array. `decay` in `infer.scm` is the conversion this comment describes, and nothing in the walk calls it.
```sex
(var a (¤ int 4) #(1 2 3 4))
(var p _ (+ a 1))
```
emits `int p[4] = a + 1`. `cc`: `invalid initializer`.
pkulev
commented
A subscript types only when the base's surface form is already stops with A subscript types only when the base's surface form is already `(¤ ...)`. An `(* int)` is not, so
```sex
(fn f ((p (* int))) int
(var e _ (¤ p 0))
(return e))
```
stops with `cannot infer the type of`. This is the other use named on `decay`.
pkulev
commented
`resolve-closure-types` runs here and `resolve-wildcard` does not. `(var n _ 1)` at toplevel emits `static _ n = 1`. `cc`: `unknown type name '_'`. The same form inside a function becomes `int n = 1`.
pkulev
commented
`walk-parts` walks the type as well as the initializer. A list whose head is an in-scope closure is rewritten as a closure call, including a parameter name inside a `fn` type.
```sex
(var c (closure ((int)) int) (make-adder 1))
(var fp (fn ((c int)) int) (lambda ((c int)) int (return c)))
```
`-m` shows `(var fp (fn ((ƛint_int_call c int)) int) ...)`, and the C is `int (*fp)(c int)`. With no closure named `c`, the same declaration emits `int (*fp)(int)`.
pkulev
commented
The callee is typed before it is walked, so a closure literal has no type yet and falls through to
The callee is typed before it is walked, so a closure literal has no type yet and falls through to `convert-arguments`. After the walk the form is a call of the constructor:
```c
return ƛ0_f_make()(5);
```
`cc`: `called object is not a function or function pointer`. `((lambda ((x int)) int (return x)) 5)` works, because the lifted function is registered before the call is typed. `((make-adder 20) 22)` works, because the callee is already a call with a closure return type.
pkulev
commented
When the ranks tie this keeps the left operand, so the declared type depends on the order of the source. With
When the ranks tie this keeps the left operand, so the declared type depends on the order of the source.
With `n` a `size-t` (an unparsed name, rank 1, same as `int`):
```c
int m1 = 1 + n;
size_t m2 = n + 1;
```
`cc` accepts both. C types `1 + n` as `size_t`, and the `int` declaration narrows. The same tie makes `(+ i u)` an `int` and `(+ u i)` an `unsigned int`. `char + char` is declared `char`.
`size-t` never reaches the `long` rank because it is not a typedef Sex has parsed, so the `strlen` example's `(+ 1 n)` lands here.
pkulev
commented
The forms that reach this walk are
The forms that reach this walk are `&&`, `||`, `|`, `&`, `<<`, `>>`, `++`. `c-and` and `c-or` are the names from before those spellings. The writer already emits `a && b` from `(&& a b)`.
`(var ok _ (&& a b))` and `(var x _ (<< a b))` and `(var x _ (++ a))` all stop with `cannot infer the type of`.
pkulev
commented
An array operand is returned as the array. emits An array operand is returned as the array. `decay` in `infer.scm` is the conversion this comment describes, and nothing in the walk calls it.
```sex
(var a (¤ int 4) #(1 2 3 4))
(var p _ (+ a 1))
```
emits `int p[4] = a + 1`. `cc`: `invalid initializer`.
pkulev
commented
A subscript types only when the base's surface form is already stops with A subscript types only when the base's surface form is already `(¤ ...)`. An `(* int)` is not, so
```sex
(fn f ((p (* int))) int
(var e _ (¤ p 0))
(return e))
```
stops with `cannot infer the type of`. This is the other use named on `decay`.
pkulev
commented
`resolve-closure-types` runs here and `resolve-wildcard` does not. `(var n _ 1)` at toplevel emits `static _ n = 1`. `cc`: `unknown type name '_'`. The same form inside a function becomes `int n = 1`.
pkulev
commented
`walk-parts` walks the type as well as the initializer. A list whose head is an in-scope closure is rewritten as a closure call, including a parameter name inside a `fn` type.
```sex
(var c (closure ((int)) int) (make-adder 1))
(var fp (fn ((c int)) int) (lambda ((c int)) int (return c)))
```
`-m` shows `(var fp (fn ((ƛint_int_call c int)) int) ...)`, and the C is `int (*fp)(c int)`. With no closure named `c`, the same declaration emits `int (*fp)(int)`.
pkulev
commented
The callee is typed before it is walked, so a closure literal has no type yet and falls through to
The callee is typed before it is walked, so a closure literal has no type yet and falls through to `convert-arguments`. After the walk the form is a call of the constructor:
```c
return ƛ0_f_make()(5);
```
`cc`: `called object is not a function or function pointer`. `((lambda ((x int)) int (return x)) 5)` works, because the lifted function is registered before the call is typed. `((make-adder 20) 22)` works, because the callee is already a call with a closure return type.
|
||||
;;; through untouched.
|
||||
(define (fn-type-of fn-form)
|
||||
`(fn ,(map (lambda (param)
|
||||
(if (and (pair? param) (= 2 (length param)))
|
||||
|
pkulev
commented
When the ranks tie this keeps the left operand, so the declared type depends on the order of the source. With
When the ranks tie this keeps the left operand, so the declared type depends on the order of the source.
With `n` a `size-t` (an unparsed name, rank 1, same as `int`):
```c
int m1 = 1 + n;
size_t m2 = n + 1;
```
`cc` accepts both. C types `1 + n` as `size_t`, and the `int` declaration narrows. The same tie makes `(+ i u)` an `int` and `(+ u i)` an `unsigned int`. `char + char` is declared `char`.
`size-t` never reaches the `long` rank because it is not a typedef Sex has parsed, so the `strlen` example's `(+ 1 n)` lands here.
pkulev
commented
The forms that reach this walk are
The forms that reach this walk are `&&`, `||`, `|`, `&`, `<<`, `>>`, `++`. `c-and` and `c-or` are the names from before those spellings. The writer already emits `a && b` from `(&& a b)`.
`(var ok _ (&& a b))` and `(var x _ (<< a b))` and `(var x _ (++ a))` all stop with `cannot infer the type of`.
pkulev
commented
An array operand is returned as the array. emits An array operand is returned as the array. `decay` in `infer.scm` is the conversion this comment describes, and nothing in the walk calls it.
```sex
(var a (¤ int 4) #(1 2 3 4))
(var p _ (+ a 1))
```
emits `int p[4] = a + 1`. `cc`: `invalid initializer`.
pkulev
commented
A subscript types only when the base's surface form is already stops with A subscript types only when the base's surface form is already `(¤ ...)`. An `(* int)` is not, so
```sex
(fn f ((p (* int))) int
(var e _ (¤ p 0))
(return e))
```
stops with `cannot infer the type of`. This is the other use named on `decay`.
pkulev
commented
`resolve-closure-types` runs here and `resolve-wildcard` does not. `(var n _ 1)` at toplevel emits `static _ n = 1`. `cc`: `unknown type name '_'`. The same form inside a function becomes `int n = 1`.
pkulev
commented
`walk-parts` walks the type as well as the initializer. A list whose head is an in-scope closure is rewritten as a closure call, including a parameter name inside a `fn` type.
```sex
(var c (closure ((int)) int) (make-adder 1))
(var fp (fn ((c int)) int) (lambda ((c int)) int (return c)))
```
`-m` shows `(var fp (fn ((ƛint_int_call c int)) int) ...)`, and the C is `int (*fp)(c int)`. With no closure named `c`, the same declaration emits `int (*fp)(int)`.
pkulev
commented
The callee is typed before it is walked, so a closure literal has no type yet and falls through to
The callee is typed before it is walked, so a closure literal has no type yet and falls through to `convert-arguments`. After the walk the form is a call of the constructor:
```c
return ƛ0_f_make()(5);
```
`cc`: `called object is not a function or function pointer`. `((lambda ((x int)) int (return x)) 5)` works, because the lifted function is registered before the call is typed. `((make-adder 20) 22)` works, because the callee is already a call with a closure return type.
|
||||
(if (and (pair? param) (= 2 (length param)) (named-arg? param))
|
||||
|
pkulev
commented
When the ranks tie this keeps the left operand, so the declared type depends on the order of the source. With
When the ranks tie this keeps the left operand, so the declared type depends on the order of the source.
With `n` a `size-t` (an unparsed name, rank 1, same as `int`):
```c
int m1 = 1 + n;
size_t m2 = n + 1;
```
`cc` accepts both. C types `1 + n` as `size_t`, and the `int` declaration narrows. The same tie makes `(+ i u)` an `int` and `(+ u i)` an `unsigned int`. `char + char` is declared `char`.
`size-t` never reaches the `long` rank because it is not a typedef Sex has parsed, so the `strlen` example's `(+ 1 n)` lands here.
pkulev
commented
The forms that reach this walk are
The forms that reach this walk are `&&`, `||`, `|`, `&`, `<<`, `>>`, `++`. `c-and` and `c-or` are the names from before those spellings. The writer already emits `a && b` from `(&& a b)`.
`(var ok _ (&& a b))` and `(var x _ (<< a b))` and `(var x _ (++ a))` all stop with `cannot infer the type of`.
pkulev
commented
An array operand is returned as the array. emits An array operand is returned as the array. `decay` in `infer.scm` is the conversion this comment describes, and nothing in the walk calls it.
```sex
(var a (¤ int 4) #(1 2 3 4))
(var p _ (+ a 1))
```
emits `int p[4] = a + 1`. `cc`: `invalid initializer`.
pkulev
commented
A subscript types only when the base's surface form is already stops with A subscript types only when the base's surface form is already `(¤ ...)`. An `(* int)` is not, so
```sex
(fn f ((p (* int))) int
(var e _ (¤ p 0))
(return e))
```
stops with `cannot infer the type of`. This is the other use named on `decay`.
pkulev
commented
`resolve-closure-types` runs here and `resolve-wildcard` does not. `(var n _ 1)` at toplevel emits `static _ n = 1`. `cc`: `unknown type name '_'`. The same form inside a function becomes `int n = 1`.
pkulev
commented
`walk-parts` walks the type as well as the initializer. A list whose head is an in-scope closure is rewritten as a closure call, including a parameter name inside a `fn` type.
```sex
(var c (closure ((int)) int) (make-adder 1))
(var fp (fn ((c int)) int) (lambda ((c int)) int (return c)))
```
`-m` shows `(var fp (fn ((ƛint_int_call c int)) int) ...)`, and the C is `int (*fp)(c int)`. With no closure named `c`, the same declaration emits `int (*fp)(int)`.
pkulev
commented
The callee is typed before it is walked, so a closure literal has no type yet and falls through to
The callee is typed before it is walked, so a closure literal has no type yet and falls through to `convert-arguments`. After the walk the form is a call of the constructor:
```c
return ƛ0_f_make()(5);
```
`cc`: `called object is not a function or function pointer`. `((lambda ((x int)) int (return x)) 5)` works, because the lifted function is registered before the call is typed. `((make-adder 20) 22)` works, because the callee is already a call with a closure return type.
|
||||
(list (second param))
|
||||
param))
|
||||
(sex-fn-arglist fn-form))
|
||||
@@ -550,9 +550,10 @@
|
||||
|
pkulev
commented
When the ranks tie this keeps the left operand, so the declared type depends on the order of the source. With
When the ranks tie this keeps the left operand, so the declared type depends on the order of the source.
With `n` a `size-t` (an unparsed name, rank 1, same as `int`):
```c
int m1 = 1 + n;
size_t m2 = n + 1;
```
`cc` accepts both. C types `1 + n` as `size_t`, and the `int` declaration narrows. The same tie makes `(+ i u)` an `int` and `(+ u i)` an `unsigned int`. `char + char` is declared `char`.
`size-t` never reaches the `long` rank because it is not a typedef Sex has parsed, so the `strlen` example's `(+ 1 n)` lands here.
pkulev
commented
The forms that reach this walk are
The forms that reach this walk are `&&`, `||`, `|`, `&`, `<<`, `>>`, `++`. `c-and` and `c-or` are the names from before those spellings. The writer already emits `a && b` from `(&& a b)`.
`(var ok _ (&& a b))` and `(var x _ (<< a b))` and `(var x _ (++ a))` all stop with `cannot infer the type of`.
pkulev
commented
An array operand is returned as the array. emits An array operand is returned as the array. `decay` in `infer.scm` is the conversion this comment describes, and nothing in the walk calls it.
```sex
(var a (¤ int 4) #(1 2 3 4))
(var p _ (+ a 1))
```
emits `int p[4] = a + 1`. `cc`: `invalid initializer`.
pkulev
commented
A subscript types only when the base's surface form is already stops with A subscript types only when the base's surface form is already `(¤ ...)`. An `(* int)` is not, so
```sex
(fn f ((p (* int))) int
(var e _ (¤ p 0))
(return e))
```
stops with `cannot infer the type of`. This is the other use named on `decay`.
pkulev
commented
`resolve-closure-types` runs here and `resolve-wildcard` does not. `(var n _ 1)` at toplevel emits `static _ n = 1`. `cc`: `unknown type name '_'`. The same form inside a function becomes `int n = 1`.
pkulev
commented
`walk-parts` walks the type as well as the initializer. A list whose head is an in-scope closure is rewritten as a closure call, including a parameter name inside a `fn` type.
```sex
(var c (closure ((int)) int) (make-adder 1))
(var fp (fn ((c int)) int) (lambda ((c int)) int (return c)))
```
`-m` shows `(var fp (fn ((ƛint_int_call c int)) int) ...)`, and the C is `int (*fp)(c int)`. With no closure named `c`, the same declaration emits `int (*fp)(int)`.
pkulev
commented
The callee is typed before it is walked, so a closure literal has no type yet and falls through to
The callee is typed before it is walked, so a closure literal has no type yet and falls through to `convert-arguments`. After the walk the form is a call of the constructor:
```c
return ƛ0_f_make()(5);
```
`cc`: `called object is not a function or function pointer`. `((lambda ((x int)) int (return x)) 5)` works, because the lifted function is registered before the call is typed. `((make-adder 20) 22)` works, because the callee is already a call with a closure return type.
pkulev
commented
When the ranks tie this keeps the left operand, so the declared type depends on the order of the source. With
When the ranks tie this keeps the left operand, so the declared type depends on the order of the source.
With `n` a `size-t` (an unparsed name, rank 1, same as `int`):
```c
int m1 = 1 + n;
size_t m2 = n + 1;
```
`cc` accepts both. C types `1 + n` as `size_t`, and the `int` declaration narrows. The same tie makes `(+ i u)` an `int` and `(+ u i)` an `unsigned int`. `char + char` is declared `char`.
`size-t` never reaches the `long` rank because it is not a typedef Sex has parsed, so the `strlen` example's `(+ 1 n)` lands here.
pkulev
commented
The forms that reach this walk are
The forms that reach this walk are `&&`, `||`, `|`, `&`, `<<`, `>>`, `++`. `c-and` and `c-or` are the names from before those spellings. The writer already emits `a && b` from `(&& a b)`.
`(var ok _ (&& a b))` and `(var x _ (<< a b))` and `(var x _ (++ a))` all stop with `cannot infer the type of`.
pkulev
commented
An array operand is returned as the array. emits An array operand is returned as the array. `decay` in `infer.scm` is the conversion this comment describes, and nothing in the walk calls it.
```sex
(var a (¤ int 4) #(1 2 3 4))
(var p _ (+ a 1))
```
emits `int p[4] = a + 1`. `cc`: `invalid initializer`.
pkulev
commented
A subscript types only when the base's surface form is already stops with A subscript types only when the base's surface form is already `(¤ ...)`. An `(* int)` is not, so
```sex
(fn f ((p (* int))) int
(var e _ (¤ p 0))
(return e))
```
stops with `cannot infer the type of`. This is the other use named on `decay`.
pkulev
commented
`resolve-closure-types` runs here and `resolve-wildcard` does not. `(var n _ 1)` at toplevel emits `static _ n = 1`. `cc`: `unknown type name '_'`. The same form inside a function becomes `int n = 1`.
pkulev
commented
`walk-parts` walks the type as well as the initializer. A list whose head is an in-scope closure is rewritten as a closure call, including a parameter name inside a `fn` type.
```sex
(var c (closure ((int)) int) (make-adder 1))
(var fp (fn ((c int)) int) (lambda ((c int)) int (return c)))
```
`-m` shows `(var fp (fn ((ƛint_int_call c int)) int) ...)`, and the C is `int (*fp)(c int)`. With no closure named `c`, the same declaration emits `int (*fp)(int)`.
pkulev
commented
The callee is typed before it is walked, so a closure literal has no type yet and falls through to
The callee is typed before it is walked, so a closure literal has no type yet and falls through to `convert-arguments`. After the walk the form is a call of the constructor:
```c
return ƛ0_f_make()(5);
```
`cc`: `called object is not a function or function pointer`. `((lambda ((x int)) int (return x)) 5)` works, because the lifted function is registered before the call is typed. `((make-adder 20) 22)` works, because the callee is already a call with a closure return type.
|
||||
(and (symbol? (car expr)) (get-return-type (car expr))))
|
||||
(else
|
||||
(case (car expr)
|
||||
;; subscripting an array gives its element type, and a pointer
|
||||
|
pkulev
commented
When the ranks tie this keeps the left operand, so the declared type depends on the order of the source. With
When the ranks tie this keeps the left operand, so the declared type depends on the order of the source.
With `n` a `size-t` (an unparsed name, rank 1, same as `int`):
```c
int m1 = 1 + n;
size_t m2 = n + 1;
```
`cc` accepts both. C types `1 + n` as `size_t`, and the `int` declaration narrows. The same tie makes `(+ i u)` an `int` and `(+ u i)` an `unsigned int`. `char + char` is declared `char`.
`size-t` never reaches the `long` rank because it is not a typedef Sex has parsed, so the `strlen` example's `(+ 1 n)` lands here.
pkulev
commented
The forms that reach this walk are
The forms that reach this walk are `&&`, `||`, `|`, `&`, `<<`, `>>`, `++`. `c-and` and `c-or` are the names from before those spellings. The writer already emits `a && b` from `(&& a b)`.
`(var ok _ (&& a b))` and `(var x _ (<< a b))` and `(var x _ (++ a))` all stop with `cannot infer the type of`.
pkulev
commented
An array operand is returned as the array. emits An array operand is returned as the array. `decay` in `infer.scm` is the conversion this comment describes, and nothing in the walk calls it.
```sex
(var a (¤ int 4) #(1 2 3 4))
(var p _ (+ a 1))
```
emits `int p[4] = a + 1`. `cc`: `invalid initializer`.
pkulev
commented
A subscript types only when the base's surface form is already stops with A subscript types only when the base's surface form is already `(¤ ...)`. An `(* int)` is not, so
```sex
(fn f ((p (* int))) int
(var e _ (¤ p 0))
(return e))
```
stops with `cannot infer the type of`. This is the other use named on `decay`.
pkulev
commented
`resolve-closure-types` runs here and `resolve-wildcard` does not. `(var n _ 1)` at toplevel emits `static _ n = 1`. `cc`: `unknown type name '_'`. The same form inside a function becomes `int n = 1`.
pkulev
commented
`walk-parts` walks the type as well as the initializer. A list whose head is an in-scope closure is rewritten as a closure call, including a parameter name inside a `fn` type.
```sex
(var c (closure ((int)) int) (make-adder 1))
(var fp (fn ((c int)) int) (lambda ((c int)) int (return c)))
```
`-m` shows `(var fp (fn ((ƛint_int_call c int)) int) ...)`, and the C is `int (*fp)(c int)`. With no closure named `c`, the same declaration emits `int (*fp)(int)`.
pkulev
commented
The callee is typed before it is walked, so a closure literal has no type yet and falls through to
The callee is typed before it is walked, so a closure literal has no type yet and falls through to `convert-arguments`. After the walk the form is a call of the constructor:
```c
return ƛ0_f_make()(5);
```
`cc`: `called object is not a function or function pointer`. `((lambda ((x int)) int (return x)) 5)` works, because the lifted function is registered before the call is typed. `((make-adder 20) 22)` works, because the callee is already a call with a closure return type.
|
||||
;; subscripts the same way
|
||||
|
pkulev
commented
When the ranks tie this keeps the left operand, so the declared type depends on the order of the source. With
When the ranks tie this keeps the left operand, so the declared type depends on the order of the source.
With `n` a `size-t` (an unparsed name, rank 1, same as `int`):
```c
int m1 = 1 + n;
size_t m2 = n + 1;
```
`cc` accepts both. C types `1 + n` as `size_t`, and the `int` declaration narrows. The same tie makes `(+ i u)` an `int` and `(+ u i)` an `unsigned int`. `char + char` is declared `char`.
`size-t` never reaches the `long` rank because it is not a typedef Sex has parsed, so the `strlen` example's `(+ 1 n)` lands here.
pkulev
commented
The forms that reach this walk are
The forms that reach this walk are `&&`, `||`, `|`, `&`, `<<`, `>>`, `++`. `c-and` and `c-or` are the names from before those spellings. The writer already emits `a && b` from `(&& a b)`.
`(var ok _ (&& a b))` and `(var x _ (<< a b))` and `(var x _ (++ a))` all stop with `cannot infer the type of`.
pkulev
commented
An array operand is returned as the array. emits An array operand is returned as the array. `decay` in `infer.scm` is the conversion this comment describes, and nothing in the walk calls it.
```sex
(var a (¤ int 4) #(1 2 3 4))
(var p _ (+ a 1))
```
emits `int p[4] = a + 1`. `cc`: `invalid initializer`.
pkulev
commented
A subscript types only when the base's surface form is already stops with A subscript types only when the base's surface form is already `(¤ ...)`. An `(* int)` is not, so
```sex
(fn f ((p (* int))) int
(var e _ (¤ p 0))
(return e))
```
stops with `cannot infer the type of`. This is the other use named on `decay`.
pkulev
commented
`resolve-closure-types` runs here and `resolve-wildcard` does not. `(var n _ 1)` at toplevel emits `static _ n = 1`. `cc`: `unknown type name '_'`. The same form inside a function becomes `int n = 1`.
pkulev
commented
`walk-parts` walks the type as well as the initializer. A list whose head is an in-scope closure is rewritten as a closure call, including a parameter name inside a `fn` type.
```sex
(var c (closure ((int)) int) (make-adder 1))
(var fp (fn ((c int)) int) (lambda ((c int)) int (return c)))
```
`-m` shows `(var fp (fn ((ƛint_int_call c int)) int) ...)`, and the C is `int (*fp)(c int)`. With no closure named `c`, the same declaration emits `int (*fp)(int)`.
pkulev
commented
The callee is typed before it is walked, so a closure literal has no type yet and falls through to
The callee is typed before it is walked, so a closure literal has no type yet and falls through to `convert-arguments`. After the walk the form is a call of the constructor:
```c
return ƛ0_f_make()(5);
```
`cc`: `called object is not a function or function pointer`. `((lambda ((x int)) int (return x)) 5)` works, because the lifted function is registered before the call is typed. `((make-adder 20) 22)` works, because the callee is already a call with a closure return type.
|
||||
((¤) (let ((base (expression-type (second expr) env)))
|
||||
(and (list? base) (>= (length base) 2) (eq? '¤ (car base))
|
||||
|
pkulev
commented
When the ranks tie this keeps the left operand, so the declared type depends on the order of the source. With
When the ranks tie this keeps the left operand, so the declared type depends on the order of the source.
With `n` a `size-t` (an unparsed name, rank 1, same as `int`):
```c
int m1 = 1 + n;
size_t m2 = n + 1;
```
`cc` accepts both. C types `1 + n` as `size_t`, and the `int` declaration narrows. The same tie makes `(+ i u)` an `int` and `(+ u i)` an `unsigned int`. `char + char` is declared `char`.
`size-t` never reaches the `long` rank because it is not a typedef Sex has parsed, so the `strlen` example's `(+ 1 n)` lands here.
pkulev
commented
The forms that reach this walk are
The forms that reach this walk are `&&`, `||`, `|`, `&`, `<<`, `>>`, `++`. `c-and` and `c-or` are the names from before those spellings. The writer already emits `a && b` from `(&& a b)`.
`(var ok _ (&& a b))` and `(var x _ (<< a b))` and `(var x _ (++ a))` all stop with `cannot infer the type of`.
pkulev
commented
An array operand is returned as the array. emits An array operand is returned as the array. `decay` in `infer.scm` is the conversion this comment describes, and nothing in the walk calls it.
```sex
(var a (¤ int 4) #(1 2 3 4))
(var p _ (+ a 1))
```
emits `int p[4] = a + 1`. `cc`: `invalid initializer`.
pkulev
commented
A subscript types only when the base's surface form is already stops with A subscript types only when the base's surface form is already `(¤ ...)`. An `(* int)` is not, so
```sex
(fn f ((p (* int))) int
(var e _ (¤ p 0))
(return e))
```
stops with `cannot infer the type of`. This is the other use named on `decay`.
pkulev
commented
`resolve-closure-types` runs here and `resolve-wildcard` does not. `(var n _ 1)` at toplevel emits `static _ n = 1`. `cc`: `unknown type name '_'`. The same form inside a function becomes `int n = 1`.
pkulev
commented
`walk-parts` walks the type as well as the initializer. A list whose head is an in-scope closure is rewritten as a closure call, including a parameter name inside a `fn` type.
```sex
(var c (closure ((int)) int) (make-adder 1))
(var fp (fn ((c int)) int) (lambda ((c int)) int (return c)))
```
`-m` shows `(var fp (fn ((ƛint_int_call c int)) int) ...)`, and the C is `int (*fp)(c int)`. With no closure named `c`, the same declaration emits `int (*fp)(int)`.
pkulev
commented
The callee is typed before it is walked, so a closure literal has no type yet and falls through to
The callee is typed before it is walked, so a closure literal has no type yet and falls through to `convert-arguments`. After the walk the form is a call of the constructor:
```c
return ƛ0_f_make()(5);
```
`cc`: `called object is not a function or function pointer`. `((lambda ((x int)) int (return x)) 5)` works, because the lifted function is registered before the call is typed. `((make-adder 20) 22)` works, because the callee is already a call with a closure return type.
|
||||
(second base))))
|
||||
|
pkulev
commented
When the ranks tie this keeps the left operand, so the declared type depends on the order of the source. With
When the ranks tie this keeps the left operand, so the declared type depends on the order of the source.
With `n` a `size-t` (an unparsed name, rank 1, same as `int`):
```c
int m1 = 1 + n;
size_t m2 = n + 1;
```
`cc` accepts both. C types `1 + n` as `size_t`, and the `int` declaration narrows. The same tie makes `(+ i u)` an `int` and `(+ u i)` an `unsigned int`. `char + char` is declared `char`.
`size-t` never reaches the `long` rank because it is not a typedef Sex has parsed, so the `strlen` example's `(+ 1 n)` lands here.
pkulev
commented
The forms that reach this walk are
The forms that reach this walk are `&&`, `||`, `|`, `&`, `<<`, `>>`, `++`. `c-and` and `c-or` are the names from before those spellings. The writer already emits `a && b` from `(&& a b)`.
`(var ok _ (&& a b))` and `(var x _ (<< a b))` and `(var x _ (++ a))` all stop with `cannot infer the type of`.
pkulev
commented
An array operand is returned as the array. emits An array operand is returned as the array. `decay` in `infer.scm` is the conversion this comment describes, and nothing in the walk calls it.
```sex
(var a (¤ int 4) #(1 2 3 4))
(var p _ (+ a 1))
```
emits `int p[4] = a + 1`. `cc`: `invalid initializer`.
pkulev
commented
A subscript types only when the base's surface form is already stops with A subscript types only when the base's surface form is already `(¤ ...)`. An `(* int)` is not, so
```sex
(fn f ((p (* int))) int
(var e _ (¤ p 0))
(return e))
```
stops with `cannot infer the type of`. This is the other use named on `decay`.
pkulev
commented
`resolve-closure-types` runs here and `resolve-wildcard` does not. `(var n _ 1)` at toplevel emits `static _ n = 1`. `cc`: `unknown type name '_'`. The same form inside a function becomes `int n = 1`.
pkulev
commented
`walk-parts` walks the type as well as the initializer. A list whose head is an in-scope closure is rewritten as a closure call, including a parameter name inside a `fn` type.
```sex
(var c (closure ((int)) int) (make-adder 1))
(var fp (fn ((c int)) int) (lambda ((c int)) int (return c)))
```
`-m` shows `(var fp (fn ((ƛint_int_call c int)) int) ...)`, and the C is `int (*fp)(c int)`. With no closure named `c`, the same declaration emits `int (*fp)(int)`.
pkulev
commented
The callee is typed before it is walked, so a closure literal has no type yet and falls through to
The callee is typed before it is walked, so a closure literal has no type yet and falls through to `convert-arguments`. After the walk the form is a call of the constructor:
```c
return ƛ0_f_make()(5);
```
`cc`: `called object is not a function or function pointer`. `((lambda ((x int)) int (return x)) 5)` works, because the lifted function is registered before the call is typed. `((make-adder 20) 22)` works, because the callee is already a call with a closure return type.
|
||||
(or (array-element-type base) (pointer-target base))))
|
||||
|
pkulev
commented
When the ranks tie this keeps the left operand, so the declared type depends on the order of the source. With
When the ranks tie this keeps the left operand, so the declared type depends on the order of the source.
With `n` a `size-t` (an unparsed name, rank 1, same as `int`):
```c
int m1 = 1 + n;
size_t m2 = n + 1;
```
`cc` accepts both. C types `1 + n` as `size_t`, and the `int` declaration narrows. The same tie makes `(+ i u)` an `int` and `(+ u i)` an `unsigned int`. `char + char` is declared `char`.
`size-t` never reaches the `long` rank because it is not a typedef Sex has parsed, so the `strlen` example's `(+ 1 n)` lands here.
pkulev
commented
The forms that reach this walk are
The forms that reach this walk are `&&`, `||`, `|`, `&`, `<<`, `>>`, `++`. `c-and` and `c-or` are the names from before those spellings. The writer already emits `a && b` from `(&& a b)`.
`(var ok _ (&& a b))` and `(var x _ (<< a b))` and `(var x _ (++ a))` all stop with `cannot infer the type of`.
pkulev
commented
An array operand is returned as the array. emits An array operand is returned as the array. `decay` in `infer.scm` is the conversion this comment describes, and nothing in the walk calls it.
```sex
(var a (¤ int 4) #(1 2 3 4))
(var p _ (+ a 1))
```
emits `int p[4] = a + 1`. `cc`: `invalid initializer`.
pkulev
commented
A subscript types only when the base's surface form is already stops with A subscript types only when the base's surface form is already `(¤ ...)`. An `(* int)` is not, so
```sex
(fn f ((p (* int))) int
(var e _ (¤ p 0))
(return e))
```
stops with `cannot infer the type of`. This is the other use named on `decay`.
pkulev
commented
`resolve-closure-types` runs here and `resolve-wildcard` does not. `(var n _ 1)` at toplevel emits `static _ n = 1`. `cc`: `unknown type name '_'`. The same form inside a function becomes `int n = 1`.
pkulev
commented
`walk-parts` walks the type as well as the initializer. A list whose head is an in-scope closure is rewritten as a closure call, including a parameter name inside a `fn` type.
```sex
(var c (closure ((int)) int) (make-adder 1))
(var fp (fn ((c int)) int) (lambda ((c int)) int (return c)))
```
`-m` shows `(var fp (fn ((ƛint_int_call c int)) int) ...)`, and the C is `int (*fp)(c int)`. With no closure named `c`, the same declaration emits `int (*fp)(int)`.
pkulev
commented
The callee is typed before it is walked, so a closure literal has no type yet and falls through to
The callee is typed before it is walked, so a closure literal has no type yet and falls through to `convert-arguments`. After the walk the form is a call of the constructor:
```c
return ƛ0_f_make()(5);
```
`cc`: `called object is not a function or function pointer`. `((lambda ((x int)) int (return x)) 5)` works, because the lifted function is registered before the call is typed. `((make-adder 20) 22)` works, because the callee is already a call with a closure return type.
|
||||
((&) (and (= 2 (length expr))
|
||||
(let ((target (expression-type (second expr) env)))
|
||||
(and target `(* ,target)))))
|
||||
|
||||
|
pkulev
commented
When the ranks tie this keeps the left operand, so the declared type depends on the order of the source. With
When the ranks tie this keeps the left operand, so the declared type depends on the order of the source.
With `n` a `size-t` (an unparsed name, rank 1, same as `int`):
```c
int m1 = 1 + n;
size_t m2 = n + 1;
```
`cc` accepts both. C types `1 + n` as `size_t`, and the `int` declaration narrows. The same tie makes `(+ i u)` an `int` and `(+ u i)` an `unsigned int`. `char + char` is declared `char`.
`size-t` never reaches the `long` rank because it is not a typedef Sex has parsed, so the `strlen` example's `(+ 1 n)` lands here.
pkulev
commented
The forms that reach this walk are
The forms that reach this walk are `&&`, `||`, `|`, `&`, `<<`, `>>`, `++`. `c-and` and `c-or` are the names from before those spellings. The writer already emits `a && b` from `(&& a b)`.
`(var ok _ (&& a b))` and `(var x _ (<< a b))` and `(var x _ (++ a))` all stop with `cannot infer the type of`.
pkulev
commented
An array operand is returned as the array. emits An array operand is returned as the array. `decay` in `infer.scm` is the conversion this comment describes, and nothing in the walk calls it.
```sex
(var a (¤ int 4) #(1 2 3 4))
(var p _ (+ a 1))
```
emits `int p[4] = a + 1`. `cc`: `invalid initializer`.
pkulev
commented
A subscript types only when the base's surface form is already stops with A subscript types only when the base's surface form is already `(¤ ...)`. An `(* int)` is not, so
```sex
(fn f ((p (* int))) int
(var e _ (¤ p 0))
(return e))
```
stops with `cannot infer the type of`. This is the other use named on `decay`.
pkulev
commented
`resolve-closure-types` runs here and `resolve-wildcard` does not. `(var n _ 1)` at toplevel emits `static _ n = 1`. `cc`: `unknown type name '_'`. The same form inside a function becomes `int n = 1`.
pkulev
commented
`walk-parts` walks the type as well as the initializer. A list whose head is an in-scope closure is rewritten as a closure call, including a parameter name inside a `fn` type.
```sex
(var c (closure ((int)) int) (make-adder 1))
(var fp (fn ((c int)) int) (lambda ((c int)) int (return c)))
```
`-m` shows `(var fp (fn ((ƛint_int_call c int)) int) ...)`, and the C is `int (*fp)(c int)`. With no closure named `c`, the same declaration emits `int (*fp)(int)`.
pkulev
commented
The callee is typed before it is walked, so a closure literal has no type yet and falls through to
The callee is typed before it is walked, so a closure literal has no type yet and falls through to `convert-arguments`. After the walk the form is a call of the constructor:
```c
return ƛ0_f_make()(5);
```
`cc`: `called object is not a function or function pointer`. `((lambda ((x int)) int (return x)) 5)` works, because the lifted function is registered before the call is typed. `((make-adder 20) 22)` works, because the callee is already a call with a closure return type.
pkulev
commented
When the ranks tie this keeps the left operand, so the declared type depends on the order of the source. With
When the ranks tie this keeps the left operand, so the declared type depends on the order of the source.
With `n` a `size-t` (an unparsed name, rank 1, same as `int`):
```c
int m1 = 1 + n;
size_t m2 = n + 1;
```
`cc` accepts both. C types `1 + n` as `size_t`, and the `int` declaration narrows. The same tie makes `(+ i u)` an `int` and `(+ u i)` an `unsigned int`. `char + char` is declared `char`.
`size-t` never reaches the `long` rank because it is not a typedef Sex has parsed, so the `strlen` example's `(+ 1 n)` lands here.
pkulev
commented
The forms that reach this walk are
The forms that reach this walk are `&&`, `||`, `|`, `&`, `<<`, `>>`, `++`. `c-and` and `c-or` are the names from before those spellings. The writer already emits `a && b` from `(&& a b)`.
`(var ok _ (&& a b))` and `(var x _ (<< a b))` and `(var x _ (++ a))` all stop with `cannot infer the type of`.
pkulev
commented
An array operand is returned as the array. emits An array operand is returned as the array. `decay` in `infer.scm` is the conversion this comment describes, and nothing in the walk calls it.
```sex
(var a (¤ int 4) #(1 2 3 4))
(var p _ (+ a 1))
```
emits `int p[4] = a + 1`. `cc`: `invalid initializer`.
pkulev
commented
A subscript types only when the base's surface form is already stops with A subscript types only when the base's surface form is already `(¤ ...)`. An `(* int)` is not, so
```sex
(fn f ((p (* int))) int
(var e _ (¤ p 0))
(return e))
```
stops with `cannot infer the type of`. This is the other use named on `decay`.
pkulev
commented
`resolve-closure-types` runs here and `resolve-wildcard` does not. `(var n _ 1)` at toplevel emits `static _ n = 1`. `cc`: `unknown type name '_'`. The same form inside a function becomes `int n = 1`.
pkulev
commented
`walk-parts` walks the type as well as the initializer. A list whose head is an in-scope closure is rewritten as a closure call, including a parameter name inside a `fn` type.
```sex
(var c (closure ((int)) int) (make-adder 1))
(var fp (fn ((c int)) int) (lambda ((c int)) int (return c)))
```
`-m` shows `(var fp (fn ((ƛint_int_call c int)) int) ...)`, and the C is `int (*fp)(c int)`. With no closure named `c`, the same declaration emits `int (*fp)(int)`.
pkulev
commented
The callee is typed before it is walked, so a closure literal has no type yet and falls through to
The callee is typed before it is walked, so a closure literal has no type yet and falls through to `convert-arguments`. After the walk the form is a call of the constructor:
```c
return ƛ0_f_make()(5);
```
`cc`: `called object is not a function or function pointer`. `((lambda ((x int)) int (return x)) 5)` works, because the lifted function is registered before the call is typed. `((make-adder 20) 22)` works, because the callee is already a call with a closure return type.
|
||||
@@ -38,8 +38,8 @@ semen.o: semen.module.scm ../semen.scm infer.o sex-macros.o sex-modules.o types.
|
||||
sex-fmt-c.o: ../sex-fmt-c.scm
|
||||
$(CHICKEN_C) $(CSC_FLAGS) $(MODULE_FLAGS) ../sex-fmt-c.scm -o sex-fmt-c.o -unit sex-fmt-c
|
||||
|
||||
fmt-c-writer.o: fmt-c-writer.module.scm ../fmt-c-writer.scm sex-fmt-c.o utils.o
|
||||
$(CHICKEN_C) $(CSC_FLAGS) $(MODULE_FLAGS) fmt-c-writer.module.scm -o fmt-c-writer.o -unit fmt-c-writer -link sex-fmt-c,utils
|
||||
fmt-c-writer.o: fmt-c-writer.module.scm ../fmt-c-writer.scm sex-fmt-c.o types.o utils.o
|
||||
$(CHICKEN_C) $(CSC_FLAGS) $(MODULE_FLAGS) fmt-c-writer.module.scm -o fmt-c-writer.o -unit fmt-c-writer -link sex-fmt-c,types,utils
|
||||
|
||||
sexc.o: sexc.module.scm infer.o types.o ../sexc.scm fmt-c-writer.o sex-macros.o sex-modules.o reader.o semen.o utils.o
|
||||
$(CHICKEN_C) $(CSC_FLAGS) $(MODULE_FLAGS) sexc.module.scm -o sexc.o -unit sexc -link fmt-c-writer,sex-macros,sex-modules,reader,semen,infer,types,utils
|
||||
|
||||
72
tests/sex-programs/type-shapes.sex
Normal file
@@ -0,0 +1,72 @@
|
||||
(input)
|
||||
(output "aggregate element: 3 4"
|
||||
"pointer element: there"
|
||||
"multi-word element: 9"
|
||||
"through a pointer: 55"
|
||||
"unsized of a typedef: 1 2"
|
||||
"unsized of a pointer: 5"
|
||||
"unnamed parameters: 7 -1 2")
|
||||
(return 0)
|
||||
|
||||
;;; Three questions about a written type that used to be answered in
|
||||
;;; three places and disagreed: is `(a b)' a named parameter or a bare
|
||||
;;; type, is the last element of a `¤' its bound or the last word of
|
||||
;;; its element type, and what is one element of an array.
|
||||
;;;
|
||||
;;; They are one question -- where does the type end -- so the answer
|
||||
;;; lives in `types' and everything else asks it.
|
||||
|
||||
(include stdio.h)
|
||||
|
||||
(struct point ((x int) (y int)))
|
||||
|
||||
(typedef small int)
|
||||
|
||||
;;; a parameter that names nothing is a type, however many words it
|
||||
;;; takes: `(unsigned int)' is one of them, not a `unsigned' called
|
||||
;;; `int'
|
||||
(fn width ((n unsigned int)) int
|
||||
(return (cast n int)))
|
||||
|
||||
(fn sign ((c const char)) int
|
||||
(if (== c #\a) (return -1))
|
||||
(return 1))
|
||||
|
||||
(fn twice ((n small)) int
|
||||
(return (* n 2)))
|
||||
|
||||
(pub fn main () int
|
||||
;; an element keeps every word of its type, tag and all
|
||||
(var pts (¤ (struct point) 2) #(#((struct point) : 1 2)
|
||||
#((struct point) : 3 4)))
|
||||
(var p _ (¤ pts 1))
|
||||
(printf "aggregate element: %d %d\n" (. p x) (. p y))
|
||||
|
||||
(var names (¤ (* const char) 2) #("hi" "there"))
|
||||
(var s _ (¤ names 1))
|
||||
(printf "pointer element: %s\n" s)
|
||||
|
||||
(var nums (¤ unsigned int 3) #(7 8 9))
|
||||
(var u _ (¤ nums 2))
|
||||
(printf "multi-word element: %u\n" u)
|
||||
|
||||
;; subscripting a pointer answers the same as subscripting an array
|
||||
(var q (* (struct point)) (& (¤ pts 0)))
|
||||
(var r _ (¤ q 1))
|
||||
(printf "through a pointer: %d\n" (+ (. r x) (* 13 (. r y))))
|
||||
|
||||
;; the last word of an unsized array's type is not its bound: neither
|
||||
;; a typedef name nor the target of a `*' can be one
|
||||
(var tail (¤ const small) #(1 2))
|
||||
(printf "unsized of a typedef: %d %d\n" (¤ tail 0) (¤ tail 1))
|
||||
|
||||
(var one size-t 5)
|
||||
(var sizes (¤ * size-t) #((& one)))
|
||||
(var w _ (¤ sizes 0))
|
||||
(printf "unsized of a pointer: %d\n" (cast (* w) int))
|
||||
|
||||
;; the same question in type position: `(fn ((unsigned int)) int)'
|
||||
;; takes one parameter, not two
|
||||
(var fp (fn ((unsigned int)) int) width)
|
||||
(printf "unnamed parameters: %d %d %d\n" (fp 7) (sign #\a) (twice 1))
|
||||
(return 0))
|
||||
@@ -18,5 +18,11 @@
|
||||
get-type-info
|
||||
get-tag-info
|
||||
get-fields
|
||||
get-underlying-type)
|
||||
get-underlying-type
|
||||
|
||||
type-head?
|
||||
named-arg?
|
||||
typedef-name?
|
||||
array-bound?
|
||||
array-element-type)
|
||||
"../types.scm")
|
||||
|
||||
@@ -18,5 +18,11 @@
|
||||
get-type-info
|
||||
get-tag-info
|
||||
get-fields
|
||||
get-underlying-type)
|
||||
get-underlying-type
|
||||
|
||||
type-head?
|
||||
named-arg?
|
||||
typedef-name?
|
||||
array-bound?
|
||||
array-element-type)
|
||||
"types.scm")
|
||||
|
||||
74
types.scm
@@ -237,3 +237,77 @@
|
||||
(map (lambda (value) (fn value type))
|
||||
(caddr info))))
|
||||
(else #f)))))
|
||||
|
||||
;;; The shape of a written type
|
||||
;;;
|
||||
;;; Three places have to tell a type from something that merely
|
||||
;;; contains one: an arglist entry is either `(name type)' or a bare
|
||||
;;; type, and an array's last element is either a bound or the last
|
||||
;;; word of its element type. They used to answer it separately, and
|
||||
;;; disagreed.
|
||||
|
||||
;;; A qualifier can never end a type, which is what tells `(¤ const t)'
|
||||
;;; -- an unsized array of `t' -- from `(¤ int 4)'.
|
||||
(define +c-qualifiers+ '(const volatile restrict _Atomic))
|
||||
|
||||
(define +c-specifiers+
|
||||
'(void char short int long float double signed unsigned
|
||||
bool _Bool complex _Complex))
|
||||
|
||||
;;; Does this list start a type rather than name one? `(const char)'
|
||||
;;; and `(unsigned int)' are types; `(f1 float)' is a named parameter.
|
||||
(define (type-head? form)
|
||||
(and (pair? form)
|
||||
(symbol? (car form))
|
||||
(or (memq (car form) '(* ¤ struct union enum))
|
||||
(memq (car form) +c-qualifiers+)
|
||||
(memq (car form) +c-specifiers+))))
|
||||
|
||||
;;; Does the parameter name itself?
|
||||
;;; (f1 float) does
|
||||
;;; (float), (const char), (unsigned int) and (¤ float 4) do not
|
||||
(define (named-arg? arg)
|
||||
(and (pair? arg)
|
||||
(pair? (cdr arg)) ; 1 element args are always type
|
||||
(not (type-head? arg))))
|
||||
|
||||
;;; Is NAME a typedef, as opposed to a `define'd constant? Both live in
|
||||
;;; the same table, and only the first is part of a type.
|
||||
(define (typedef-name? name)
|
||||
(let ((info (and (symbol? name) (get-type-info name))))
|
||||
(and info (memq (car info) '(typedef struct union enum)) #t)))
|
||||
|
||||
;;; `(¤ int N)' is N of int
|
||||
;;; `(¤ unsigned int)' is an unsized array of unsigned int
|
||||
;;;
|
||||
;;; The last element is a bound only if what precedes it is already a
|
||||
;;; complete type, so `(¤ const mytype)' and `(¤ * size-t)' end in the
|
||||
;;; last word of their element type and not in a bound. A type is
|
||||
;;; complete when it ends in a specifier, in a tag following its
|
||||
;;; keyword, or in a typedef we have seen declared.
|
||||
;;;
|
||||
;;; TYPE is the whole `(¤ ...)' form.
|
||||
(define (array-bound? type)
|
||||
(and (> (length type) 2)
|
||||
(let ((bound (last type))
|
||||
(preceding (last (drop-right type 1))))
|
||||
(cond
|
||||
((not (symbol? bound)) #t)
|
||||
((or (memq bound +c-specifiers+) (memq bound +c-qualifiers+)) #f)
|
||||
((memq preceding +c-specifiers+) #t)
|
||||
;; a tag always follows its keyword, so `(¤ * struct tt)' ends
|
||||
;; in a name belonging to the type
|
||||
((memq preceding '(struct union enum)) #f)
|
||||
(else (typedef-name? preceding))))))
|
||||
|
||||
;;; What one element of a written array type is:
|
||||
;;; (¤ struct point 2) -> (struct point), (¤ * const char 2) -> (* const char)
|
||||
(define (array-element-type type)
|
||||
(and (pair? type)
|
||||
(eq? '¤ (car type))
|
||||
(pair? (cdr type))
|
||||
(let ((words (if (array-bound? type)
|
||||
(drop-right (cdr type) 1)
|
||||
(cdr type))))
|
||||
(and (pair? words)
|
||||
(if (null? (cdr words)) (car words) words)))))
|
||||
|
||||
Re 351. One per line, alphabetical.
e87463b.