Skip to content

Commit

Permalink
Add curly bracket nesting
Browse files Browse the repository at this point in the history
Fixes #2
  • Loading branch information
asnewman committed Dec 26, 2024
1 parent 179c842 commit 3016ea7
Show file tree
Hide file tree
Showing 5 changed files with 288 additions and 44 deletions.
30 changes: 28 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ The most basic application will look like this:
<head>
<meta charset="UTF-8" />
<title>Example</title>
<script src="https://cdn.jsdelivr.net/gh/asnewman/ashjs@v0.0.5/index.js"></script>
<script src="https://cdn.jsdelivr.net/gh/asnewman/ashjs@v0.0.6/index.js"></script>
</head>
<body class="">
<div id="ashjs"></div>
Expand Down Expand Up @@ -103,11 +103,37 @@ The simplest way to re-render your UI is by calling the `render()` function. Thi
### Passing Events Data

Pass data to event handlers like so:

```
-div()
--"Current count: ${store.count}"
--button(onclick='increaseCountBy("1")')
```
```

### Nested Generator Function

Often, it makes sense to use a helper function to build portions of UI that is reused in multiple places. For example:

```
function footer() {
return `
-div(id="footer")
--"Please contact me"
`
}
```

To make sure the UI gets rendered at the approriate HTML level, use `{}` like so:

```
-div("root")
// some content
{ ${footer()} }
```

Here, even though `footer()` generates UI code at the same `-` level as `-div("root")`, the engine will properly nest the footer inside "root".

For more details, please see https://github.com/asnewman/ashjs/issues/2.

## Development

Expand Down
2 changes: 1 addition & 1 deletion example.html
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
const store = { count: 0 };
const events = {
increaseCountBy: (rawData, render) => {
const data = JSON.parse(rawData)
const data = JSON.parse(rawData);
const byValue = parseInt(data);
if (isNaN(byValue)) {
return;
Expand Down
59 changes: 39 additions & 20 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@
"dialog",
"script",
"template",
"slot"
"slot",
]);
var Tokenizer = class {
cursor = 0;
Expand Down Expand Up @@ -184,7 +184,10 @@
this.cursor++;
continue;
}
if (this.markup[this.cursor] === '"' || this.markup[this.cursor] === "'") {
if (
this.markup[this.cursor] === '"' ||
this.markup[this.cursor] === "'"
) {
this.tokenizeQuote();
continue;
}
Expand All @@ -209,15 +212,26 @@
const startingQuoteSymbol = this.markup[this.cursor];
this.cursor++;
const strArr = [];
while (this.markup[this.cursor] !== startingQuoteSymbol && this.cursor < this.markup.length) {
while (
this.markup[this.cursor] !== startingQuoteSymbol &&
this.cursor < this.markup.length
) {
strArr.push(this.markup[this.cursor]);
this.cursor++;
}
this.result.push({ type: 5 /* STRING */, value: strArr.join("") });
this.cursor++;
}
isNotSymbol() {
return this.markup[this.cursor] !== " " && this.markup[this.cursor] !== "-" && this.markup[this.cursor] !== "=" && this.markup[this.cursor] !== "(" && this.markup[this.cursor] !== ")" && this.markup[this.cursor] !== "\n" && this.cursor < this.markup.length;
return (
this.markup[this.cursor] !== " " &&
this.markup[this.cursor] !== "-" &&
this.markup[this.cursor] !== "=" &&
this.markup[this.cursor] !== "(" &&
this.markup[this.cursor] !== ")" &&
this.markup[this.cursor] !== "\n" &&
this.cursor < this.markup.length
);
}
};
var Parser = class {
Expand All @@ -230,7 +244,7 @@
currLevels = {};
constructor(tokens) {
const tokensNoSpaces = tokens.filter(
(t) => t.type !== 8 /* SPACE */ && t.type !== 6 /* NEW_LINE */
(t) => t.type !== 8 /* SPACE */ && t.type !== 6 /* NEW_LINE */,
);
this.tokens = tokensNoSpaces;
}
Expand All @@ -257,9 +271,11 @@
if (this.tokens[this.cursor].type === 5 /* STRING */) {
const newStringExpression = {
type: 3 /* STRING_LITERAL */,
body: this.tokens[this.cursor].value
body: this.tokens[this.cursor].value,
};
this.currLevels[this.currDashLevel - 1].body.push(newStringExpression);
this.currLevels[this.currDashLevel - 1].body.push(
newStringExpression,
);
this.cursor++;
}
}
Expand All @@ -270,12 +286,15 @@
type: 2 /* TAG */,
tagName: this.tokens[this.cursor].value,
attributes: {},
body: []
body: [],
};
this.cursor++;
if (this.tokens[this.cursor].type === 3 /* L_PAREN */) {
this.cursor++;
while (this.tokens[this.cursor].type !== 4 /* R_PAREN */ && this.cursor < this.tokens.length) {
while (
this.tokens[this.cursor].type !== 4 /* R_PAREN */ &&
this.cursor < this.tokens.length
) {
if (this.tokens[this.cursor].type !== 7 /* WORD */) {
throw new Error("Expected attribute for tag");
}
Expand All @@ -300,20 +319,22 @@
return {
type: 4 /* EVENT_FUNCTION */,
name: parts[0],
arg: parts[1].replace(")", "")
arg: parts[1].replace(")", ""),
};
} else {
return token.value;
}
}
throw new Error("On event function must be a string. Instead found " + JSON.stringify(token));
throw new Error(
"On event function must be a string. Instead found " +
JSON.stringify(token),
);
}
};
var Transformer = class {
ast = { type: 0 /* ROOT */, body: [] };
cursor = 0;
emit = (e, d) => {
};
emit = (e, d) => {};
constructor(ast, emit) {
this.ast = ast;
this.emit = emit;
Expand All @@ -330,16 +351,14 @@
transformTag(tagExpression) {
if (tagExpression.type !== 2 /* TAG */) {
throw new Error(
"Expected tag expression, instead received: " + tagExpression.type
"Expected tag expression, instead received: " + tagExpression.type,
);
}
const jsonTag = {
[tagExpression.tagName]: tagExpression.body.map((element) => {
if (element.type === 2 /* TAG */)
return this.transformTag(element);
if (element.type === 3 /* STRING_LITERAL */)
return element.body;
})
if (element.type === 2 /* TAG */) return this.transformTag(element);
if (element.type === 3 /* STRING_LITERAL */) return element.body;
}),
};
const onEventAttributes = [];
for (const [key, value] of Object.entries(tagExpression.attributes)) {
Expand Down Expand Up @@ -371,7 +390,7 @@
go: (path) => {
const removedSlash = path.startsWith("/") ? path.substring(1) : path;
window.location.hash = `#${removedSlash}`;
}
},
};
this.render();
window.onhashchange = () => {
Expand Down
60 changes: 60 additions & 0 deletions parseMarkup.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -476,3 +476,63 @@ Deno.test("String into event", () => {
assertEquals(result[0].div[0].button.length, 1);
assertEquals(result[0].div[0].button[0], "Foo");
});

Deno.test("Nesting marker", () => {
function genButton() {
return `
-button()
--"I am a button"
`;
}

function genButtonWrapper() {
return `
-div(id="wrapper")
{${genButton()}}
`;
}

const markup = `
-div(id="root")
{${genButtonWrapper()}}
`;

const tokenizer = new Tokenizer(markup);
const tokens = tokenizer.tokenize();
const parser = new Parser(tokens);
const ast = parser.parse();
assertEquals(ast, {
type: ExpressionTypes.ROOT,
body: [
{
type: ExpressionTypes.TAG,
attributes: {
id: "root",
},
tagName: "div",
body: [
{
type: ExpressionTypes.TAG,
tagName: "div",
attributes: {
id: "wrapper",
},
body: [
{
type: ExpressionTypes.TAG,
tagName: "button",
attributes: {},
body: [
{
type: ExpressionTypes.STRING_LITERAL,
body: "I am a button",
},
],
},
],
},
],
},
],
});
});
Loading

0 comments on commit 3016ea7

Please sign in to comment.