namespace fbsemantic;

table Fresher {
  u:ulong;
}

table TypeEnvironment {
  assignments:[TypeAssignment];
}

table TypeAssignment {
  id:string;
  ty:PolyType;
}

union MonoType {
  Basic,
  Var,
  Arr,
  Record,
  Fun,
  Dict,
}

table MonoTypeHolder {
  typ:MonoType;
}

enum Type : ubyte {
  Bool,
  Int,
  Uint,
  Float,
  String,
  Duration,
  Time,
  Regexp,
  Bytes,
}

table Var {
  i:ulong;
}

table Basic {
  t:Type;
}

// TODO: required directives have been commented out due to the following issue:
// https://github.com/google/flatbuffers/issues/5055
//
// Uncomment when the fix https://github.com/google/flatbuffers/pull/5056 has
// been released.

table Arr {
  t:MonoType /*(required)*/;
}

table Record {
  props:[Prop] /*(required)*/;
  extends:Var;
}

table Fun {
  args:[Argument] /*(required)*/;
  retn:MonoType   /*(required)*/;
}

table Dict {
  k:MonoType /*(required)*/;
  v:MonoType /*(required)*/;
}

table Argument {
  name:string /*(required)*/;
  t:MonoType  /*(required)*/;
  pipe:bool;
  optional:bool;
}

table Prop {
  k:string   /*(required)*/;
  v:MonoType /*(required)*/;
}

table PolyType {
  vars:[Var]        /*(required)*/;
  cons:[Constraint] /*(required)*/;
  expr:MonoType     /*(required)*/;
}

enum Kind : ubyte {
  Addable,
  Subtractable,
  Divisible,
  Numeric,
  Comparable,
  Equatable,
  Nullable,
  Record,
  Negatable,
  Timeable,
  Stringable,
}

table Constraint {
  tvar:Var /*(required)*/;
  kind:Kind;
}

union Statement {
  OptionStatement,
  BuiltinStatement,
  TestStatement,
  ExpressionStatement,
  NativeVariableAssignment,
  MemberAssignment,
  ReturnStatement,
}

union Assignment {
  MemberAssignment,
  NativeVariableAssignment,
}

union Expression {
  StringExpression,
  ArrayExpression,
  DictExpression,
  FunctionExpression,
  BinaryExpression,
  CallExpression,
  ConditionalExpression,
  IdentifierExpression,
  LogicalExpression,
  MemberExpression,
  IndexExpression,
  ObjectExpression,
  UnaryExpression,
  BooleanLiteral,
  DateTimeLiteral,
  DurationLiteral,
  FloatLiteral,
  IntegerLiteral,
  StringLiteral,
  RegexpLiteral,
  UnsignedIntegerLiteral,
}

table Package {
  loc:SourceLocation;
  package:string;
  files:[File];
}

table File {
  loc:SourceLocation;
  package:PackageClause;
  imports:[ImportDeclaration];
  body:[WrappedStatement];
}

table PackageClause {
  loc:SourceLocation;
  name:Identifier;
}

table ImportDeclaration {
  loc:SourceLocation;
  alias:Identifier;
  path:StringLiteral;
}

table SourceLocation {
  file:string;
  start:Position;
  end:Position;
  source:string;
}

struct Position {
  line:int;
  column:int;
}

// Statements

// Unions cannot be vector elements,
// so wrap the union in a table.
table WrappedStatement {
  statement:Statement;
}

table OptionStatement {
  loc:SourceLocation;
  assignment:Assignment;
}

table BuiltinStatement {
  loc:SourceLocation;
  id:Identifier;
}

table TestStatement {
  loc:SourceLocation;
  assignment:NativeVariableAssignment;
}

table ExpressionStatement {
  loc:SourceLocation;
  expression:Expression;
}

table ReturnStatement {
  loc:SourceLocation;
  argument:Expression;
}

table NativeVariableAssignment {
  loc:SourceLocation;
  identifier:Identifier;
  init_:Expression;
  typ:PolyType;
}

table MemberAssignment {
  loc:SourceLocation;
  member:MemberExpression;
  init_:Expression;
}

// Expressions

// Unions cannot be vector elements,
// so wrap the union in a table.
table WrappedExpression {
  expression:Expression;
}

table StringExpression {
  loc:SourceLocation;
  parts:[StringExpressionPart];
}

table StringExpressionPart {
  loc:SourceLocation;
  // Following two fields are mutually exclusive:
  text_value:string;// for TextPart
  interpolated_expression:Expression;// for InterpolatedPart
}

table ArrayExpression {
  loc:SourceLocation;
  elements:[WrappedExpression];
  typ:MonoType;
}

table DictExpression {
  loc:SourceLocation;
  elements:[DictItem];
  typ:MonoType;
}

table DictItem {
  key:Expression;
  val:Expression;
}

table FunctionExpression {
  loc:SourceLocation;
  params:[FunctionParameter];
  body:Block;
  typ:MonoType;
}

table FunctionParameter {
  loc:SourceLocation;
  is_pipe: bool;
  key:Identifier;
  default:Expression;
}

table Block {
  loc:SourceLocation;
  body:[WrappedStatement];
}

enum Operator : byte {
  MultiplicationOperator,
  DivisionOperator,
  ModuloOperator,
  PowerOperator,
  AdditionOperator,
  SubtractionOperator,
  LessThanEqualOperator,
  LessThanOperator,
  GreaterThanEqualOperator,
  GreaterThanOperator,
  StartsWithOperator,
  InOperator,
  NotOperator,
  ExistsOperator,
  NotEmptyOperator,
  EmptyOperator,
  EqualOperator,
  NotEqualOperator,
  RegexpMatchOperator,
  NotRegexpMatchOperator,
  InvalidOperator,
}

enum LogicalOperator : byte {
  AndOperator,
  OrOperator,
}

table BinaryExpression {
  loc:SourceLocation;
  operator:Operator;
  left: Expression;
  right: Expression;
  typ:MonoType;
}

table CallExpression {
  loc:SourceLocation;
  callee:Expression;
  arguments:[Property];
  pipe:Expression;
  typ:MonoType;
}

table ConditionalExpression {
  loc:SourceLocation;
  test:Expression;
  alternate:Expression;
  consequent:Expression;
}

table LogicalExpression {
  loc:SourceLocation;
  operator:LogicalOperator;
  left:Expression;
  right:Expression;
}

table MemberExpression {
  loc:SourceLocation;
  object:Expression;
  property:string;
  typ:MonoType;
}

table IndexExpression {
  loc:SourceLocation;
  array:Expression;
  index:Expression;
  typ:MonoType;
}

table ObjectExpression {
  loc:SourceLocation;
  with:IdentifierExpression;
  properties:[Property];
  typ: MonoType; 
}

table UnaryExpression {
  loc:SourceLocation;
  operator:Operator;
  argument:Expression;
  typ:MonoType;
}

table Property {
  loc:SourceLocation;
  key:Identifier;
  value:Expression;
}

table IdentifierExpression {
  loc:SourceLocation;
  name:string;
  typ:MonoType;
}

table Identifier {
  loc:SourceLocation;
  name:string;
}

table BooleanLiteral {
  loc:SourceLocation;
  value:bool;
}

table DateTimeLiteral {
  loc:SourceLocation;
  value:Time;
}

table Time {
  secs:int64;
  nsecs:uint32;
  offset:int32;
}

table Duration {
  months: int64;
  nanoseconds: int64;
  negative: bool;
}

table DurationLiteral {
  loc:SourceLocation;
  value:[Duration];
}

table IntegerLiteral {
  loc:SourceLocation;
  value:int64;
}

table FloatLiteral {
  loc:SourceLocation;
  value:float64;
}

table RegexpLiteral {
  loc:SourceLocation;
  value:string;
}

table StringLiteral {
  loc:SourceLocation;
  value:string;
}

table UnsignedIntegerLiteral {
  loc:SourceLocation;
  value:uint64;
}

root_type Package;
