Skip to content
This repository has been archived by the owner on May 9, 2018. It is now read-only.

Create classes for syntax nodes, variables, assignment #1

Merged
merged 8 commits into from
Oct 23, 2017
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions src/assignment.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import SyntaxNode from './syntaxnode';
import Variable from './variable';
import Reference from './reference';

export default class Assignment implements SyntaxNode {
private lhs: Reference;
private rhs: SyntaxNode;

constructor(lhs: Reference, rhs: SyntaxNode) {
this.lhs = lhs;
this.rhs = rhs;
}

public dependencies(): Set<Variable> {
return this.rhs.dependencies();
}

public source(): string {
return `${this.lhs.source()} = ${this.rhs.source()};`;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you need to reference this explicitly, or is TypeScript smart enough to figure out you're referring to the particular instance in question, given that it's an instance method for the class?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You need this explicitly

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sad js reaccs only

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

😢

}
}
10 changes: 5 additions & 5 deletions src/index.ts → src/calder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@
* THE SOFTWARE.
*/

import { Variable } from './variable';

export namespace Calder {
export class Variable { }
}
export * from './variable';
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any reason for removing the namespace?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adding stuff to namespaces is nice but it makes it kind of weird importing from another file, because of your class adds to a namespace, the file is no longer a module you can import

export * from './function';
export * from './reference';
export * from './shader';
export * from './qualifier';
27 changes: 27 additions & 0 deletions src/function.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import Variable from './variable';
import SyntaxNode from './syntaxnode';

export default class Function implements SyntaxNode {
public readonly name: string;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ahhh TypeScript has C#'s readonly 😍

private statements: SyntaxNode[];
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you change this type to be a Statement[]? You'd need to create a new SyntaxNode type, but it would avoid being able to do wacky stuff like having just a list of References. If you feel that this might be better addressed in a future PR, can you add a TODO?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense. I could make two subtypes Statement and Expression that for now are the exact same as the super for now maybe?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

actually I realize the interfaces aren't exactly the same, Expression has a return type. Statement is a class now that just adds a semicolon to a SyntaxNode. I took the semicolon out of the Assignment class because I realize you can have those in the middle of statements too.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually Statement probably shouldn't JUST be a semicolon because ifs and whiles also are statements. Going to make that a separate issue for now though

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.


constructor(name: string, statements: SyntaxNode[] = []) {
this.name = name;
this.statements = statements;
}

public dependencies(): Set<Variable> {
return this.statements.reduce(
(union, statement) => new Set([...union, ...statement.dependencies()]),
new Set());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A bit of a style nitpick here, but could you write the code this way instead?

        return this.statements.reduce((union, statement) =>
            new Set([...union, ...statement.dependencies()]),
            new Set()
        );

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the reasoning? imo it looks a bit weird visually pairing the return value of the lambda with the next parameter in the function

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like putting the lambda parameters on the same line as the function call reduce. Just seems like a convention for JavaScript arrow functions: 1, 2, 3.

}

public source(): string {
// TODO: allow more than void()
return `void ${this.name}() {\n` +
this.statements
.map(statement => statement.source())
.join('\n') +
'\n}';
}
}
11 changes: 11 additions & 0 deletions src/qualifier.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
enum Qualifier {
Attribute = 'attribute',
Uniform = 'uniform',
Varying = 'varying',
Const = 'const',
In = 'in',
Out = 'out',
InOut = 'inout'
}

export default Qualifier;
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rip you cant export default enum in TS but you can do this for some reason: microsoft/TypeScript#3320

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sad ts reaccs only

18 changes: 18 additions & 0 deletions src/reference.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import Variable from './variable';
import SyntaxNode from './syntaxnode';

export default class Reference implements SyntaxNode {
private variable: Variable;

constructor(variable: Variable) {
this.variable = variable;
}

public dependencies(): Set<Variable> {
return new Set([this.variable]);
}

public source(): string {
return this.variable.name;
}
}
20 changes: 20 additions & 0 deletions src/shader.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import Variable from './variable';
import Function from './function';

export default class Shader {
public main: Function;

constructor(main: Function = new Function('main')) {
this.main = main;
}

public source(): string {
return `${this.header()}\n${this.main.source()}`;
}

private header(): string {
return [...this.main.dependencies()]
.map(dependency => dependency.declaration())
.join('\n');
}
}
6 changes: 6 additions & 0 deletions src/syntaxnode.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import Variable from './variable';
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So here, you're importing all of the code from in variable.ts, right? This includes the exported enum Qualifier. Should you do import import { Variable } from './variable'; instead?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

update for reference later: we moved out Qualifier so Variable is a default export now


export default interface SyntaxNode {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice interface! 💯

dependencies(): Set<Variable>;
source(): string;
}
39 changes: 15 additions & 24 deletions src/variable.ts
Original file line number Diff line number Diff line change
@@ -1,27 +1,18 @@
/**
* Calder - A WebGL library for writing WebGL shaders
*
* Copyright (c) 2017 Paul Bardea, Abhishek Madan, Andrew McBurney, Dave Pagurek
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
import Qualifier from './qualifier';

export class Variable {
export default class Variable {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will we have a separate type for local variables, or will we just extend this type to handle global and local variables?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we could maybe extend this one, but I haven't thought through too hard to see what differences there would need to be

public readonly name: string;
public readonly qualifier: Qualifier;
public readonly kind: string;

// TODO: typecheck kind
constructor(qualifier: Qualifier, kind: string, name: string) {
this.kind = kind;
this.qualifier = qualifier;
this.name = name;
}

public declaration(): string {
return `${this.qualifier} ${this.kind} ${this.name};`;
}
}
28 changes: 28 additions & 0 deletions test/shader.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { expect } from 'chai';
import * as cgl from './calder';
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just for clarification, is this is how people will interface with our library in the future?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We'll probably want to make some builders later, but for now, yeah


describe('Shader', () => {
describe('source', () => {
it('generates the expected source code', () => {
const glPosition = new cgl.Variable(cgl.Qualifier.Out, 'vec4', 'gl_Position');
const vertexPosition = new cgl.Variable(cgl.Qualifier.Attribute, 'vec4', 'vertexPosition');
const shader = new cgl.Shader(new cgl.Function('main', [
new cgl.Assignment(
new cgl.Reference(glPosition),
new cgl.Reference(vertexPosition))]));
Copy link
Member

@armcburney armcburney Oct 21, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are a lot of braces here - and while I love lisp (emacs is bae 😍), I think this would have improved readability with some of the braces lined up with the indents:

const shader = new cgl.Shader(
  new cgl.Function('main', [
    new cgl.Assignment(
      new cgl.Reference(glPosition),
      new cgl.Reference(vertexPosition)
    )
  ])
);

or even more concisely (although I'm a bigger fan of the first):

const shader = new cgl.Shader(
  new cgl.Function('main', [
    new cgl.Assignment(new cgl.Reference(glPosition), new cgl.Reference(vertexPosition))
  ])
);


const source = shader.source();

// TODO: recognize gl_Position as a default that doesn't need declaring
const expected = `
attribute vec4 vertexPosition;
out vec4 gl_Position;

void main() {
gl_Position = aVertexPosition;
}`;

expect(source).to.equalIgnoreSpaces(expected);
});
})
});
4 changes: 3 additions & 1 deletion tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@
"module": "es6",
"moduleResolution": "node",
"experimentalDecorators": true,
"noImplicitAny": true
"noImplicitAny": true,
"strictNullChecks": true,
"lib": ["es5", "es6", "dom"]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice 👍

},
"exclude": ["node_modules"]
}
Loading