Browse Source

- new util to check the understanding of c3 mro
- update structs
- small documentation on how inheritance works under the hood

tcheukueppo 2 years ago
parent
commit
7b353a80be
6 changed files with 263 additions and 49 deletions
  1. 71 0
      docs/inheritance.md
  2. 1 1
      docs/maat.md
  3. 4 5
      docs/types/Array.md
  4. 4 6
      src/ma_conf.h
  5. 80 37
      src/ma_obj.h
  6. 103 0
      utils/c3-mro.pl

+ 71 - 0
docs/inheritance.md

@@ -0,0 +1,71 @@
+# Classes
+
+## Overview
+
+## Class Artichecture
+
+## Inheritances
+
+Maat uses C3 mro as resolution order for resolving method calls, method
+resolution is done during compilation which makes Maat pretty fast at
+execution.
+
+```
+
+                (f(D))        (f(Z))
+                 D ──────────► Z
+                 ▲
+                 │
+                 │
+                 │
+
+                (f(B))
+        ┌──────►  B ────────► E (f(E))
+        │                    ▲
+ (f(A)) A                    │
+        │                    │
+        │       (f(C))       │
+        └──────► C ──────────┘
+                 │
+                 │
+               │       (f(M))     (f(K))
+                 └──────► M ───────► K
+```
+
+The above diagram shows the inheritance relationships between the classes
+`A`, `B`, `C`, `D`, `E`, `M`, `K`, and `Z`. For example we have class `A`
+which directly inherits from class `B` and `C` and indirectly inherits from
+class `K`.
+
+### Problem
+
+We wish to resolve a method `f` when calling it from an object which is an
+instance of `A`.
+
+### Solution
+
+Let `X` be a class belonging to the set of classes
+`{A, B, C, D, E, M, K, Z}`, `X` **may** have its own implementation of `f`
+denoted `f(X)`. By applying the C3 method resolution algorithm at
+compilation, we use the divide and conquer approach to perform a breadth-first
+traversal on the inheritance tree of `X`, this yields an array which contains
+contains in C3 order pointers to all the implementations of `f` we found.
+This array is recorded as the value stored at the entry of `f` in the method
+hash table of X, the first element of this arrays is the top level method that
+is going to be called during an `f` call by an object instance of `X`. The
+rest of the elements are used to handle `__SUPER__` calls, especially when
+they are chained across multiple `f` calls.
+
+Let function R(X:f) denotes the `f` method resolution of class `X` that
+returns the array we were talking of.
+
+We have the following:
+
+```
+```
+
+For an object instance of A, the following is true
+
+```
+...
+```

+ 1 - 1
docs/maat.md

@@ -410,7 +410,6 @@ var array = [1, 2, 3, 4, 5]
 Maat has ... builtin objects, types are objects and objects are types, check
 details on each types here.
 
-- [`Any`](./types/Any.md)
 - [`Bool`](./types/Bool.md)
 - [`Num`](./types/Num.md)
 - [`Str`](./types/Str.md)
@@ -426,6 +425,7 @@ details on each types here.
 - [`GFun`](./types/GFun.md)
 - [`Regex`](./types/Regex.md)
 - [`Ma`](./types/Ma.md)
+- [`Co`](./types/Co.md)
 - [`Work`](./types/Work.md)
 - [`Chan`](./types/Chan.md)
 - [`Socket`](./types/Socket.md)

+ 4 - 5
docs/types/Array.md

@@ -6,13 +6,12 @@
 
 ```
 -- Yep, we have multiple ways of declaring an Array object
-var x = [qw(one two three four)]
-var a = qa<liza madjou monthe>
+var x = [ w(one two three four) ]
 var b = ["kueppo", "cyrus", "christ"]
-var c = Array.new(2, 3, "four", qa<5 6>)
+var c = Array.new(2, 3, "four", [w<5 6>])
 
-.say for a.lmap(.split) -- qa{l i z a m a d j o u m o n t h e}
-.say for a.map(.split)  -- qa{l i z a}, qa{m a d j o u}, qa{m o n t h e}
+.say for a.lmap(.split) -- [ w{l i z a m a d j o u m o n t h e} ]
+.say for a.map(.split)  -- [ [ w{l i z a} ], [ w{m a d j o u} ], [ w{m o n t h e} ] ]
 ```
 
 ## Methods

+ 4 - 6
src/ma_conf.h

@@ -65,13 +65,12 @@
  * Maat can co-exist within 'X:\\Program Files\Maat\'
  */
 #if !defined(MA_CLIB_DEFAULT_PATH)
-#define MA_CLIB_DEFAULT_PATH  "!\\" MA_VERSION "\\lib\\",
-#endif
 
-#if !defined(MA_MTLIB_DEFAULT_PATH)
+#define MA_CLIB_DEFAULT_PATH  "!\\" MA_VERSION "\\lib\\",
 #define MA_MTLIB_DEFAULT_PATH \
    "!\\" MA_VERSION "\\share\\", \
    "!\\" MA_VERSION "\\maat\\",
+
 #endif
 
 #define MA_DIRSEP  "\\"
@@ -81,13 +80,12 @@
 #define MA_LOCAL_ROOT  "/usr/local/"
 
 #if !defined(MA_CLIB_DEFAULT_PATH)
-#define MA_CLIB_DEFAULT_PATH  MA_LOCAL_ROOT "lib/maat/" MA_VERSION "/",
-#endif
 
-#if !defined(MA_MTLIB_DEFAULT_PATH)
+#define MA_CLIB_DEFAULT_PATH  MA_LOCAL_ROOT "lib/maat/" MA_VERSION "/",
 #define MA_MTLIB_DEFAULT_PATH \
    MA_LOCAL_ROOT "share/maat/" MA_VERSION "/", \
    "/usr/share/maat/" MA_VERSION "/",
+
 #endif
 
 #define MA_DIRSEP  "/"

+ 80 - 37
src/ma_obj.h

@@ -14,7 +14,7 @@
  * specific to `#if` body of the MA_NAN_TAGGING check.
  */
 
-/* Vary type 't' with the variant bits 'v', see Value */
+/* Vary type 't' with variant bits 'v', see Value */
 #define vary(t, v)  ((t) | (v << 5))
 
 #define with_variant(v)      ((v)->type & 0x7F)
@@ -25,7 +25,7 @@
 /* Get top level value type of 'v' */
 #define vtype(v)  with_variant(v)
 
-/* value 'o' is an object and get its object type */
+/* Value 'o' is an object and get its object type */
 #define otype(o)  with_variant(as_obj(v))
 
 /* Get profound type of 'v': dig into 'v' if it's an object */
@@ -68,7 +68,7 @@ typedef struct {
       struct Header *obj;
       Num n;
       CFunc f;
-      void *cdata;
+      void *p;
    } val;
 } Value;
 
@@ -77,15 +77,15 @@ typedef struct {
 #define set_valo(v, o)      set_val(v, o, (o)->type)
 
 /*
- * For objects, copy by reference v2 into v1 while
- * for non objects (numbers, etc), copy by value.
+ * For objects, copy by reference 'v2' into 'v1' while for other type
+ * of values like numbers, booleans, and function/void pointers, do a
+ * copy by value.
  */
 #define copy(v1, v2)  set_val(v1, v2->val, v2->type)
 
 /*
- * All the possible standard types of a value, look! V_TYPE_OBJ
- * is also one. A value 'v' is an object if the MSB of its $type
- * is 1.
+ * Defines all the possible standard types of a value, V_TYPE_OBJ is
+ * also a value. A value 'v' is an object if the MSB of its $type is 1.
  */
 #define V_TYPE_NIL    0
 #define V_TYPE_BOOL   1
@@ -102,7 +102,7 @@ typedef struct {
 #define is_cfunc(v)  check_type(v, V_TYPE_CFUNC)
 #define is_cdata(v)  check_type(v, V_TYPE_CDATA)
 
-/* Check if a value is collectable */
+/* Check if value 'v' is collectable */
 #define is_ctb(v)  is_obj(v)
 
 #define as_bool(v)   (is_true(v))
@@ -171,23 +171,25 @@ typedef struct Header {
 #define o_check_type(v, t)  is_obj(v) && check_type(v, t)
 
 /* Defining of all base objects */
-#define O_TYPE_CLASS   5    
+#define O_TYPE_CLASS   5   /* variant: O_TYPEV_CCLASS */
 #define O_TYPE_STR     6    
 #define O_TYPE_RANGE   7    
 #define O_TYPE_ARRAY   8   /* variants: O_TYPEV_SET, O_TYPEV_MSET, O_TYPEV_LIST */
 #define O_TYPE_MAP     9   /* variants: O_TYPEV_BAG, O_TYPEV_MBAG */
-#define O_TYPE_FUN     10  /* variants: O_TYPEV_CLOSURE */
-#define O_TYPE_CO     12  /* variants: O_TYPEV_MA, O_TYPEV_WORK */
-#define O_TYPE_RBQ     13  /* variants: O_TYPEV_CHAN, O_TYPEV_SCHEDQ */
-#define O_TYPE_REGEX   14
-#define O_TYPE_SOCKET  15
-#define O_TYPE_PIPE    16
-#define O_TYPE_FILE    17
-#define O_TYPE_DIR     18
-#define O_TYPE_PROC    19
-#define O_TYPE_SYS     20
-#define O_TYPE_DATE    21
+#define O_TYPE_FUN     10  /* variant:  O_TYPEV_CLOSURE */
+#define O_TYPE_MA      11  /* variants: O_TYPEV_CO, O_TYPEV_WORK */
+#define O_TYPE_RBQ     12  /* variants: O_TYPEV_CHAN, O_TYPEV_SCHEDQ */
+#define O_TYPE_REGEX   13
+#define O_TYPE_SOCKET  14
+#define O_TYPE_PIPE    15
+#define O_TYPE_FILE    16
+#define O_TYPE_DIR     17
+#define O_TYPE_PROC    18
+#define O_TYPE_SYS     19
+#define O_TYPE_DATE    20
+#define O_TYPE_PKG     21
 #define O_TYPE_TERM    22
+
 #define O_DEADKEY      31  /* */
 
 /* A Check macro on 'v' for each object type */
@@ -211,7 +213,16 @@ typedef struct Header {
 /* Test if the value 'v' is an object variant of variant type 't' */
 #define o_check_vartype(v,t)  is_obj(v) && check_vartype(v,t)
 
-/* Here are variants of some base objects */
+/* Here are variants of some standard objects */
+
+/*
+ * Cclass--a special way of extending Maat code with C/C++, here
+ * we store references to C/C++ functions as methods and structs
+ * as attributes.
+ */
+#define O_TYPEV_CCLASS  vary(O_TYPE_CLASS, 1)
+
+#define is_cclass(v)  o_check_vartype(v, O_TYPEV_CCLASS)
 
 /* Immutable and mutable bags */
 #define O_TYPEV_BAG   vary(O_TYPE_MAP, 1)
@@ -234,9 +245,9 @@ typedef struct Header {
 
 #define is_closure(v)  o_check_vartype(v, O_TYPEV_CLOSURE)
 
-#define O_TYPEV_MA    vary(O_TYPE_CMA, 1)
-#define O_TYPEV_GFUN  vary(O_TYPE_CMA, 1)
-#define O_TYPEV_MA    vary(O_TYPE_CMA, 1)
+/* Two other concurrency models: Coroutine, Work */
+#define O_TYPEV_CO    vary(O_TYPE_CMA, 1)
+#define O_TYPEV_WORK  vary(O_TYPE_CMA, 2)
 
 /* A threadsafe Channel and Scheduler queue */
 #define O_TYPEV_CHAN    vary(O_TYPE_RBQ, 1)
@@ -246,50 +257,81 @@ typedef struct Header {
 #define is_schedq(v)  o_check_type(v, O_TYPEV_SCHEDQ)
 
 /*
- * What? utf8 string object
- * $str: utf8 encoded string itself
+ * What? string object
+ * $str: utf-8 encoded string itself
  * $hash: hash value of $str
  * $rlen: real length of $str
  * $len: user-percieved length of $str
  */
+
 typedef struct {
    Header obj;
-   Uint hash;
+   unsigned int hash;
    size_t rlen;
    size_t len;
    Byte str[1];
 } Str;
 
 /*
- * What? Range object x..y
- * $x: the start (inclusive)
- * $y: the end (inclusive)
+ * What? Range object [x..y] (inclusive)
+ * $x: the start
+ * $y: and the end
  */
+
 typedef struct {
    Header obj;
-   Num x, y;
+   Num x;
+   Num y;
 } Range;
 
+/*
+ */
+
 typedef struct {
    Header obj;
 } Map;
 
 /*
- * $super: class' superclasses (C3 mro)
+ * $is: class' superclasses (Maat implements C3 mro)
  * $name: class name
- * $nfields: number of fields
+ * $n: number of fields(Class)/cdatas(Cclass)
  * $methods: a Map for methods
  */
+
+#define x ...
+
 typedef struct Class {
    Header obj;
-   struct Class *super[1];
    Str *name;
-   Ubyte nfields;
-   Map *methods[1];
+   union {
+      struct Class *is[1];
+      Value *cdatas;
+   } sc;
+   Ubyte n;
+   Map *methods;
 } Class;
 
+/*
+ * What? Instance of a class
+ * $fields: (C)?class' attributes/cdata
+ */
+typedef struct {
+   Header obj;
+   Value *fields;
+} Instance;
+
+typedef struct Ma {
+
+} Ma;
+
+typedef struct {
+   Ma ma;
+   
+} Work;
+
 /*
  */
+
 typedef struct {
    Header obj;
    //...
@@ -297,6 +339,7 @@ typedef struct {
 
 /*
  */
+
 typedef struct Upvalue {
    Header obj;
    Value *to;

+ 103 - 0
utils/c3-mro.pl

@@ -0,0 +1,103 @@
+#!/usr/bin/env perl
+
+# Abstract: Check if c3 mro is well understood.
+# License: AGL, see LICENSE file for copyright and license details.
+#
+# General structure of a small DSL for defining classes with their
+# methods and inheritance relationships. This program parses the
+# DSL following __DATA__ and perform series of tests of c3 method
+# resolution with validation done with the help of Perl's c3 mro.
+#
+# CLASS_NAME: METHOD_NAME
+# SUBCLASS_NAME -> ( SUPERCLASS_NAME ... ) : METHOD_NAME ...
+#
+# Example:
+# O: meth1 meth2 meth3
+# A -> (O) : meth2
+# B -> (O) : meth1
+# C -> (A B) : meth2
+# 
+# The above example defines a section, sections are separated by
+# the token '###'. Sections are tested individually.
+
+use strict;
+use warnings;
+use feature qw(say);
+
+use Data::Dumper;
+
+sub parse_class_defs_section {
+   my $def_str = shift;
+   my $defs;
+
+   while ($def_str =~ /\G ([A-Z]) (?: \h+ -> \h+ \( (?<s>[A-Z](?:\h++[A-Z])*) \) )? (?: \h+ : \h+ (?<m>\w+(?:\h++\w++)*) )? \s* /gx) {
+      $defs->{$1} = undef;
+      if (defined $+{s}) {
+         my @supers = split " ", $+{s};
+         my @undefs = grep { !exists $defs->{$_} } @supers;
+         die "undefined class(es): [@undefs]\n" if @undefs;
+         $defs->{$1}{s} = [@supers];
+      }
+      $defs->{$1}{m} = [ split " ", $+{m} ] if defined($+{m});
+   }
+
+   return $defs;
+}
+
+sub parse_class_defs_sections {
+   my $data = shift;
+   my $sections;
+
+   push @$sections, parse_class_defs_section($1) while $data =~ /\G\s*(.+?)(?:###|\z)/gs;
+   return $sections;
+}
+
+sub gen_classdefs_perl_code {
+   my $sections = shift;
+   my ($section_codes, $perl_code);
+
+   foreach my $section (@$sections) {
+      $perl_code .= "\n\nuse feature qw(say);\n\n";
+      foreach my $class (sort keys %$section) {
+         my $class_info = $section->{$class};
+
+         $perl_code .= "package $class {\n\n";
+         $perl_code .= "\tuse parent -norequire qw(@{$class_info->{s}});\n\n" if exists $class_info->{s};
+         if (exists $class_info->{m}) {
+            foreach my $method (@{$class_info->{m}}) {
+               my $name = $method =~ s/@$//r;
+
+               $perl_code .= "\tsub $name { say 'A::$name'; " . ( $method =~ /@$/ ? "'$class'->next::method(); }" : "; }" );
+               $perl_code .= "\n\n";
+               $perl_code .= "}\n\n";
+            }
+         }
+      }
+      push @$section_codes, [ $section, $perl_code ];
+   }
+
+   return $section_codes;
+}
+
+sub c3_linearize {
+   my $section = shift;
+   my %linearized;
+
+   foreach my $class (sort keys %$section) {
+
+   }
+}
+
+{
+   local $/;
+   my $sections = parse_class_defs_sections(<DATA>);
+   say Dumper gen_perl_code($sections);
+}
+
+__DATA__
+O : meth1 meth2 meth3
+A -> (O) : meth2
+B -> (O) : meth1
+C -> (A B) : meth2
+###
+O