Last active
November 20, 2018 07:16
-
-
Save jdanyow/c2394c5748d27f3dc77acabc7b8f2752 to your computer and use it in GitHub Desktop.
AST Visualizer
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<template> | |
<require from="./expression"></require> | |
<button class="button-outline button-small" | |
repeat.for="example of examples" | |
click.delegate="expressionString = example.expression"> | |
${example.name} | |
</button> | |
<label class="expression-input"> | |
Binding Expression: | |
<input type="text" value.bind="expressionString" placeholder="Enter binding expression..."> | |
</label> | |
<expression if.bind="value" value.bind="value"></expression> | |
${error} | |
</template> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { | |
inject, | |
observable, | |
Parser, | |
Chain | |
} | |
from 'aurelia-framework'; | |
@inject(Parser) | |
export class App { | |
@observable expressionString; | |
value = null; | |
examples = [ | |
{ name: 'Basic Property', expression: `firstName` }, | |
{ name: 'Property Path', expression: `person.firstName` }, | |
{ name: 'Conditional', expression: `isActive ? 'active' : ''` }, | |
{ name: 'Array Index', expression: `myArray[index]` }, | |
{ name: 'Binary', expression: `x * y` }, | |
{ name: 'Object Literal', expression: `{ x: 3, y: height, z: depth }` }, | |
{ name: 'Literal Array', expression: `[a, 1, 'hello', null, undefined]` }, | |
{ name: 'Call Method', expression: `save(entity)` }, | |
{ name: 'Assignment', expression: `width = rangeInput.value` }, | |
{ name: 'Value Converter', expression: `startDate | dateFormat:'MM/dd/yyyy'` }, | |
{ name: 'Binding Behavior', expression: `lastName & updateTrigger:'blur'` }, | |
{ name: 'Kitchen Sink', expression: `getPosts({ start: minDate, end: maxDate })[0].timestamp | timeAgo & signal:'tick'` }, | |
]; | |
constructor(parser) { | |
this.parser = parser; | |
this.expressionString = ``; | |
} | |
expressionStringChanged(newValue, oldValue) { | |
this.error = ''; | |
this.expression = null; | |
try { | |
let value = { role: 'Root', expression: this.parser.parse(newValue) }; | |
if (value.expression instanceof Chain) { | |
value = null; | |
} | |
this.value = value; | |
} | |
catch(e) { | |
this.value = null; | |
this.error = e.toString(); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
ul.expression-tree { | |
padding: 0; | |
margin: 0; | |
list-style-type: none; | |
position: relative; | |
font-family: sans-serif; | |
font-size: 100%; | |
} | |
ul.expression-tree li { | |
list-style-type: none; | |
border-left: 2px solid #808080; | |
margin-left: 1em; | |
margin-bottom: 0; | |
} | |
ul.expression-tree li div { | |
padding-left: 1em; | |
position: relative; | |
} | |
ul.expression-tree li div::before { | |
content: ''; | |
position: absolute; | |
top: 0; | |
left: -2px; | |
bottom: 50%; | |
width: 0.75em; | |
border: 2px solid #808080; | |
border-top: 0 none transparent; | |
border-right: 0 none transparent; | |
} | |
ul.expression-tree > li:last-child { | |
border-left: 2px solid transparent; | |
} | |
.expression-type { | |
color: blue; | |
font-weight: bold; | |
} | |
.expression-role { | |
color: green; | |
} | |
.expression-text { | |
font-family: monospace; | |
color: #808080; | |
background-color: transparent; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<template> | |
<require from="./expression.css"></require> | |
<ul class="expression-tree"> | |
<div> | |
<span class="expression-type">${value.expression.constructor.name}</span> | |
<span class="expression-role">[${value.role}]</span> | |
<code class="expression-text">${value.expression}</code> | |
</div> | |
<li repeat.for="child of children"> | |
<expression value.bind="child"></expression> | |
</li> | |
</ul> | |
</template> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { | |
AccessThis, | |
AccessScope, | |
AccessMember, | |
AccessKeyed, | |
Assign, | |
Binary, | |
BindingBehavior, | |
CallFunction, | |
CallMember, | |
CallScope, | |
Conditional, | |
LiteralPrimitive, | |
LiteralArray, | |
LiteralObject, | |
LiteralString, | |
PrefixNot, | |
ValueConverter, | |
bindable, | |
containerless, | |
} | |
from 'aurelia-framework'; | |
@containerless | |
export class Expression { | |
@bindable value; | |
children = null; | |
valueChanged({ role, expression }) { | |
if (expression instanceof AccessThis) { | |
this.children = null; | |
} else if (expression instanceof AccessScope) { | |
this.children = null; | |
} else if (expression instanceof AccessMember) { | |
this.children = [{ role: 'Object', expression: expression.object }]; | |
} else if (expression instanceof AccessKeyed) { | |
this.children = [ | |
{ role: 'Object', expression: expression.object }, | |
{ role: 'Key', expression: expression.key } | |
]; | |
} else if (expression instanceof Assign) { | |
this.children = [ | |
{ role: 'Target', expression: expression.target }, | |
{ role: 'Value', expression: expression.value } | |
]; | |
} else if (expression instanceof Binary) { | |
this.children = [ | |
{ role: 'Left', expression: expression.left }, | |
{ role: 'Right', expression: expression.right } | |
]; | |
} else if (expression instanceof BindingBehavior) { | |
this.children = [ | |
{ role: 'Target', expression: expression.expression }, | |
...expression.args.map(x => ({ role: 'Argument', expression: x })) | |
]; | |
} else if (expression instanceof CallFunction) { | |
this.children = [ | |
{ role: 'Function', expression: expression.func }, | |
...expression.args.map(x => ({ role: 'Argument', expression: x })) | |
]; | |
} else if (expression instanceof CallMember) { | |
this.children = [ | |
{ role: 'Object', expression: expression.object }, | |
...expression.args.map(x => ({ role: 'Argument', expression: x })) | |
]; | |
} else if (expression instanceof CallScope) { | |
this.children = expression.args.map(x => ({ role: 'Argument', expression: x })); | |
} else if (expression instanceof Conditional) { | |
this.children = [ | |
{ role: 'Condition', expression: expression.condition }, | |
{ role: 'True-Value', expression: expression.yes }, | |
{ role: 'False-Value', expression: expression.no } | |
]; | |
} else if (expression instanceof LiteralPrimitive || expression instanceof LiteralString) { | |
this.children = null; | |
} else if (expression instanceof LiteralArray) { | |
this.children = expression.elements.map(x => ({ role: 'Element', expression: x })); | |
} else if (expression instanceof LiteralObject) { | |
this.children = expression.values.map(x => ({ role: 'Property Value', expression: x })); | |
} else if (expression instanceof PrefixNot) { | |
this.children = [{ role: 'Target', expression: expression.expression }]; | |
} else if (expression instanceof ValueConverter) { | |
this.children = [ | |
{ role: 'Target', expression: expression.allArgs[0] }, | |
...expression.args.map(x => ({ role: 'Argument', expression: x })) | |
]; | |
} else { | |
this.children = null; | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!doctype html> | |
<html> | |
<head> | |
<title>Aurelia</title> | |
<meta name="viewport" content="width=device-width, initial-scale=1"> | |
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,300italic,700,700italic"> | |
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/normalize/3.0.3/normalize.css"> | |
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/milligram/1.1.0/milligram.min.css"> | |
<style> | |
body { | |
padding-left: 20px; | |
padding-right: 20px; | |
min-height: 300px; | |
} | |
.button-small { | |
font-size: .8rem; | |
height: 2rem; | |
line-height: 2rem; | |
padding: 0 .5rem; | |
margin-bottom: 0px; | |
} | |
.expression-input { | |
display: block; | |
margin-top: 10px; | |
width: 100%; | |
margin-bottom: 5px; | |
} | |
</style> | |
</head> | |
<body aurelia-app> | |
<h1>Loading...</h1> | |
<script src="https://jdanyow.github.io/rjs-bundle/node_modules/requirejs/require.js"></script> | |
<script src="https://jdanyow.github.io/rjs-bundle/config.js"></script> | |
<script src="https://jdanyow.github.io/rjs-bundle/bundles/aurelia.js"></script> | |
<script src="https://jdanyow.github.io/rjs-bundle/bundles/babel.js"></script> | |
<script> | |
require(['aurelia-bootstrapper']); | |
</script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment