implement native record

native record primitives are defined in (picrin record-primitive)
- (make-record rectype)
- (record-of? record rectype)
- (record-ref record rectype field-name)
- (record-set! record rectype field-name value)

define-record-type is defined using these primitives
This commit is contained in:
Yuito Murase 2014-07-26 10:47:48 +09:00 committed by Yuito Murase
parent 2f44145d3e
commit c76dfcb861
10 changed files with 240 additions and 123 deletions

31
include/picrin/record.h Normal file
View File

@ -0,0 +1,31 @@
/**
* See Copyright Notice in picrin.h
*/
#ifndef PICRIN_RECORD_H
#define PICRIN_RECORD_H
#if defined(__cplusplus)
extern "C" {
#endif
struct pic_record {
PIC_OBJECT_HEADER
pic_value rectype;
xhash hash;
};
#define pic_record_p(v) (pic_type(v) == PIC_TT_RECORD)
#define pic_record_ptr(v) ((struct pic_record *)pic_ptr(v))
struct pic_record *pic_record_new(pic_state *, pic_value);
bool pic_record_of(pic_state *, struct pic_record *, pic_value);
pic_value pic_record_ref(pic_state *, struct pic_record *, pic_value, pic_sym);
void pic_record_set(pic_state *, struct pic_record *, pic_value, pic_sym, pic_value);
#if defined(__cplusplus)
}
#endif
#endif

View File

@ -115,7 +115,8 @@ enum pic_tt {
PIC_TT_VAR, PIC_TT_VAR,
PIC_TT_IREP, PIC_TT_IREP,
PIC_TT_DATA, PIC_TT_DATA,
PIC_TT_DICT PIC_TT_DICT,
PIC_TT_RECORD
}; };
#define PIC_OBJECT_HEADER \ #define PIC_OBJECT_HEADER \
@ -268,6 +269,8 @@ pic_type_repr(enum pic_tt tt)
return "data"; return "data";
case PIC_TT_DICT: case PIC_TT_DICT:
return "dict"; return "dict";
case PIC_TT_RECORD:
return "record";
} }
UNREACHABLE(); UNREACHABLE();
} }

View File

@ -406,141 +406,69 @@
;;; Record Type ;;; Record Type
(define-library (picrin record) (define-library (picrin record)
(import (scheme base) (import (scheme base)
(picrin macro)) (picrin macro)
(picrin record-primitive))
(define record-marker (list 'record-marker)) (define (caddr x) (car (cddr x)))
(define (cdddr x) (cdr (cddr x)))
(define (cadddr x) (car (cdddr x)))
(define (cddddr x) (cdr (cdddr x)))
(define real-vector? vector?) (define (make-record-type name)
(let ((rectype (make-record #t)))
(record-set! rectype #t 'name name)
rectype))
(set! vector? (define-syntax define-record-constructor
(lambda (x) (ir-macro-transformer
(and (real-vector? x) (lambda (form inject compare?)
(or (= 0 (vector-length x)) (let ((rectype (cadr form))
(not (eq? (vector-ref x 0) (name (caddr form))
record-marker)))))) (fields (cdddr form)))
`(define (,name ,@fields)
(let ((record (make-record ,rectype)))
,@(map (lambda (field)
`(record-set! record ,rectype ',field ,field))
fields)
record))))))
#| (define-syntax define-record-predicate
;; (scheme eval) is not provided for now (ir-macro-transformer
(define eval (lambda (form inject compare?)
(let ((real-eval eval)) (let ((rectype (cadr form))
(lambda (exp env) (name (caddr form)))
((real-eval `(lambda (vector?) ,exp)) `(define (,name obj)
vector?)))) (record-of? obj ,rectype))))))
|#
(define (record? x)
(and (real-vector? x)
(< 0 (vector-length x))
(eq? (vector-ref x 0) record-marker)))
(define (make-record size)
(let ((new (make-vector (+ size 1))))
(vector-set! new 0 record-marker)
new))
(define (record-ref record index)
(vector-ref record (+ index 1)))
(define (record-set! record index value)
(vector-set! record (+ index 1) value))
(define record-type% (make-record 3))
(record-set! record-type% 0 record-type%)
(record-set! record-type% 1 'record-type%)
(record-set! record-type% 2 '(name field-tags))
(define (make-record-type name field-tags)
(let ((new (make-record 3)))
(record-set! new 0 record-type%)
(record-set! new 1 name)
(record-set! new 2 field-tags)
new))
(define (record-type record)
(record-ref record 0))
(define (record-type-name record-type)
(record-ref record-type 1))
(define (record-type-field-tags record-type)
(record-ref record-type 2))
(define (field-index type tag)
(let rec ((i 1) (tags (record-type-field-tags type)))
(cond ((null? tags)
(error "record type has no such field" type tag))
((eq? tag (car tags)) i)
(else (rec (+ i 1) (cdr tags))))))
(define (record-constructor type tags)
(let ((size (length (record-type-field-tags type)))
(arg-count (length tags))
(indexes (map (lambda (tag) (field-index type tag)) tags)))
(lambda args
(if (= (length args) arg-count)
(let ((new (make-record (+ size 1))))
(record-set! new 0 type)
(for-each (lambda (arg i) (record-set! new i arg)) args indexes)
new)
(error "wrong number of arguments to constructor" type args)))))
(define (record-predicate type)
(lambda (thing)
(and (record? thing)
(eq? (record-type thing)
type))))
(define (record-accessor type tag)
(let ((index (field-index type tag)))
(lambda (thing)
(if (and (record? thing)
(eq? (record-type thing)
type))
(record-ref thing index)
(error "accessor applied to bad value" type tag thing)))))
(define (record-modifier type tag)
(let ((index (field-index type tag)))
(lambda (thing value)
(if (and (record? thing)
(eq? (record-type thing)
type))
(record-set! thing index value)
(error "modifier applied to bad value" type tag thing)))))
(define-syntax define-record-field (define-syntax define-record-field
(ir-macro-transformer (ir-macro-transformer
(lambda (form inject compare?) (lambda (form inject compare?)
(let ((type (car (cdr form))) (let ((rectype (cadr form))
(field-tag (car (cdr (cdr form)))) (field-name (caddr form))
(acc-mod (cdr (cdr (cdr form))))) (accessor (cadddr form))
(if (= 1 (length acc-mod)) (modifier? (cddddr form)))
`(define ,(car acc-mod) (if (null? modifier?)
(record-accessor ,type ',field-tag)) `(define (,accessor record)
(record-roef record ,rectype ',field-name))
`(begin `(begin
(define ,(car acc-mod) (define (,accessor record)
(record-accessor ,type ',field-tag)) (record-ref record ,rectype ',field-name))
(define ,(cadr acc-mod) (define (,(car modifier?) record val)
(record-modifier ,type ',field-tag)))))))) (record-set! record ,rectype ',field-name val))))))))
(define-syntax define-record-type (define-syntax define-record-type
(ir-macro-transformer (ir-macro-transformer
(lambda (form inject compare?) (lambda (form inject compare?)
(let ((type (cadr form)) (let ((name (cadr form))
(constructor (car (cdr (cdr form)))) (constructor (caddr form))
(predicate (car (cdr (cdr (cdr form))))) (pred (cadddr form))
(field-tag (cdr (cdr (cdr (cdr form)))))) (fields (cddddr form)))
`(begin `(begin
(define ,type (define ,name (make-record-type ',name))
(make-record-type ',type ',(cdr constructor))) (define-record-constructor ,name ,@constructor)
(define ,(car constructor) (define-record-predicate ,name ,pred)
(record-constructor ,type ',(cdr constructor))) ,@(map (lambda (field) `(define-record-field ,name ,@field))
(define ,predicate fields))))))
(record-predicate ,type))
,@(map
(lambda (x)
`(define-record-field ,type ,(car x) ,(cadr x) ,@(cddr x)))
field-tag))))))
(export define-record-type)) (export define-record-type))

View File

@ -831,6 +831,7 @@ analyze_node(analyze_state *state, pic_value obj, bool tailpos)
case PIC_TT_IREP: case PIC_TT_IREP:
case PIC_TT_DATA: case PIC_TT_DATA:
case PIC_TT_DICT: case PIC_TT_DICT:
case PIC_TT_RECORD:
pic_errorf(pic, "invalid expression given: ~s", obj); pic_errorf(pic, "invalid expression given: ~s", obj);
} }
UNREACHABLE(); UNREACHABLE();

View File

@ -20,6 +20,7 @@
#include "picrin/var.h" #include "picrin/var.h"
#include "picrin/data.h" #include "picrin/data.h"
#include "picrin/dict.h" #include "picrin/dict.h"
#include "picrin/record.h"
#if GC_DEBUG #if GC_DEBUG
# include <string.h> # include <string.h>
@ -506,6 +507,17 @@ gc_mark_object(pic_state *pic, struct pic_object *obj)
} }
break; break;
} }
case PIC_TT_RECORD: {
struct pic_record *rec = (struct pic_record *)obj;
xh_iter it;
gc_mark(pic, rec->rectype);
xh_begin(&it, &rec->hash);
while (xh_next(&it)) {
gc_mark(pic, xh_val(it.e, pic_value));
}
break;
}
case PIC_TT_NIL: case PIC_TT_NIL:
case PIC_TT_BOOL: case PIC_TT_BOOL:
case PIC_TT_FLOAT: case PIC_TT_FLOAT:
@ -658,6 +670,11 @@ gc_finalize_object(pic_state *pic, struct pic_object *obj)
xh_destroy(&dict->hash); xh_destroy(&dict->hash);
break; break;
} }
case PIC_TT_RECORD: {
struct pic_record *rec = (struct pic_record *)obj;
xh_destroy(&rec->hash);
break;
}
case PIC_TT_NIL: case PIC_TT_NIL:
case PIC_TT_BOOL: case PIC_TT_BOOL:
case PIC_TT_FLOAT: case PIC_TT_FLOAT:

View File

@ -31,6 +31,7 @@ void pic_init_load(pic_state *);
void pic_init_write(pic_state *); void pic_init_write(pic_state *);
void pic_init_read(pic_state *); void pic_init_read(pic_state *);
void pic_init_dict(pic_state *); void pic_init_dict(pic_state *);
void pic_init_record(pic_state *);
void pic_init_contrib(pic_state *); void pic_init_contrib(pic_state *);
void pic_load_piclib(pic_state *); void pic_load_piclib(pic_state *);
@ -92,6 +93,7 @@ pic_init_core(pic_state *pic)
pic_init_write(pic); DONE; pic_init_write(pic); DONE;
pic_init_read(pic); DONE; pic_init_read(pic); DONE;
pic_init_dict(pic); DONE; pic_init_dict(pic); DONE;
pic_init_record(pic); DONE;
pic_load_piclib(pic); DONE; pic_load_piclib(pic); DONE;

View File

@ -416,6 +416,7 @@ macroexpand_node(pic_state *pic, pic_value expr, struct pic_senv *senv)
case PIC_TT_IREP: case PIC_TT_IREP:
case PIC_TT_DATA: case PIC_TT_DATA:
case PIC_TT_DICT: case PIC_TT_DICT:
case PIC_TT_RECORD:
pic_errorf(pic, "unexpected value type: ~s", expr); pic_errorf(pic, "unexpected value type: ~s", expr);
} }
UNREACHABLE(); UNREACHABLE();

114
src/record.c Normal file
View File

@ -0,0 +1,114 @@
/**
* See Copyright Notice in picrin.h
*/
#include "picrin.h"
#include "picrin/record.h"
struct pic_record *
pic_record_new(pic_state *pic, pic_value rectype)
{
struct pic_record *rec;
rec = (struct pic_record *)pic_obj_alloc(pic, sizeof(struct pic_record), PIC_TT_RECORD);
rec->rectype = rectype;
xh_init_int(&rec->hash, sizeof(pic_value));
return rec;
}
bool
pic_record_of(pic_state *pic, struct pic_record *rec, pic_value rectype) {
UNUSED(pic);
return pic_eq_p(rec->rectype, rectype);
}
pic_value
pic_record_ref(pic_state *pic, struct pic_record *rec, pic_value rectype, pic_sym slotname)
{
xh_entry *e;
if (! pic_eq_p(rec->rectype, rectype)) {
pic_errorf(pic, "value is not record of ~s", rectype);
}
e = xh_get_int(&rec->hash, slotname);
if (! e) {
pic_errorf(pic, "slot named ~s is not found for record: ~s", pic_sym_value(slotname), rectype);
}
return xh_val(e, pic_value);
}
void
pic_record_set(pic_state *pic, struct pic_record *rec, pic_value rectype, pic_sym slotname, pic_value val)
{
if (! pic_eq_p(rec->rectype, rectype)) {
pic_errorf(pic, "value is not record of ~s", rectype);
}
xh_put_int(&rec->hash, slotname, &val);
}
static pic_value
pic_record_record(pic_state *pic)
{
struct pic_record * rec;
pic_value rectype;
pic_get_args(pic, "o", &rectype);
rec = pic_record_new(pic, rectype);
return pic_obj_value(rec);
}
static pic_value
pic_record_record_of(pic_state *pic)
{
struct pic_record *rec;
pic_value rectype;
pic_get_args(pic, "ro", &rec, &rectype);
return pic_bool_value(pic_record_of(pic, rec, rectype));
}
static pic_value
pic_record_record_ref(pic_state *pic)
{
struct pic_record *rec;
pic_value rectype;
pic_sym slotname;
pic_get_args(pic, "rom", &rec, &rectype, &slotname);
return pic_record_ref(pic, rec, rectype, slotname);
}
static pic_value
pic_record_record_set(pic_state *pic)
{
struct pic_record *rec;
pic_value rectype;
pic_sym slotname;
pic_value val;
pic_get_args(pic, "romo", &rec, &rectype, &slotname, &val);
pic_record_set(pic, rec, rectype, slotname, val);
return pic_none_value();
}
void
pic_init_record(pic_state *pic)
{
pic_deflibrary ("(picrin record-primitive)") {
pic_defun(pic, "make-record", pic_record_record);
pic_defun(pic, "record-of?", pic_record_record_of);
pic_defun(pic, "record-ref", pic_record_record_ref);
pic_defun(pic, "record-set!", pic_record_record_set);
}
}

View File

@ -20,6 +20,7 @@
#include "picrin/macro.h" #include "picrin/macro.h"
#include "picrin/error.h" #include "picrin/error.h"
#include "picrin/dict.h" #include "picrin/dict.h"
#include "picrin/record.h"
#define GET_OPERAND(pic,n) ((pic)->ci->fp[(n)]) #define GET_OPERAND(pic,n) ((pic)->ci->fp[(n)])
@ -346,6 +347,23 @@ pic_get_args(pic_state *pic, const char *format, ...)
} }
break; break;
} }
case 'r': {
struct pic_record **r;
pic_value v;
r = va_arg(ap, struct pic_record **);
if (i < argc) {
v = GET_OPERAND(pic,i);
if (pic_record_p(v)) {
*r = pic_record_ptr(v);
}
else {
pic_errorf(pic, "pic_get_args: expected record, but got ~s", v);
}
i++;
}
break;
}
default: default:
pic_error(pic, "pic_get_args: invalid argument specifier given"); pic_error(pic, "pic_get_args: invalid argument specifier given");
} }

View File

@ -333,6 +333,8 @@ write_core(struct writer_control *p, pic_value obj)
case PIC_TT_DICT: case PIC_TT_DICT:
xfprintf(file, "#<dict %p>", pic_ptr(obj)); xfprintf(file, "#<dict %p>", pic_ptr(obj));
break; break;
case PIC_TT_RECORD:
xfprintf(file, "#<record %p>", pic_ptr(obj));
} }
} }