Merge parser and expressions into repository

This commit is contained in:
Christopher Schleiden
2023-01-06 15:54:31 -08:00
parent 4f961a6247
commit 70e33f999d
328 changed files with 35337 additions and 0 deletions
@@ -0,0 +1,211 @@
import { Evaluator, Lexer, Parser } from "@github/actions-expressions"
import { Expr } from "@github/actions-expressions/ast"
import {
Dictionary,
ExpressionData,
Kind,
} from "@github/actions-expressions/data/index"
import { FunctionInfo } from "@github/actions-expressions/funcs/info"
import { TemplateContext } from "./template-context"
import {
BasicExpressionToken,
BooleanToken,
LiteralToken,
MappingToken,
NullToken,
NumberToken,
SequenceToken,
StringToken,
TemplateToken,
} from "./tokens"
import { TokenType } from "./tokens/types"
export function evaluateStringToken(
token: BasicExpressionToken,
context: TemplateContext
): TemplateToken {
const expr = parseExpression(
token.expression,
context.expressionNamedContexts,
context.expressionFunctions
)
if (!expr) {
throw new Error("Unexpected empty expression")
}
// TODO: Pass in context
const evaluator = new Evaluator(expr, new Dictionary())
const result = evaluator.evaluate()
if (!result.primitive) {
context.error(token, "Expected a string")
return new StringToken(
token.file,
token.range,
token.expression,
token.definitionInfo
)
}
return new StringToken(
token.file,
token.range,
result.coerceString(),
token.definitionInfo
)
}
export function evaluateSequenceToken(
token: BasicExpressionToken,
context: TemplateContext
): TemplateToken {
const expr = parseExpression(
token.expression,
context.expressionNamedContexts,
context.expressionFunctions
)
if (!expr) {
throw new Error("Unexpected empty expression")
}
// TODO: Pass in context
const evaluator = new Evaluator(expr, new Dictionary())
const result = evaluator.evaluate()
const value = convertToTemplateToken(token, result)
if (value.templateTokenType !== TokenType.Sequence) {
context.error(token, "Expected a sequence")
return new SequenceToken(token.file, token.range, token.definitionInfo)
}
return value
}
export function evaluateMappingToken(
token: BasicExpressionToken,
context: TemplateContext
): MappingToken {
const expr = parseExpression(
token.expression,
context.expressionNamedContexts,
context.expressionFunctions
)
if (!expr) {
throw new Error("Unexpected empty expression")
}
// TODO: Pass in context
const evaluator = new Evaluator(expr, new Dictionary())
const result = evaluator.evaluate()
const value = convertToTemplateToken(token, result)
if (value.templateTokenType !== TokenType.Mapping) {
context.error(token, "Expected a mapping")
return new MappingToken(token.file, token.range, token.definitionInfo)
}
return value as MappingToken
}
export function evaluateTemplateToken(
token: BasicExpressionToken,
context: TemplateContext
): TemplateToken {
const expr = parseExpression(
token.expression,
context.expressionNamedContexts,
context.expressionFunctions
)
if (!expr) {
throw new Error("Unexpected empty expression")
}
// TODO: Pass in context
const evaluator = new Evaluator(expr, new Dictionary())
const result = evaluator.evaluate()
return convertToTemplateToken(token, result)
}
function convertToTemplateToken(
token: BasicExpressionToken,
result: ExpressionData
): TemplateToken {
// Literal
const literal = convertToLiteralToken(token, result)
if (literal) {
return literal
}
// TODO: Support expressions that return a sequence or mapping token
// Leverage the expression SDK to traverse the object
switch (result.kind) {
case Kind.Dictionary: {
const mapping = new MappingToken(
token.file,
token.range,
token.definitionInfo
)
for (const { key, value } of result.pairs()) {
const keyToken = new StringToken(
token.file,
token.range,
key,
token.definitionInfo
)
const valueToken = convertToTemplateToken(token, value)
mapping.add(keyToken, valueToken)
}
return mapping
}
case Kind.Array: {
const sequence = new SequenceToken(
token.file,
token.range,
token.definitionInfo
)
for (const value of result.values()) {
const itemToken = convertToTemplateToken(token, value)
sequence.add(itemToken)
}
return sequence
}
default:
throw new Error("Unable to convert the object to a template token")
}
}
function convertToLiteralToken(
token: BasicExpressionToken,
result: ExpressionData
): LiteralToken | undefined {
switch (result.kind) {
case Kind.Null:
return new NullToken(token.file, token.range, token.definitionInfo)
case Kind.Boolean:
return new BooleanToken(
token.file,
token.range,
result.value,
token.definitionInfo
)
case Kind.Number:
return new NumberToken(
token.file,
token.range,
result.value,
token.definitionInfo
)
case Kind.String:
return new StringToken(
token.file,
token.range,
result.value,
token.definitionInfo
)
}
return undefined
}
function parseExpression(
expression: string,
contexts: string[],
functions: FunctionInfo[]
): Expr {
const lexer = new Lexer(expression)
const result = lexer.lex()
const p = new Parser(result.tokens, contexts, functions)
return p.parse()
}