Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
7 changes: 7 additions & 0 deletions .changeset/rare-rivers-open.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"@getlang/parser": patch
"@getlang/ast": patch
"@getlang/get": patch
---

primitive literals
16 changes: 16 additions & 0 deletions packages/ast/src/ast.ts
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,13 @@ type SliceExpr = {
typeInfo: TypeInfo
}

type LiteralExpr = {
kind: 'LiteralExpr'
value: boolean | number
raw: Token
typeInfo: TypeInfo
}

export type Stmt =
| Program
| ExtractStmt
Expand All @@ -166,6 +173,7 @@ export type Expr =
| ObjectLiteralExpr
| SliceExpr
| DrillExpr
| LiteralExpr

export type Node = Stmt | Expr

Expand Down Expand Up @@ -326,6 +334,13 @@ const sliceExpr = (slice: Token): SliceExpr => ({
slice,
})

const literalExpr = (raw: Token): LiteralExpr => ({
kind: 'LiteralExpr',
typeInfo: { type: Type.Value },
raw,
value: JSON.parse(raw.value),
})

const templateExpr = (elements: (Expr | Token)[]): TemplateExpr => ({
kind: 'TemplateExpr',
typeInfo: { type: Type.Value },
Expand Down Expand Up @@ -353,4 +368,5 @@ export const t = {
objectLiteralExpr,
subqueryExpr,
drillExpr,
literalExpr,
}
4 changes: 4 additions & 0 deletions packages/get/src/execute.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,10 @@ export async function execute(
return { data, typeInfo: node.typeInfo }
},

LiteralExpr(node) {
return { data: node.value, typeInfo: node.typeInfo }
},

Program: {
enter() {
scope.extracted = { data: null, typeInfo: { type: Type.Value } }
Expand Down
8 changes: 2 additions & 6 deletions packages/get/src/modules.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,12 +93,8 @@ export class Modules {
macros.push(i)
}
}
const {
program: simplified,
inputs,
calls,
modifiers,
} = desugar(ast, macros)
const simplified = desugar(ast, macros)
const { inputs, calls, modifiers } = analyze(simplified)

const returnTypes: Record<string, TypeInfo> = {}
for (const call of calls) {
Expand Down
25 changes: 16 additions & 9 deletions packages/parser/src/grammar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,13 @@ declare var request_block_body: any;
declare var request_block_body_end: any;
declare var drill_arrow: any;
declare var link: any;
declare var identifier_expr: any;
declare var call: any;
declare var literal: any;
declare var str: any;
declare var interpvar: any;
declare var bool: any;
declare var num: any;
declare var slice: any;
declare var identifier_expr: any;
declare var ws: any;
declare var comment: any;
declare var nl: any;
Expand Down Expand Up @@ -91,7 +93,6 @@ const grammar: Grammar = {
{"name": "request_blocks$ebnf$2", "symbols": [], "postprocess": () => null},
{"name": "request_blocks", "symbols": ["request_blocks$ebnf$1", "request_blocks$ebnf$2"], "postprocess": p.requestBlocks},
{"name": "request_block_named", "symbols": [(lexer.has("request_block_name") ? {type: "request_block_name"} : request_block_name), "line_sep", "request_block"], "postprocess": p.requestBlockNamed},
{"name": "request_block_body", "symbols": [(lexer.has("request_block_body") ? {type: "request_block_body"} : request_block_body), "template", (lexer.has("request_block_body_end") ? {type: "request_block_body_end"} : request_block_body_end)], "postprocess": p.requestBlockBody},
{"name": "request_block$ebnf$1", "symbols": []},
{"name": "request_block$ebnf$1$subexpression$1", "symbols": ["line_sep", "request_entry"]},
{"name": "request_block$ebnf$1", "symbols": ["request_block$ebnf$1", "request_block$ebnf$1$subexpression$1"], "postprocess": (d) => d[0].concat([d[1]])},
Expand All @@ -100,6 +101,7 @@ const grammar: Grammar = {
{"name": "request_entry$ebnf$1", "symbols": ["request_entry$ebnf$1$subexpression$1"], "postprocess": id},
{"name": "request_entry$ebnf$1", "symbols": [], "postprocess": () => null},
{"name": "request_entry", "symbols": ["template", {"literal":":"}, "request_entry$ebnf$1"], "postprocess": p.requestEntry},
{"name": "request_block_body", "symbols": [(lexer.has("request_block_body") ? {type: "request_block_body"} : request_block_body), "template", (lexer.has("request_block_body_end") ? {type: "request_block_body_end"} : request_block_body_end)], "postprocess": p.requestBlockBody},
{"name": "expression", "symbols": ["drill"], "postprocess": id},
{"name": "expression$ebnf$1$subexpression$1", "symbols": ["drill", "_", (lexer.has("drill_arrow") ? {type: "drill_arrow"} : drill_arrow), "_"]},
{"name": "expression$ebnf$1", "symbols": ["expression$ebnf$1$subexpression$1"], "postprocess": id},
Expand All @@ -112,13 +114,14 @@ const grammar: Grammar = {
{"name": "drill$ebnf$2$subexpression$1", "symbols": ["_", (lexer.has("drill_arrow") ? {type: "drill_arrow"} : drill_arrow), "_", "bit"]},
{"name": "drill$ebnf$2", "symbols": ["drill$ebnf$2", "drill$ebnf$2$subexpression$1"], "postprocess": (d) => d[0].concat([d[1]])},
{"name": "drill", "symbols": ["drill$ebnf$1", "bit", "drill$ebnf$2"], "postprocess": p.drill},
{"name": "bit$subexpression$1", "symbols": ["template"]},
{"name": "bit$subexpression$1", "symbols": ["literal"]},
{"name": "bit$subexpression$1", "symbols": ["slice"]},
{"name": "bit$subexpression$1", "symbols": ["call"]},
{"name": "bit$subexpression$1", "symbols": ["object"]},
{"name": "bit$subexpression$1", "symbols": ["subquery"]},
{"name": "bit", "symbols": ["bit$subexpression$1"], "postprocess": p.idd},
{"name": "bit", "symbols": ["id_expr"], "postprocess": p.identifier},
{"name": "bit", "symbols": ["template"], "postprocess": p.selector},
{"name": "bit", "symbols": [(lexer.has("identifier_expr") ? {type: "identifier_expr"} : identifier_expr)], "postprocess": p.idbit},
{"name": "subquery", "symbols": [{"literal":"("}, "_", "statements", "_", {"literal":")"}], "postprocess": p.subquery},
{"name": "call$ebnf$1$subexpression$1", "symbols": [{"literal":"("}, "object", {"literal":")"}]},
{"name": "call$ebnf$1", "symbols": ["call$ebnf$1$subexpression$1"], "postprocess": id},
Expand All @@ -141,22 +144,26 @@ const grammar: Grammar = {
{"name": "object_entry", "symbols": [(lexer.has("identifier") ? {type: "identifier"} : identifier), "object_entry$ebnf$3"], "postprocess": p.objectEntryShorthandSelect},
{"name": "object_entry$ebnf$4", "symbols": [{"literal":"?"}], "postprocess": id},
{"name": "object_entry$ebnf$4", "symbols": [], "postprocess": () => null},
{"name": "object_entry", "symbols": ["id_expr", "object_entry$ebnf$4"], "postprocess": p.objectEntryShorthandIdent},
{"name": "template$ebnf$1$subexpression$1", "symbols": [(lexer.has("literal") ? {type: "literal"} : literal)]},
{"name": "object_entry", "symbols": [(lexer.has("identifier_expr") ? {type: "identifier_expr"} : identifier_expr), "object_entry$ebnf$4"], "postprocess": p.objectEntryShorthandIdent},
{"name": "template$ebnf$1$subexpression$1", "symbols": [(lexer.has("str") ? {type: "str"} : str)]},
{"name": "template$ebnf$1$subexpression$1", "symbols": [(lexer.has("interpvar") ? {type: "interpvar"} : interpvar)]},
{"name": "template$ebnf$1$subexpression$1", "symbols": ["interp_expr"]},
{"name": "template$ebnf$1$subexpression$1", "symbols": ["interp_tmpl"]},
{"name": "template$ebnf$1", "symbols": ["template$ebnf$1$subexpression$1"]},
{"name": "template$ebnf$1$subexpression$2", "symbols": [(lexer.has("literal") ? {type: "literal"} : literal)]},
{"name": "template$ebnf$1$subexpression$2", "symbols": [(lexer.has("str") ? {type: "str"} : str)]},
{"name": "template$ebnf$1$subexpression$2", "symbols": [(lexer.has("interpvar") ? {type: "interpvar"} : interpvar)]},
{"name": "template$ebnf$1$subexpression$2", "symbols": ["interp_expr"]},
{"name": "template$ebnf$1$subexpression$2", "symbols": ["interp_tmpl"]},
{"name": "template$ebnf$1", "symbols": ["template$ebnf$1", "template$ebnf$1$subexpression$2"], "postprocess": (d) => d[0].concat([d[1]])},
{"name": "template", "symbols": ["template$ebnf$1"], "postprocess": p.template},
{"name": "interp_expr", "symbols": [{"literal":"${"}, "_", (lexer.has("identifier") ? {type: "identifier"} : identifier), "_", {"literal":"}"}], "postprocess": p.interpExpr},
{"name": "interp_tmpl", "symbols": [{"literal":"$["}, "_", "template", "_", {"literal":"]"}], "postprocess": p.interpTmpl},
{"name": "literal$subexpression$1", "symbols": [(lexer.has("bool") ? {type: "bool"} : bool)]},
{"name": "literal$subexpression$1", "symbols": [(lexer.has("num") ? {type: "num"} : num)]},
{"name": "literal", "symbols": ["literal$subexpression$1"], "postprocess": p.literal},
{"name": "literal", "symbols": [{"literal":"'"}, "template", {"literal":"'"}], "postprocess": p.string},
{"name": "literal", "symbols": [{"literal":"\""}, "template", {"literal":"\""}], "postprocess": p.string},
{"name": "slice", "symbols": [(lexer.has("slice") ? {type: "slice"} : slice)], "postprocess": p.slice},
{"name": "id_expr", "symbols": [(lexer.has("identifier_expr") ? {type: "identifier_expr"} : identifier_expr)], "postprocess": id},
{"name": "line_sep$ebnf$1", "symbols": []},
{"name": "line_sep$ebnf$1$subexpression$1", "symbols": [(lexer.has("ws") ? {type: "ws"} : ws)]},
{"name": "line_sep$ebnf$1$subexpression$1", "symbols": [(lexer.has("comment") ? {type: "comment"} : comment)]},
Expand Down
17 changes: 11 additions & 6 deletions packages/parser/src/grammar/getlang.ne
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,9 @@ expression -> (drill _ %drill_arrow _):? %link _ drill {% p.link %}
drill -> (%drill_arrow _):? bit (_ %drill_arrow _ bit):* {% p.drill %}

# drill bit
bit -> (template | slice | call | object | subquery) {% p.idd %}
bit -> id_expr {% p.identifier %}
bit -> (literal | slice | call | object | subquery) {% p.idd %}
bit -> template {% p.selector %}
bit -> %identifier_expr {% p.idbit %}

# subqueries
subquery -> "(" _ statements _ ")" {% p.subquery %}
Expand All @@ -49,14 +50,18 @@ call -> %call ("(" object ")"):? {% p.call %}
object -> "{" _ (object_entry (_ ","):? _):* "}" {% p.object %}
object_entry -> "@":? %identifier "?":? ":" _ expression {% p.objectEntry %}
object_entry -> %identifier "?":? {% p.objectEntryShorthandSelect %}
object_entry -> id_expr "?":? {% p.objectEntryShorthandIdent %}
object_entry -> %identifier_expr "?":? {% p.objectEntryShorthandIdent %}

# literals
template -> (%literal | %interpvar | interp_expr | interp_tmpl):+ {% p.template %}
# templates
template -> (%str | %interpvar | interp_expr | interp_tmpl):+ {% p.template %}
interp_expr -> "${" _ %identifier _ "}" {% p.interpExpr %}
interp_tmpl -> "$[" _ template _ "]" {% p.interpTmpl %}

# literals
literal -> (%bool | %num) {% p.literal %}
literal -> "'" template "'" {% p.string %}
literal -> "\"" template "\"" {% p.string %}
slice -> %slice {% p.slice %}
id_expr -> %identifier_expr {% id %}

# whitespace
line_sep -> (%ws | %comment):* %nl _ {% p.ws %}
Expand Down
22 changes: 20 additions & 2 deletions packages/parser/src/grammar/lex/templates.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ export const templateUntil = (

return {
term: {
defaultType: 'literal',
defaultType: 'str',
match: new RegExp(`(?=${term.source})`),
lineBreaks: true,
...(next ? { next } : { pop: 1 }),
Expand All @@ -56,7 +56,7 @@ export const templateUntil = (
),
value: (text: string) => text.slice(1),
},
literal: {
str: {
match: until(new RegExp(`[${interpSymbols.join('')}]|${term.source}`)),
value: (text: string) => text.replace(/\\(.)/g, '$1').replace(/\s/g, ' '),
lineBreaks: true,
Expand Down Expand Up @@ -91,9 +91,27 @@ const interpTmplParams = {
...templateUntil(/]/, { interpParams: true }),
}

const stringS = {
squot: {
match: `'`,
pop: 1,
},
...templateUntil(/'/),
}

const stringD = {
dquot: {
match: '"',
pop: 1,
},
...templateUntil(/"/),
}

export const templateStates = {
template: templateUntil(/\n|->|=>/, { interpTemplate: false }),
interpExpr,
interpTmpl,
interpTmplParams,
stringS,
stringD,
}
40 changes: 37 additions & 3 deletions packages/parser/src/grammar/lexer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ const main = {
},
drill_arrow: {
match: ['->', '=>'],
push: 'expr',
push: 'drillExpr',
},
colon: {
match: ':',
Expand All @@ -48,13 +48,12 @@ const main = {
symbols: /[{}(),?@]/,
}

const expr = {
const exprBase = {
ws: patterns.ws,
nl: {
match: /\n/,
lineBreaks: true,
},
drill_arrow: ['->', '=>'],
link: {
match: patterns.link,
value: (text: string) => text.slice(1, -1),
Expand All @@ -80,6 +79,40 @@ const expr = {
value: (text: string) => text.slice(1),
pop: 1,
},
squot: {
match: `'`,
next: 'stringS',
},
dquot: {
match: `"`,
next: 'stringD',
},
}

const expr = {
...exprBase,
drill_arrow: {
match: ['->', '=>'],
next: 'drillExpr',
},
num: {
match: /\d+(?:\.\d+)?/,
pop: 1,
},
bool: {
match: ['true', 'false'],
pop: 1,
},
template: {
defaultType: 'ws',
match: /(?=.)/,
next: 'template',
},
}

const drillExpr = {
...exprBase,
drill_arrow: ['->', '=>'],
template: {
defaultType: 'ws',
match: /(?=.)/,
Expand All @@ -91,6 +124,7 @@ const lexer: any = moo.states({
$all: { err: moo.error },
main,
expr,
drillExpr,
...templateStates,
...requestStates,
})
Expand Down
29 changes: 14 additions & 15 deletions packages/parser/src/grammar/parse.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,15 +124,12 @@ export const objectEntryShorthandIdent: PP = ([identifier, optional]) => {

function drillBase(bit: Expr, arrow?: string): Expr {
const expand = arrow === '=>'
switch (bit.kind) {
case 'TemplateExpr':
return t.selectorExpr(bit, expand)
case 'IdentifierExpr':
return t.drillIdentifierExpr(bit.id, expand)
default:
invariant(!expand, new QuerySyntaxError('Misplaced wide arrow drill'))
return bit
if (bit.kind === 'SelectorExpr' || bit.kind === 'DrillIdentifierExpr') {
bit.expand = expand
} else if (expand) {
throw new QuerySyntaxError('Misplaced wide arrow drill')
}
return bit
}

export const drill: PP = ([arrow, bit, bits]) => {
Expand All @@ -143,9 +140,8 @@ export const drill: PP = ([arrow, bit, bits]) => {
return t.drillExpr([expr, ...exprs])
}

export const identifier: PP = ([id]) => {
return t.identifierExpr(id)
}
export const selector: PP = ([template]) => t.selectorExpr(template, false)
export const idbit: PP = ([id]) => t.drillIdentifierExpr(id, false)

export const template: PP = d => {
const elements = d[0].reduce((els: any, dd: any) => {
Expand All @@ -158,10 +154,10 @@ export const template: PP = d => {
els.push(el)
} else if (el.type === 'interpvar' || el.type === 'identifier') {
els.push(t.identifierExpr(el))
} else if (el.type === 'literal') {
} else if (el.type === 'str') {
if (el.value) {
const prev = els.at(-1)
if (prev?.type === 'literal') {
if (prev?.type === 'str') {
els.pop()
els.push({ ...prev, value: prev.value + el.value })
} else {
Expand All @@ -176,19 +172,22 @@ export const template: PP = d => {
}, [])

const first = elements.at(0)
if (first.type === 'literal') {
if (first.type === 'str') {
elements[0] = { ...first, value: first.value.trimLeft() }
}

const lastIdx = elements.length - 1
const last = elements[lastIdx]
if (last.type === 'literal') {
if (last.type === 'str') {
elements[lastIdx] = { ...last, value: last.value.trimRight() }
}

return t.templateExpr(elements)
}

export const literal: PP = ([[token]]) => t.literalExpr(token)
export const string: PP = ([, template]) => template

export const interpExpr: PP = ([, , token]) => token
export const interpTmpl: PP = ([, , template]) => template

Expand Down
Loading