chharvey / counterpoint

A robust programming language.
GNU Affero General Public License v3.0
2 stars 0 forks source link

Syntax & Semantics: Keys #59

Open chharvey opened 3 years ago

chharvey commented 3 years ago

Keys are global primitive values without intrinsic semantics.

Discussion

Keys are values whose implementations are hidden.

.WATER; % a key
.FIRE;  % another key

Key names are programmer-defined identifiers, and always follow a dot. They’re conventionally written in MACRO_CASE, but sometimes also in snake_case.

The values of Keys are the only of their kind, and their implementations are unexposed. Keys can be thought of as integers or strings, but can’t be operated on as such and do not take up as much space. Keys are also not assignable to the int or str types. Like all values, keys can be assigned to types obj and unknown.

let greet: obj = .hello_world;

Key names may be any keyword or identifier, starting with a letter or underscore, and subsequently containing letters, underscores, and/or digits. Key names may also be Unicode names, but this is not recommended as it hinders readability. As with Unicode identifiers, there are no escapes.

let greet: unknown = .'¡héllö wòrld!';

.'\u{24}3.99' != .'$3.99';

Other than obj and unknown, Keys are assignable to their unit types. (Unit types are types containing only one value — also referred to as “constant types”.)

let earth: .EARTH = .EARTH;

Key unit types can be unioned to form an enumeration of values.

type Element = .WATER | .EARTH | .FIRE | .AIR;
let unfixed el: Element = .FIRE;
set el = .AIR;
set el = .AETHER; %> TypeError

All Key unit types are disjoint, so their intersection is always never.

type Impossible = .WATER & .EARTH; % type `never`

Counterpoint has a designated key type, which is conceptually the infinite union of all Key values. Any Key value is assignable to the key type, and only Key values are assignable to this type.

let unfixed el: key = .FIRE;
set el = .AIR;
set el = .AETHER;
set el = 42;         %> TypeError
set el = "a string"; %> TypeError

Key values are always truthy and always nonempty.

!(.WATER) == false; % `!.WATER` is a parse error
?(.FIRE)  == false; % `?.FIRE`  is a parse error

.EARTH || .AIR == .EARTH;
.EARTH && .AIR == .AIR;

(Note: The Keys above must be wrapped in parentheses, because the tokens !. and ?. are accessor operators. Or we could use whitespace to separate the operator from the Key.)

! .WATER == false; % also acceptable, but maybe less readable
? .FIRE  == false; % also acceptable, but maybe less readable

Keys are equal if and only if their names are exactly the same. Keys are identical if and only if they are equal.

.LIGHT   !=  .DARK;
.NEUTRAL === .NEUTRAL;
.NEUTRAL !=  .neutral;

As primitive values, Key values are value objects rather than reference objects.

Specification

Lexicon

Keyword :::=
    // literal
        | "void"
        | "null"
        | "bool"
        | "false"
        | "true"
+       | "key"
        | "int"
        | "float"
        | "str"
        | "obj"
    // operator
        | "mutable"
        | "is"
        | "isnt"
        | "if"
        | "then"
        | "else"
    // storage
        | "let"
        | "type"
    // modifier
        | "unfixed"
;

Syntax

Word ::=
    | KEYWORD
    | IDENTIFIER
;

+Key
+   ::= "." Word;

PrimitiveLiteral ::=
    | "null"
    | "false"
    | "true"
    | INTEGER
    | FLOAT
    | STRING
+   | Key
;

TypeKeyword ::=
    | "void"
    | "bool"
+   | "key"
    | "int"
    | "float"
    | "str"
    | "obj"
;

Decorate

+Decorate(Key ::= "." Word) -> SemanticConstant
+   := (SemanticConstant[value=new Key(Decorate(Word).id)]);

Decorate(PrimitiveLiteral ::= "null") -> SemanticConstant
    := (SemanticConstant[value=null]);
Decorate(PrimitiveLiteral ::= "false") -> SemanticConstant
    := (SemanticConstant[value=false]);
Decorate(PrimitiveLiteral ::= "true") -> SemanticConstant
    := (SemanticConstant[value=true]);
Decorate(PrimitiveLiteral ::= INTEGER) -> SemanticConstant
    := (SemanticConstant[value=new Integer(TokenWorth(INTEGER))]);
Decorate(PrimitiveLiteral ::= FLOAT) -> SemanticConstant
    := (SemanticConstant[value=new Float(TokenWorth(FLOAT))]);
Decorate(PrimitiveLiteral ::= STRING) -> SemanticConstant
    := (SemanticConstant[value=new String(TokenWorth(STRING))]);
+Decorate(PrimitiveLiteral ::= Key) -> SemanticConstant
+   := Decorate(Key);

Decorate(TypeKeyword ::= "void") -> SemanticTypeConstant
    := (SemanticTypeConstant[value=Void]);
Decorate(TypeKeyword ::= "bool") -> SemanticTypeConstant
    := (SemanticTypeConstant[value=Boolean]);
+Decorate(TypeKeyword ::= "key") -> SemanticTypeConstant
+   := (SemanticTypeConstant[value=Key]);
Decorate(TypeKeyword ::= "int") -> SemanticTypeConstant
    := (SemanticTypeConstant[value=Integer]);
Decorate(TypeKeyword ::= "float") -> SemanticTypeConstant
    := (SemanticTypeConstant[value=Float]);
Decorate(TypeKeyword ::= "str") -> SemanticTypeConstant
    := (SemanticTypeConstant[value=String]);
Decorate(TypeKeyword ::= "obj") -> SemanticTypeConstant
    := (SemanticTypeConstant[value=Object]);

Decorate(TypeUnit ::= PrimitiveLiteral) -> SemanticTypeConstant
    := (SemanticTypeConstant[value=ToType(Decorate(PrimitiveLiteral).value)]);

Decorate(ExpressionUnit ::= PrimitiveLiteral) -> SemanticConstant
    := Decorate(PrimitiveLiteral);

Semantics

-SemanticConstant[value: Null | Boolean |       Number | String]
+SemanticConstant[value: Null | Boolean | Key | Number | String]
    ::= ();

Data Types

 - [Never](#never)
 - [Void](#void)
 - [Null](#null)
 - [Boolean](#boolean)
+- [Key](#key)
 - [Integer](#integer)
 - [Float](#float)
 - [String](#string)
 - [Object](#object)
 - [Unknown](#unknown)

+#### Key
+The **Key** type contains programmer-defined values whose implementations are hidden.
+They may only be referred to by name.
+The meaning of each Key value may be specified by the programmer.

ValueOf

-Or<Null, Boolean,      Number, String> ValueOf(SemanticConstant const) :=
+Or<Null, Boolean, Key, Number, String> ValueOf(SemanticConstant const) :=
    1. *Return:* `const.value`.
;

Build

Note: Keys will be implemented as int32 values in the virtual machine.

+Sequence<Instruction> Build(Key a) :=
+   1. *Return:* ["Push `a` onto the operand stack."].
+;