Types have a per-module namespace. This is pre-populated with int i8 i16 uint u8 u64 etc num float f64 f128 Bool byte char string Types can be added with: struct name: content record name: content enum name: content class name: content name is optional, and can list (parameters) and /attributes Types can be constructed with name(args) parameterized type name^ reference type name[size] array type (args:: args) procedure type (args:: type) function type The content of struct and record are a list of: fieldname: type or fieldname/attribute: type 'attribute' can: indicate endianness - bigendian littleendian hostendian 'const' ?? identify a refcount, protected by a given lock?? For enum, content is list of name = value where "= value" is optional Pointers: to assign a pointer, use foo = stuff to update what the pointer points to, use foo^ = stuff to get a reference to store in a pointer.... references are either borrowed or owned. a name defined "type^" is a borrowed reference, a name defined "type@" is owned. borrowed references may be taken of anything, but only remain defined as long as the owner remains defined. owned references can only be taken of ownable objects, and remain indefinitely. There are various ways to own an object: - refcount or lock - ownership of a containing object. - ownership provided by class method non-type names (vars, constants) can be introduced with const prefix: name = value; ... func: name(args::type): statements proc: name(args::result): statements main: statements init: exit: enum name: values Plan: decide on data structure different types can't really be handled by a big switch now, I probably need and object with function pointers. free_value(), vtype_compat(), val_init(), dup_value(), value_cmp(), print_value(), parse_value() parse_value only needed for args - str and num val_init... print_value - only needed for print and code-dump dup_value - needed until pointers can make sense vtype_compat - needed for various things value_cmp - needed until we have object behaviours pre-defined Bool int - sizes char string array I think I need to disassociate the type from the storage. So a 'value' is a parsed constant, but something new is needed for the content of a variable. Also, something new is needed for an intermediate lvalue such as an index to an array or a field in a record. Ths is a type plus a pointer. What about rvalues and the result of a calculation. I guess I store that in a temp location, with a pointer... I need to be clear what "interp_exec()" returns. Conceptually it can be an object of any type, and for procedures it can be a tuple of objects. It needs to identify a type and a value of that type. The "value" might be a reference into a variable, or it might be a copy from a calculation. Eventually the reference will have ownership information const struct Steps: 0/ when does 1/2 produce an integer? Only when explicitly expected. 1/ remove 'tail' from value 2/ define a 'type'. 3/ Add a 'Vtyped' vtype and a 'type' pointer 4/ add an owership enum: borrowed, single 3/ add a 'void *valref' ?? 4/ Convert num bool str label to Vtyped No, this is awkward because propagate_types wants a 'type' and this intermediate format has a enum+pointer. So make it just a pointer... What do I do with Vnolabel - rule flag Vunknown - NULL pointer Vnone - special value A 'type' must be able to: check is it can convert to some other type, reporting if it wants to convert to another type. This requires visibility into other types. print, compare, parse add subtract multiply divide index ---------- I currently have an enum of types that is used to test compatability and for propagation. This needs to change .... I guess I need a struct type* What goes in it? - name - scalar/record/struct/array/pointer/func/enum - other details. Do I need forward declarations? Maybe I can just be lazy and require everything to be declared eventually. That isn't sufficient for: - mutually recursive functions - mutually recursive structures What about unions ??? blend with enum: a tag with fields? struct name: x:int; y:int .Bool.true = a:char; b:char .Bool.false = ...... Are enums just a fancy way of doing 'const'? I could have const: a=1; b=2; c; d which defines module-wide consts. And separately have enum foo: a, b, c which defines consts foo.a foo.b foo.c But I don't really like foo entering the val namespace. If bar:foo, then bar.a could be true/false depending on value of bar. bar.a! could set it?? bar.a=true? var = .foo.a How do we allocate new objects? new(type) ??? ptrname = new ?? new(ptrname) ptrname := new(type) Maybe ^= assigns a borrowed reference, and @= assigns an owned reference. allocating isn't really a top priority, so I should just focus on non-allocated types. ----- I need a list of steps again: - look up type by name and add syntax for name : type = value and name : type - Add arrays: name : type[size] declares an array of that type/size name : [] = [ 1,2,3 ] declares an array of 3 numbers. name[1] extracts an element from the array, name[2] = 4 updates an element name[4:5] creates a slice, which can be stored in a var (borrowed ref) or assigned to This requires: - new type class which has a size and member type - new type access methods: index and size(?) - new syntax - new manifest values: [a,b,c] creates an array of whatever member type. - add syntax for struct name : [[ name : type ]] to define a new struct - add syntax to extra a field from a struct how is this type-checked if I don't know the type yet? - add syntax for pointers a:= new(struct foo) a:struct foo^ = new() ------- Questions: If I declare "struct foo ..." do I use "foo" or "struct foo" to ref the type? I think just "foo". So structs, records, enums, and classes must have distinct names.