Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Strucutre #18

Open
lan-lyu opened this issue Jun 7, 2023 · 1 comment
Open

Strucutre #18

lan-lyu opened this issue Jun 7, 2023 · 1 comment
Labels

Comments

@lan-lyu
Copy link
Collaborator

lan-lyu commented Jun 7, 2023

Code Structure

  1. go through the file: parse the SourceFile and get Statements[]
  2. build IR: convert the Statements[] into the Internal Representation, replace type reference with exact types
  3. generate output: from Internal Representation, emit the output in typescript and python

Input & Output Example

For each step, the example input and output are:

step 1

input:

interface FieldDef {
  field: string
  type: "quantitative" | "ordinal"
}

type ValueDef<T> = {
  value: T
}

type PositionDef = FieldDef;
type ColorDef = FieldDef | ValueDef<string>
type PrimitiveMark = "bar" | "area" | "line";
type Mark = PrimitiveMark | {
  type: PrimitiveMark
}

interface Encoding {
  x?: PositionDef;
  y?: PositionDef;
  color?: ColorDef;
}

interface LayerSpec {
  layer: Spec[]
}

export type Spec = {
    mark: Mark
    data: string
    encode: Encoding
}

output:

Statements[9]

export class Statement {
    public name?: string;
    public kind: typeKind | undefined; 
    public type: typeType | undefined;
    public isGeneric: boolean = false;
    public members: Record<string, string> = {};
    public children: string[] | undefined;
}

[0]{
    name: "FieldDef"
    kind: 261 // create our own enum
    type: undefined
    isGeneric: false
    members: {"field": "string", "type": '"quantitative" | "ordinal"'}
    children:[]
}

[1]{
    name: "ValueDef"
    kind: 262
    type: 184 //TypeLiteral
    isGeneric: true
    members: {"value": "T"}
    children:[]
}

[2]{
    name: "PositionDef"
    kind: 262
    type: 180  //TypeReference
    isGeneric: false
    members: {"type": "FieldDef"}
    children:["FieldDef"]
}

[3]{
    name: "ColorDef"
    kind: 262
    type: 189 //UnionType
    isGeneric: false
    members: {"type": "FieldDef | ValueDef<string>"}
    children:["FieldDef", "ValueDef<string>"]
}

[4]{
    name: "PrimitiveMark"
    kind: 262
    type: 189 //UnionType
    isGeneric: false
    members: {"type": "bar | area | line"}
    children:[]
}

[5]{
    name: "Mark"
    kind: 262
    type: 189 //UnionType
    isGeneric: false
    members: {"type": "PrimitiveMark | { PrimitiveMark }"}
    children:["PrimitiveMark"]
}

[6]{
    name: "Encoding"
    kind: 261
    type: undefined
    isGeneric: false
    members: {"x?": "PositionDef", "y?": "PositionDef", "color?": "ColorDef"}
    children:["PositionDef", "ColorDef"]
}

[7]{
    name: "LayerSpec"
    kind: 261
    type: undefined
    isGeneric: false
    members: {"layer": "Spec[]"}
    children:["Spec"]
}

[8]{
    name: "Spec"
    kind: 262
    type: 184  //TypeLiteral
    isGeneric: false
    members: {"mark": "Mark", "data": "string", "encode": "Encoding"}
    children:["Mark", "Encoding"]
}

step 2

build a tree that the root node is Spec

output is a tree like this

spec ___ mark
         |___ data
         |___ encode ____ x
                     |____ y
                     |____ color

At the same time, replace the TypeReference with the exact type based on children of the statement.
For example: turn mark's member into "type: bar | area | line | { bar| area | line}"
input (statement we have in step 1)

[4]{
    name: "PrimitiveMark"
    kind: 262
    type: 189 //UnionType
    isGeneric: false
    members: {"type": "bar | area | line"}
    children:[]
}

[5]{
    name: "Mark"
    kind: 262
    type: 189 //UnionType
    isGeneric: false
    members: {"type": "PrimitiveMark | { PrimitiveMark }"}
    children:["PrimitiveMark"]
}

output (mark object in the tree)

{
    name: "Mark"
    kind: 262
    type: 189 //UnionType
    isGeneric: false
    members: {"type": "bar | area | line | { bar| area | line}"}
}

step 3

Traverse the tree and generate classes and functions

  • generate "class Spec" and "export function spec"
  • mark, data, encode, x, y, color
  • toSpec and toJSON
  • need to add method chaining
class Spec {
  constructor(private mark: Mark, private data: string, private encode: Encoding) {}
  
}
  
export function spec(mark: Mark, data: string, encode: Encoding){
  return new Spec(mark, data, encode);
}

class Mark {
  constructor(private type: "bar" | "area" | "line" | { type: "bar" | "area" | "line"}) {}

}
  
export function mark(type: "bar" | "area" | "line" | { type: "bar" | "area" | "line"}){
  return new Mark(type);
}

class Data {
  constructor(private type: string) {}

}

export function data(type: string) {
  return new Data(type);
}

class X {
  constructor(private field?: string, private type?: "quantitative" | "ordinal") {}
    
}
  
export function x(field?: string, type?: "quantitative" | "ordinal"){
  return new X(field, type);
}

class Y {
  constructor(private field?: string, private type?: "quantitative" | "ordinal") {}
  
}
  
export function y(field?: string, type?: "quantitative" | "ordinal"){
  return new Y(field, type);
}

// TODO: optimize the color argument
class Color {
  constructor(private field?: string, private type?: "quantitative" | "ordinal", private value?: string) {}
  
}
  
export function color(field?: string, type?: "quantitative" | "ordinal", value?: string){
  return new Color(field, type, value);
}

class Encoding {
  constructor(private x?: X, private y?: Y, private color?: Color) {}
  
}
  
export function encode(x?: X, y?: Y, color?: Color){
  return new Encoding(x, y, color);
}

export function toSpec(obj: any){
  return obj;
}

export function toJSON(obj: any){
  return JSON.stringify(obj);
}
@lan-lyu
Copy link
Collaborator Author

lan-lyu commented Jun 7, 2023

Should the tree be simple

spec ___ mark
         |___ data
         |___ encode ____ x
                     |____ y
                     |____ color

or having reference types as leaves, and creating methods for these reference types?

spec ___ mark
         |___ data
         |___ encode ____ x ____ position
                     |____ y ____ position
                     |____ color ____ field
                                 |____ value

If using the latter one, the final output will be like this

class Color extends BaseObject{
  // constructor(private field?: string, private type?: "quantitative" | "ordinal", private value?: string) {}
  constructor(...args) {
    super();
    init(this);
    assign(this, ...args);
  }

  field(field: {field:string, type:"quantitative" | "ordinal"} | string ){
    if (arguments.length) {
      const obj = copy(this);
      set(obj, "color", field);
      return obj;
    } else {
      return get(this, "color");
    }
  }
}

@lan-lyu lan-lyu added the update label Jun 14, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant