X-Git-Url: https://ocean-lang.org/code/?p=ocean-D;a=blobdiff_plain;f=Ocean-types;h=a89544cc3c87da1b04b679ff0cff88bc5d401720;hp=f0f9d929494acf7ae114e17776f33d09710154cb;hb=b07cd85563bb728e47d3781428d74c81db76e45e;hpb=6bbab56e5001bac144c6ef8fe73ae27df4695bb3 diff --git a/Ocean-types b/Ocean-types index f0f9d92..a89544c 100644 --- a/Ocean-types +++ b/Ocean-types @@ -24,6 +24,19 @@ Types can be constructed with (args:: args) procedure type (args:: type) function type + I think that for pointer/array constructor, the decoration comes first. + foo: [5]int + is an array of 5 integers, so foo[5] is an int + foo: @bar + is an owned pointer to bar, so "foo@" is a bar. + foo: @[5]int + is an owner pointer to 5 integers while + foo:[5]^int + is an array of 5 borrowed pointers to integers. + Having the result type at the end fits better with the function type. + foo:(int, int)string + then foo(1,2) returns a string. + The content of struct and record are a list of: fieldname: type @@ -94,12 +107,46 @@ Plan: 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 + I currently have an enum of types that is used to test comparability and for propagation. This needs to change .... I guess I need a struct type* What goes in it? - name @@ -120,6 +167,26 @@ Plan: .Bool.true = a:char; b:char .Bool.false = ...... + I like "inheritance" but that doesn't allow the size of the whole to + be known in advance. + I need a way to talk about which instance is active, even if the value + isn't stored. So Pascal-like variant records are good. + Maybe a struct could be declared as 'extensible' and other structs + could 'extend', and it literally makes it bigger + struct a extensible {x1:int} + struct b extends a {y1:int} + struct c extends a extensible {y2:int} + struct d extends c {z1:int} + + now 'a' has room for x1, (y2 and z1) or y1 + Which is used depends on context .. or by assertion on content. + So extensions can set values for existing fields + struct z extends a {x1=4; string c} + + Syntax is a bit clumsy. Do I need "extensible"?? + + + Are enums just a fancy way of doing 'const'? I could have const: a=1; b=2; c; d @@ -142,3 +209,211 @@ Plan: 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] + e.g. int[4] or foo@[3] + What is int[5][20] ?? it is an array of int[5], which is backwards. + So maybe I want [5][20]int ?? + 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. + + When do I differentiate between compile-time constants and run-time values? + When declaring an array, do I require the size to be constant? + In a struct I do ... at time of declaration I calculate the size. + For foo:[sqr(a)]int + I do that too - and it is at run-time. + So during parsing, I need to describe the array with a member-type and executable size. + When that is evaluated, a type is created. + So we really need an executable which returns a type. + But ... we need to know the type when doing type analysis. So while variable size + is OK, the compiler needs to know what it is. Maybe the size needs to be a constant, as in + a names assiged with "size ::= 4*5". This gives the compiler some chance of comparing + types of array - and doing range checking on indexes. + + We currently call var_init to set the type of a variable during type + analysis - which makes sense. + But for an array we don't have the final type until run-time. So we need an + intermediate type. + So (for now) the size of an array is either a NUMBER or an IDENTIFIER which must be a + constant var. + I need a point where the type is instantiated - where the variable is evaluated + and the size is set. I guess this happens when the 'struct var' is evaluated... + no, when a Declare binode is evaluated. + + What happens if I have + a:[foo]number = thing + I guess the type analysis needs to afirm that thing has the correct type, + then a doesn't need to be initialized. + + If I find + a[4] = "hello" + and 'a' hasn't been declared .... obviously an error. + + When/what/how. + For field access, I need to know the type of the variable. + But I can delay the look up until type analysis. + So a[4] is Index(a, 4) - a binode + a.foo is Field(a, "foo") - need a new exec type - Fieldname + a(args) is Call(a, args) - need a new Binode type - Tuple. + +So I have to delay 'const' assessment to later too. + +---------- +Where do type definition go? +I don't think they go with statements, they belong separately. +I don't want the full separation of a "type" section like Pascal +So they probably go at the top level, equivalent to "program" - and before. +They start with "struct" or "enum" or "record" etc. + +So: what about constants? These are currently statements and so affect a scope in time. +But for declaring arrays in structs, or initial values of fields, we might want constants. +A constant could be within a struct, but only that it too limiting. I need module-wide +constants. +So I guess: + + const: + name ::= value + name ::= value + +or + const { name ::= value ; name ::= value } + +-------------- +I'm in the middle of stage-1 on structures. + +I need a type to parse the declaration into. It needs to be a linked list +of fields, each of which is a type, a name, and an initial value. i.e. a 'struct field'. + +----------------- + +Numbers... +I want signed/unsigned/bitset integers (and probably floats). +These are different sizes, and I want to move 'type' out of 'value' +so I can have arrays of numbers that are *just* the densely packets numbers. + +So there are two questions here: how will I handle values in oceani, and +what are the semantics of numbers in ocean. + +I think I want bitops to requires bitsets and arith ops to require signed/unsigned. +But there is some overlap. +e.g. we use bitops to test if a number is a power of two +We sometimes use bitops to multiply, but that is probably best avoided. +use * to multiply. + +Converting between the two can be done with simple assignment. + +So + - * / % require/assume signed or unsigned + | & ~ << >> require/assume bitset + + # accepts either and produces a bitset + +Other issue is overflow/underflow checking. +Do we need another unsigned type - cyclic + + i32 - signed integer in 32 bits + u32 - unsigned integer + c32 - unsigned with overflow permitted and ignored + b32 - bitset + + int uint cint bset - whatever size. + +i32 and u32 detect overflow/underflow and set to NaN - all 1's +If I want to allow overloading (such a NaN), I need a type that +declare no overloading. s32 and c32? Or annotations. !s32 !u32 + +So what about values in oceani? I want to separate out the type and not +use a union. +Where are they used? + - return of init, prepare, parse, dup + - passed to print, cmp, dup, free, to_int, to_float, to_mpq + - field in 'struct variable' + - field in 'struct lrval' + - result of 'interp' + - intermediate left/right in interp + - field in array and struct field + - field in 'struct val' for manifest constants + +So: + variable gets a 'type' pointer and a union which can be a pointer + to the value, or the value itself (depending on size) + lrval get a type pointer as well, plus the union + interp returns ... + + +----------------- +Struct/array initialisers. +I like [a,b,c] rather than {a,b,c} because the latter can look like code. +But [] is also array indexing. +So an array initializer could look: + [ [1] = "hello", [5] = "there" ] +and that is confusingly similar to nested initialization + [ [1,2] , [3,4] ] +Options: + 1/ use different outer. {} () <> << >> + < is possibly as it is not a prefix operator. + But nesting results in <<1,2>,<3,4>> which looks like << instead of < < + {} I already don't like + () is bad enough with function calls - it is best if it is grouping only. + though with function calls it is a list ... + << [1]="hello", [2]="there" >>... I don't really like that + + array[ ] + struct[ ] + No, too noisy. + + 2/ use different inner syntax. + [ .[1] = "hello", .[5] = "hello" ] + + What about a newline-based syntax: + a: [4]int : + [0] = 2 + [1] = 3 + [3] = 1 + + Nice, but doesn't actually help. Still need .[] because I want to allow + a one-line syntax too. + Maybe I just use {} after all. + + a:[4]int = { [0]=2, [1]=3, [3]=1 } + Yes, I guess that is best.