Skip to content
This repository has been archived by the owner on Jun 20, 2024. It is now read-only.

Commit

Permalink
Add support for Liquid literals
Browse files Browse the repository at this point in the history
true, false, empty, blank, nil, null
  • Loading branch information
charlespwd committed Aug 2, 2022
1 parent 96f2b11 commit b591d8c
Show file tree
Hide file tree
Showing 11 changed files with 134 additions and 7 deletions.
13 changes: 9 additions & 4 deletions grammar/liquid-html.ohm
Original file line number Diff line number Diff line change
Expand Up @@ -104,12 +104,9 @@ LiquidHTML {

// TODO
liquidExpression =
| liquidLiteral

// TODO
liquidLiteral =
| liquidString
| liquidNumber
| liquidLiteral

liquidString = liquidSingleQuotedString | liquidDoubleQuotedString
liquidSingleQuotedString = "'" anyExceptStar<("'"| "%}" | "}}")> "'"
Expand All @@ -122,6 +119,14 @@ LiquidHTML {
// coming from shopify/liquid...
liquidFloat = "-"? digit (digit | ".")+

liquidLiteral =
| "true"
| "false"
| "blank"
| "empty"
| "nil"
| "null"

// https://www.w3.org/TR/2011/WD-html-markup-20110113/syntax.html#void-element
// Cheating a bit with by stretching it to the doctype
voidElementName =
Expand Down
22 changes: 22 additions & 0 deletions src/parser/ast.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,28 @@ describe('Unit: toLiquidHtmlAST', () => {
expectPosition(ast, 'children.0.markup.expression');
});
});

it('should parse numbers as LiquidVariable > LiquidLiteral', () => {
[
{ expression: `nil`, value: null },
{ expression: `null`, value: null },
{ expression: `true`, value: true },
{ expression: `blank`, value: '' },
{ expression: `empty`, value: '' },
].forEach(({ expression, value }) => {
ast = toLiquidHtmlAST(`{{ ${expression} }}`);
expectPath(ast, 'children.0').to.exist;
expectPath(ast, 'children.0.type').to.eql('LiquidDrop');
expectPath(ast, 'children.0.markup.type').to.eql('LiquidVariable');
expectPath(ast, 'children.0.markup.rawSource').to.eql(expression);
expectPath(ast, 'children.0.markup.expression.type').to.eql('LiquidLiteral');
expectPath(ast, 'children.0.markup.expression.keyword').to.eql(expression);
expectPath(ast, 'children.0.markup.expression.value').to.eql(value);
expectPosition(ast, 'children.0');
expectPosition(ast, 'children.0.markup');
expectPosition(ast, 'children.0.markup.expression');
});
});
});

it('should transform a basic Liquid Tag into a LiquidTag', () => {
Expand Down
17 changes: 16 additions & 1 deletion src/parser/ast.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
ConcreteAttrDoubleQuoted,
ConcreteAttrUnquoted,
ConcreteLiquidVariable,
ConcreteLiquidLiteral,
ConcreteLiquidFilters,
ConcreteLiquidExpression,
} from '~/parser/cst';
Expand Down Expand Up @@ -130,7 +131,7 @@ interface LiquidVariable extends ASTNode<NodeTypes.LiquidVariable> {
}

// TODO
type LiquidExpression = LiquidString | LiquidNumber;
type LiquidExpression = LiquidString | LiquidNumber | LiquidLiteral;

// TODO
type LiquidFilter = undefined;
Expand All @@ -144,6 +145,11 @@ interface LiquidNumber extends ASTNode<NodeTypes.Number> {
value: string;
}

interface LiquidLiteral extends ASTNode<NodeTypes.LiquidLiteral> {
keyword: ConcreteLiquidLiteral['keyword'];
value: ConcreteLiquidLiteral['value'];
}

export type HtmlNode =
| HtmlComment
| HtmlElement
Expand Down Expand Up @@ -647,6 +653,15 @@ function toExpression(
source,
};
}
case ConcreteNodeTypes.LiquidLiteral: {
return {
type: NodeTypes.LiquidLiteral,
position: position(node),
value: node.value,
keyword: node.keyword,
source,
};
}
default: {
return assertNever(node);
}
Expand Down
23 changes: 23 additions & 0 deletions src/parser/cst.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,29 @@ describe('Unit: toLiquidHtmlCST(text)', () => {
expectLocation(cst, '0.markup.expression');
});
});

it('should parse Liquid literals', () => {
[
{ expression: `nil`, value: null },
{ expression: `null`, value: null },
{ expression: `true`, value: true },
{ expression: `blank`, value: '' },
{ expression: `empty`, value: '' },
].forEach(({ expression, value }) => {
cst = toLiquidHtmlCST(`{{ ${expression} }}`);
expectPath(cst, '0.type').to.equal('LiquidDrop');
expectPath(cst, '0.markup.type').to.equal('LiquidVariable', expression);
expectPath(cst, '0.markup.rawSource').to.equal(expression);
expectPath(cst, '0.markup.expression.type').to.equal('LiquidLiteral');
expectPath(cst, '0.markup.expression.keyword').to.equal(expression);
expectPath(cst, '0.markup.expression.value').to.equal(value);
expectPath(cst, '0.whitespaceStart').to.equal(null);
expectPath(cst, '0.whitespaceEnd').to.equal(null);
expectLocation(cst, '0');
expectLocation(cst, '0.markup');
expectLocation(cst, '0.markup.expression');
});
});
});

describe('Case: LiquidNode', () => {
Expand Down
31 changes: 29 additions & 2 deletions src/parser/cst.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,20 @@ export enum ConcreteNodeTypes {
TextNode = 'TextNode',

LiquidVariable = 'LiquidVariable',
LiquidLiteral = 'LiquidLiteral',
String = 'String',
Number = 'Number',
}

export const LiquidLiteralValues = {
nil: null,
null: null,
true: true as true,
false: false as false,
blank: '' as '',
empty: '' as '',
};

export interface Parsers {
[astFormat: string]: Parser;
}
Expand Down Expand Up @@ -149,7 +159,8 @@ export type ConcreteLiquidFilters = undefined; // TODO
// TODO
export type ConcreteLiquidExpression =
| ConcreteStringLiteral
| ConcreteNumberLiteral;
| ConcreteNumberLiteral
| ConcreteLiquidLiteral;

export interface ConcreteStringLiteral
extends ConcreteBasicNode<ConcreteNodeTypes.String> {
Expand All @@ -162,6 +173,12 @@ export interface ConcreteNumberLiteral
value: string; // float parsing is weird but supported
}

export interface ConcreteLiquidLiteral
extends ConcreteBasicNode<ConcreteNodeTypes.LiquidLiteral> {
keyword: keyof typeof LiquidLiteralValues;
value: typeof LiquidLiteralValues[keyof typeof LiquidLiteralValues];
}

export type ConcreteHtmlNode =
| ConcreteHtmlComment
| ConcreteHtmlRawTag
Expand Down Expand Up @@ -342,7 +359,17 @@ export function toLiquidHtmlCST(text: string): LiquidHtmlCST {

liquidDropCases: 0,
liquidExpression: 0,
liquidLiteral: 0,
liquidLiteral: {
type: ConcreteNodeTypes.LiquidLiteral,
value: (tokens: Node[]) => {
const keyword = tokens[0]
.sourceString as keyof typeof LiquidLiteralValues;
return LiquidLiteralValues[keyword];
},
keyword: 0,
locStart,
locEnd,
},
liquidDropBaseCase: (sw: Node) => sw.sourceString.trimEnd(),
liquidVariable: {
type: ConcreteNodeTypes.LiquidVariable,
Expand Down
2 changes: 2 additions & 0 deletions src/printer/preprocess/augment-with-css-properties.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ function getCssDisplay(
return 'block';

case NodeTypes.LiquidVariable:
case NodeTypes.LiquidLiteral:
case NodeTypes.String:
case NodeTypes.Number:
return 'should not be relevant';
Expand Down Expand Up @@ -128,6 +129,7 @@ function getNodeCssStyleWhiteSpace(node: AugmentedNode<WithSiblings>): string {
return CSS_WHITE_SPACE_DEFAULT;

case NodeTypes.LiquidVariable:
case NodeTypes.LiquidLiteral:
case NodeTypes.String:
case NodeTypes.Number:
return 'should not be relevant';
Expand Down
8 changes: 8 additions & 0 deletions src/printer/printer-liquid-html.ts
Original file line number Diff line number Diff line change
Expand Up @@ -390,6 +390,14 @@ function printNode(
return node.value;
}

case NodeTypes.LiquidLiteral: {
// We prefer nil over null.
if (node.keyword === 'null') {
return 'nil';
}
return node.keyword;
}

case NodeTypes.LiquidVariable: {
// TODO this is where you'll do the pipe first/last logic.
return [path.call(print, 'expression')];
Expand Down
1 change: 1 addition & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export enum NodeTypes {
TextNode = 'TextNode',

LiquidVariable = 'LiquidVariable',
LiquidLiteral = 'LiquidLiteral',
String = 'String',
Number = 'Number',
// Range = 'Range',
Expand Down
9 changes: 9 additions & 0 deletions test/liquid-drop-liquid-literal/fixed.liquid
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
It should print literals
{{ true }}
{{ false }}
{{ empty }}
{{ blank }}

It should prefer nil over null
{{ nil }}
{{ nil }}
9 changes: 9 additions & 0 deletions test/liquid-drop-liquid-literal/index.liquid
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
It should print literals
{{ true }}
{{ false }}
{{ empty }}
{{ blank }}

It should prefer nil over null
{{ nil }}
{{ null }}
6 changes: 6 additions & 0 deletions test/liquid-drop-liquid-literal/index.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { assertFormattedEqualsFixed } from '../test-helpers';
import * as path from 'path';

describe(`Unit: ${path.basename(__dirname)}`, () => {
assertFormattedEqualsFixed(__dirname);
});

0 comments on commit b591d8c

Please sign in to comment.