Skip to content

Commit

Permalink
Add support for object type (ballerina-platform#1218)
Browse files Browse the repository at this point in the history
  • Loading branch information
heshanpadmasiri authored Jul 27, 2023
1 parent 0331ffa commit 84ccc96
Show file tree
Hide file tree
Showing 25 changed files with 531 additions and 17 deletions.
29 changes: 28 additions & 1 deletion compiler/modules/front.syntax/ast.bal
Original file line number Diff line number Diff line change
Expand Up @@ -418,7 +418,7 @@ public type TypeDefn record {|

public type TypeDesc BuiltinTypeDesc|BinaryTypeDesc|ConstructorTypeDesc|TypeDescRef|SingletonTypeDesc|UnaryTypeDesc;

public type ConstructorTypeDesc TupleTypeDesc|ArrayTypeDesc|MappingTypeDesc|FunctionTypeDesc|ErrorTypeDesc|XmlSequenceTypeDesc|TableTypeDesc;
public type ConstructorTypeDesc TupleTypeDesc|ArrayTypeDesc|MappingTypeDesc|FunctionTypeDesc|ErrorTypeDesc|XmlSequenceTypeDesc|TableTypeDesc|ObjectTypeDesc;

public type TupleTypeDesc record {|
*PositionFields;
Expand Down Expand Up @@ -500,6 +500,33 @@ public type TableTypeDesc record {|
TypeDesc row;
|};

public type ObjectTypeDesc record {|
*PositionFields;
MemberDesc[] members;
t:ObjectDefinition? defn = ();
|};

public type MemberDesc FieldMemberDesc|MethodMemberDesc;

public type MemberDescBase record {|
*PositionFields;
Position namePos;
string name;
"field"|"method" kind;
TypeDesc td;
|};

public type FieldMemberDesc record {|
*MemberDescBase;
"field" kind = "field";
|};

public type MethodMemberDesc record {|
*MemberDescBase;
"method" kind = "method";
FunctionTypeDesc td;
|};

public type TypeDescRef record {|
*PositionFields;
string? prefix = ();
Expand Down
56 changes: 56 additions & 0 deletions compiler/modules/front.syntax/parseTypeDesc.bal
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,9 @@ function parsePrimaryTypeDesc(Tokenizer tok) returns TypeDesc|err:Syntax {
"record" => {
return parseRecordTypeDesc(tok, startPos);
}
"object" => {
return parseObjectTypeDesc(tok, startPos);
}
"table" => {
check tok.advance();
TypeDesc row = check parseTypeParam(tok);
Expand Down Expand Up @@ -327,6 +330,59 @@ function parseRecordTypeDesc(Tokenizer tok, Position startPos) returns MappingTy
}
}

function parseObjectTypeDesc(Tokenizer tok, Position startPos) returns ObjectTypeDesc|err:Syntax {
check tok.advance();
check tok.expect("{");
MemberDesc[] members = [];
while tok.current() != "}" {
Position fieldStartPos = tok.currentStartPos();
// We can have fields with function values
check tok.expect("public");
boolean isMethod = tok.current() == "function" && tok.peek() != "(";
MemberDesc memberDesc = isMethod ? check parseMethodMemberDesc(tok, fieldStartPos):
check parseFieldMemberDesc(tok, fieldStartPos);
members.push(memberDesc);
}
Position endPos = tok.currentEndPos();
check tok.advance();
return { startPos, endPos, members };
}

function parseFieldMemberDesc(Tokenizer tok, Position startPos) returns FieldMemberDesc|err:Syntax {
TypeDesc td = check parseTypeDesc(tok);
Position namePos = tok.currentStartPos();
string name = check tok.expectIdentifier();
Position endPos = check tok.expectEnd(";");
return { name, namePos, td, startPos, endPos };
}

function parseMethodMemberDesc(Tokenizer tok, Position startPos) returns MethodMemberDesc|err:Syntax {
check tok.advance();
Position namePos = tok.currentStartPos();
string name;
Token? t = tok.current();
match t {
[IDENTIFIER, var identifier] => {
name = identifier;
}
// SUBSET when we have join and start as keywords they need to be added here as well
"map" => {
// JBUG cast
name = <string>t;
}
_ => {
// JBUG name is not initialized otherwise
name = "";
return parseError(tok, "expected a valid method name");
}
}
check tok.advance();
// passing [] to parseFunctionTypeDesc to ensure parameters are named
FunctionTypeDesc td = check parseFunctionTypeDesc(tok, []);
Position endPos = check tok.expectEnd(";");
return { name, namePos, td, startPos, endPos };
}

function parseExclusiveRecordTypeDesc(Tokenizer tok, Position startPos) returns MappingTypeDesc|err:Syntax {
check tok.advance();
FieldDesc[] fields = [];
Expand Down
1 change: 1 addition & 0 deletions compiler/modules/front.syntax/scan.bal
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ final readonly & Keyword[] keywords = [
"match",
"never",
"null",
"object",
"panic",
"public",
"readonly",
Expand Down
36 changes: 36 additions & 0 deletions compiler/modules/front.syntax/syntaxNode.bal
Original file line number Diff line number Diff line change
Expand Up @@ -471,6 +471,9 @@ function syntaxNodeFromTypeDesc(TypeDesc td) returns SubSyntaxNode {
else if td is MappingTypeDesc {
return syntaxNodeFromMappingTypeDesc(td);
}
else if td is ObjectTypeDesc {
return syntaxNodeFromObjectTypeDesc(td);
}
else if td is FunctionTypeDesc {
return syntaxNodeFromFunctionTypeDesc(td);
}
Expand Down Expand Up @@ -531,6 +534,39 @@ function syntaxNodeFromMappingTypeDesc(MappingTypeDesc td) returns NonTerminalSy
}
}

function syntaxNodeFromObjectTypeDesc(ObjectTypeDesc td) returns NonTerminalSyntaxNode {
SubSyntaxNode[] members = from MemberDesc m in td.members select syntaxNodeFromMemberDesc(m);
return nonTerminalSyntaxNode(td, { token: "object", pos: td.startPos },
{ token: "{" },
members,
{ token: "}" });
}

function syntaxNodeFromMemberDesc(MemberDesc md) returns NonTerminalSyntaxNode {
if md is FieldMemberDesc {
return syntaxNodeFromObjectFieldMemberDesc(md);
}
// JBUG: cast
return syntaxNodeFromMethodDesc(<MethodMemberDesc>md);
}

function syntaxNodeFromObjectFieldMemberDesc(FieldMemberDesc md) returns NonTerminalSyntaxNode {
var { td, name, namePos } = md;
return nonTerminalSyntaxNode(md, { token: "public", pos: md.startPos },
syntaxNodeFromTypeDesc(td),
{ name, pos: namePos },
{ token: ";" });
}

function syntaxNodeFromMethodDesc(MethodMemberDesc md) returns NonTerminalSyntaxNode {
var { td, name, namePos } = md;
return nonTerminalSyntaxNode(md, { token: "public", pos: md.startPos },
{ token: "function" },
{ name, pos: namePos },
syntaxNodeFromFunctionTypeDesc(td, true),
{ token: ";" });
}

function syntaxNodeFromFunctionTypeDesc(FunctionTypeDesc td, boolean functionSignature = false) returns SubSyntaxNode {
SubSyntaxNode[] params = joinSyntaxNodesWithSeperator((from FunctionTypeParam param in td.params select syntaxNodeFromFunctionTypeParam(param)), { token: "," });
TypeDesc? retTd = td.ret;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
// @case
type O object {
public function foo(int, int);
};
// @end
4 changes: 4 additions & 0 deletions compiler/modules/front.syntax/tests/data/V-mod-object1.bal
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// @case
type O object {
};
// @end
19 changes: 19 additions & 0 deletions compiler/modules/front.syntax/tests/data/V-mod-object2.bal
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// @case
type O object {
public int a;
public function foo (int a, int b);
public object {
public string b;
public function bar (string a);
}
b;
public record {|
int c;
string d;
|}
c;
public function map (int a, int b);
public function join (int a, int b);
public function start (int a, int b);
};
// @end
19 changes: 10 additions & 9 deletions compiler/modules/front.syntax/token.bal
Original file line number Diff line number Diff line change
Expand Up @@ -34,44 +34,45 @@ type Keyword
| "anydata"
| "as"
| "boolean"
| "break"
| "byte"
| "check"
| "checkpanic"
| "const"
| "continue"
| "decimal"
| "else"
| "error"
| "false"
| "final"
| "float"
| "foreach"
| "function"
| "handle"
| "if"
| "import"
| "in"
| "is"
| "int"
| "is"
| "json"
| "map"
| "match"
| "never"
| "null"
| "object"
| "panic"
| "public"
| "readonly"
| "record"
| "return"
| "returns"
| "string"
| "table"
| "true"
| "type"
| "typedesc"
| "xml"
| "if"
| "else"
| "while"
| "continue"
| "break"
| "public"
| "import"
| "table"
| "xml"
;

type StringIterator object {
Expand Down
20 changes: 20 additions & 0 deletions compiler/modules/front/resolveTypes.bal
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,26 @@ function resolveTypeDesc(ModuleSymbols mod, s:ModuleLevelDefn modDefn, int depth
return check nonEmptyType(mod, modDefn, td, defn.getSemType(env));
}
}
if td is s:ObjectTypeDesc {
t:ObjectDefinition? defn = td.defn;
if defn == () {
t:ObjectDefinition d = new;
td.defn = d;
t:Member[] members = [];
string[] memberNames = [];
foreach s:MemberDesc memberDesc in td.members {
if memberNames.indexOf(memberDesc.name) != () {
return err:semantic(`duplicate member ${memberDesc.name}`, s:locationInDefn(modDefn, memberDesc.namePos));
}
memberNames.push(memberDesc.name);
t:SemType valueTy = check resolveTypeDesc(mod, modDefn, depth + 1, memberDesc.td);
"field"|"method" kind = memberDesc is s:FieldMemberDesc ? "field" : "method";
members.push({ name: memberDesc.name, valueTy, kind });
}
return nonEmptyType(mod, modDefn, td, d.define(env, members));
}
return nonEmptyType(mod, modDefn, td, defn.getSemType(env));
}
if td is s:ArrayTypeDesc {
t:ListDefinition? defn = td.defn;
if defn == () {
Expand Down
3 changes: 3 additions & 0 deletions compiler/modules/types.sexpr/schema.bal
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ public type Type AtomRef | Union | Intersection | Not |
List |
Mapping |
Table | TableSubtype |
Object | ObjectSubtype |
Xml | XmlSubtype |
Any | Readonly | Never;

Expand Down Expand Up @@ -51,6 +52,8 @@ public type List "list";
public type Mapping "mapping";
public type Table "table";
public type TableSubtype ["table", Type];
public type Object "object";
public type ObjectSubtype ["object", Atom];
public type Xml "xml";
// XXX: _rw and _ro are temporary placeholders until we support readonly in type sexpr
public type XmlBuiltinSubtypeNames "text"|"element_ro"|"pi_ro"|"comment_ro"|"element_rw"|"pi_rw"|"comment_rw"|"element"|"pi"|"comment";
Expand Down
24 changes: 23 additions & 1 deletion compiler/modules/types/core.bal
Original file line number Diff line number Diff line change
Expand Up @@ -1060,6 +1060,10 @@ public function mappingSubtype(SemType t) returns Bdd {
return <boolean|Bdd>subtypeData(t, BT_MAPPING);
}

public function objectSubtype(SemType t) returns Bdd {
return <boolean|Bdd>subtypeData(t, BT_OBJECT);
}

public function tableSubtype(SemType t) returns Bdd {
return <boolean|Bdd>subtypeData(t, BT_TABLE);
}
Expand Down Expand Up @@ -1357,6 +1361,14 @@ final CellAtomicType CELL_ATOMIC_INNER_RO = { ty: INNER_READONLY, mut: CELL_MUT_
final MappingAtomicType MAPPING_ATOMIC_INNER = { names: [], types: [], rest: CELL_SEMTYPE_INNER };
final ListAtomicType LIST_ATOMIC_INNER = { members: { initial: [], fixedLength: 0 }, rest: CELL_SEMTYPE_INNER };
final ListAtomicType LIST_ATOMIC_MAPPING = { members: {initial: [], fixedLength: 0 }, rest: CELL_SEMTYPE_INNER_MAPPING };
// JBUG: can't use ty: union(MEMBER_KIND_FIELD, MEMBER_KIND_METHOD) : nullPtr exeception at runtime
final CellAtomicType CELL_ATOMIC_OBJECT_MEMBER_KIND = { ty: STRING, mut: CELL_MUT_NONE };
final MappingAtomicType MAPPING_ATOMIC_OBJECT_MEMBER = { names: ["kind", "value"],
types: [CELL_SEMTYPE_OBJECT_MEMBER_KIND, CELL_SEMTYPE_VAL],
rest: CELL_SEMTYPE_NEVER };
// JBUG: This is an approximation since we get a nullPtr exeception when we try to represent ty as a union
final CellAtomicType CELL_ATOMIC_OBJECT_MEMBER = { ty: MAPPING_SEMTYPE_OBJECT_MEMBER, mut: CELL_MUT_UNLIMITED };
final MappingAtomicType MAPPING_ATOMIC_OBJECT = { names: [], types: [], rest: CELL_SEMTYPE_OBJECT_MEMBER };

final TypeAtom ATOM_CELL_VAL = { index: 0, atomicType: CELL_ATOMIC_VAL };
final TypeAtom ATOM_CELL_NEVER = { index: 1, atomicType: CELL_ATOMIC_NEVER };
Expand All @@ -1366,18 +1378,28 @@ final TypeAtom ATOM_LIST_MAPPING = { index: 4, atomicType: LIST_ATOMIC_MAPPING }
final TypeAtom ATOM_CELL_INNER_MAPPING_RO = { index: 5, atomicType: CELL_ATOMIC_INNER_MAPPING_RO };
final TypeAtom ATOM_LIST_MAPPING_RO = { index: 6, atomicType: LIST_ATOMIC_MAPPING_RO };
final TypeAtom ATOM_CELL_INNER_RO = { index: 7, atomicType: CELL_ATOMIC_INNER_RO };
final TypeAtom ATOM_MAPPING_OBJECT = { index: 8, atomicType: MAPPING_ATOMIC_OBJECT };
final TypeAtom ATOM_MAPPING_OBJECT_MEMBER = { index: 9, atomicType: MAPPING_ATOMIC_OBJECT_MEMBER };
final TypeAtom ATOM_CELL_OBJECT_MEMBER = { index: 10, atomicType: CELL_ATOMIC_OBJECT_MEMBER };
final TypeAtom ATOM_CELL_OBJECT_MEMBER_KIND = { index: 11, atomicType: CELL_ATOMIC_OBJECT_MEMBER_KIND };

const BDD_REC_ATOM_READONLY = 0;
final BddNode BDD_SUBTYPE_RO = bddAtom(BDD_REC_ATOM_READONLY); // represents both readonly & map<readonly> and readonly & readonly[]
final BddNode LIST_SUBTYPE_MAPPING = bddAtom(ATOM_LIST_MAPPING); // represents (map<any|error>)[]
final BddNode LIST_SUBTYPE_MAPPING_RO = bddAtom(ATOM_LIST_MAPPING_RO); // represents readonly & (map<readonly>)[]
// represents record { ObjectField...; } where ObjectField = { string kind; any value }
final BddNode MAPPING_SUBTYPE_OBJECT = bddAtom(ATOM_MAPPING_OBJECT);

final ComplexSemType MAPPING_RO = basicSubtype(BT_MAPPING, BDD_SUBTYPE_RO);
final CellSemType CELL_SEMTYPE_VAL = <CellSemType>basicSubtype(BT_CELL, bddAtom(ATOM_CELL_VAL));
final CellSemType CELL_SEMTYPE_INNER = <CellSemType>basicSubtype(BT_CELL, bddAtom(ATOM_CELL_INNER));
final CellSemType CELL_SEMTYPE_INNER_MAPPING = <CellSemType>basicSubtype(BT_CELL, bddAtom(ATOM_CELL_INNER_MAPPING));
final CellSemType CELL_SEMTYPE_INNER_RO = <CellSemType>basicSubtype(BT_CELL, bddAtom(ATOM_CELL_INNER_RO));
final CellSemType CELL_SEMTYPE_INNER_MAPPING_RO = <CellSemType>basicSubtype(BT_CELL, bddAtom(ATOM_CELL_INNER_MAPPING_RO));
final CellSemType CELL_SEMTYPE_NEVER = <CellSemType>basicSubtype(BT_CELL, bddAtom(ATOM_CELL_NEVER));
final ComplexSemType MAPPING_SEMTYPE_OBJECT_MEMBER = basicSubtype(BT_MAPPING, bddAtom(ATOM_MAPPING_OBJECT_MEMBER));
final CellSemType CELL_SEMTYPE_OBJECT_MEMBER = <CellSemType>basicSubtype(BT_CELL, bddAtom(ATOM_CELL_OBJECT_MEMBER));
final CellSemType CELL_SEMTYPE_OBJECT_MEMBER_KIND = <CellSemType>basicSubtype(BT_CELL, bddAtom(ATOM_CELL_OBJECT_MEMBER_KIND));

public function cellAtomicType(SemType t) returns CellAtomicType? {
if t is BasicTypeBitSet {
Expand Down Expand Up @@ -1731,7 +1753,7 @@ function init() {
mappingOps, // mapping
tableOps, // table
xmlOps, // xml
{}, // object
objectOps, // object
cellOps, // cell
{} // undef
];
Expand Down
Loading

0 comments on commit 84ccc96

Please sign in to comment.