mirror of
https://github.com/hanzoai/numscript.git
synced 2026-10-30 18:26:57 +00:00
Modules declare go 1.27.1 and the build images follow. go fix applied the modernizers, and the interpreter's parseArg, evaluateExprAs and evaluateOptExprAs are now generic methods on the parser and program state they take. omitzero is not applied, since it changes what the JSON says. Co-Authored-By: Claude Opus 5 <[email protected]>
683 lines
17 KiB
Go
683 lines
17 KiB
Go
package parser
|
|
|
|
import (
|
|
"fmt"
|
|
"math/big"
|
|
"strings"
|
|
|
|
antlrParser "github.com/formancehq/numscript/internal/parser/antlrParser"
|
|
"github.com/formancehq/numscript/internal/utils"
|
|
|
|
"github.com/antlr4-go/antlr/v4"
|
|
)
|
|
|
|
type ParserError struct {
|
|
Range
|
|
Msg string
|
|
}
|
|
|
|
func (e ParserError) Error() string {
|
|
return e.Msg
|
|
}
|
|
|
|
type ParseResult struct {
|
|
Source string
|
|
Value Program
|
|
Errors []ParserError
|
|
}
|
|
|
|
type ErrorListener struct {
|
|
antlr.DefaultErrorListener
|
|
Errors []ParserError
|
|
}
|
|
|
|
func (l *ErrorListener) SyntaxError(recognizer antlr.Recognizer, offendingSymbol any, startL, startC int, msg string, e antlr.RecognitionException) {
|
|
length := 1
|
|
if token, ok := offendingSymbol.(antlr.Token); ok {
|
|
length = len(token.GetText())
|
|
}
|
|
endL := startL
|
|
endC := startC + length - 1 // -1 so that end character is inside the offending token
|
|
l.Errors = append(l.Errors, ParserError{
|
|
Msg: msg,
|
|
Start: Position{Character: startC, Line: startL - 1},
|
|
End: Position{Character: endC, Line: endL - 1},
|
|
})
|
|
}
|
|
|
|
func Parse(input string) ParseResult {
|
|
// TODO handle lexer errors
|
|
listener := &ErrorListener{}
|
|
|
|
is := antlr.NewInputStream(input)
|
|
lexer := antlrParser.NewLexer(is)
|
|
lexer.RemoveErrorListeners()
|
|
lexer.AddErrorListener(listener)
|
|
|
|
stream := antlr.NewCommonTokenStream(lexer, antlr.TokenDefaultChannel)
|
|
|
|
p := antlrParser.NewNumscriptParser(stream)
|
|
p.RemoveErrorListeners()
|
|
p.AddErrorListener(listener)
|
|
|
|
parsed := parseProgram(p.Program())
|
|
|
|
for _, tk := range stream.GetAllTokens() {
|
|
if tk.GetChannel() == antlr.TokenHiddenChannel {
|
|
parsed.Comments = append(parsed.Comments, Comment{
|
|
Content: tk.GetText()[2:],
|
|
Range: tokenToRange(tk),
|
|
})
|
|
}
|
|
}
|
|
|
|
return ParseResult{
|
|
Source: input,
|
|
Value: parsed,
|
|
Errors: listener.Errors,
|
|
}
|
|
}
|
|
|
|
func ParseErrorsToString(errors []ParserError, source string) string {
|
|
var buf strings.Builder
|
|
buf.WriteString("Got errors while parsing:\n")
|
|
for _, err := range errors {
|
|
buf.WriteString(err.Msg + "\n" + err.ShowOnSource(source) + "\n")
|
|
}
|
|
return buf.String()
|
|
}
|
|
|
|
func parseVarsDeclaration(varsCtx antlrParser.IVarsDeclarationContext) *VarDeclarations {
|
|
|
|
if varsCtx == nil {
|
|
return nil
|
|
}
|
|
|
|
varBlock := VarDeclarations{
|
|
Range: ctxToRange(varsCtx),
|
|
}
|
|
|
|
for _, varDecl := range varsCtx.AllVarDeclaration() {
|
|
decl := parseVarDeclaration(varDecl)
|
|
if decl != nil {
|
|
varBlock.Declarations = append(varBlock.Declarations, *decl)
|
|
}
|
|
}
|
|
return &varBlock
|
|
}
|
|
|
|
func parseProgram(programCtx antlrParser.IProgramContext) Program {
|
|
vars := parseVarsDeclaration(programCtx.VarsDeclaration())
|
|
|
|
featureCtx := programCtx.FeatureDecl()
|
|
|
|
var flagIdents []StringLiteral
|
|
if featureCtx != nil {
|
|
for _, flagTk := range featureCtx.GetFlag() {
|
|
flagIdents = append(flagIdents, *parseStringTk(flagTk))
|
|
}
|
|
}
|
|
|
|
var statements []Statement
|
|
for _, statementCtx := range programCtx.AllStatement() {
|
|
statements = append(statements, parseStatement(statementCtx))
|
|
}
|
|
|
|
return Program{
|
|
Flags: flagIdents,
|
|
Statements: statements,
|
|
Vars: vars,
|
|
}
|
|
}
|
|
|
|
func parseVarDeclaration(varDecl antlrParser.IVarDeclarationContext) *VarDeclaration {
|
|
if varDecl == nil {
|
|
return nil
|
|
}
|
|
|
|
var origin *ValueExpr
|
|
if varDecl.VarOrigin() != nil {
|
|
expr := parseValueExpr(varDecl.VarOrigin().ValueExpr())
|
|
origin = &expr
|
|
}
|
|
|
|
return &VarDeclaration{
|
|
Range: ctxToRange(varDecl),
|
|
Type: parseVarType(varDecl.GetType_()),
|
|
Name: parseVarLiteral(varDecl.GetName()),
|
|
Origin: origin,
|
|
}
|
|
}
|
|
|
|
func parseVarLiteral(tk antlr.Token) *Variable {
|
|
if tk == nil || tk.GetTokenIndex() == -1 {
|
|
return nil
|
|
}
|
|
|
|
name := tk.GetText()[1:]
|
|
|
|
return &Variable{
|
|
Range: tokenToRange(tk),
|
|
Name: name,
|
|
}
|
|
}
|
|
|
|
func parseVarType(tk antlr.Token) *TypeDecl {
|
|
if tk == nil || tk.GetTokenIndex() == -1 {
|
|
return nil
|
|
}
|
|
|
|
return &TypeDecl{
|
|
Range: tokenToRange(tk),
|
|
Name: tk.GetText(),
|
|
}
|
|
}
|
|
|
|
func parseColorConstraint(colorConstraintCtx antlrParser.IColorConstraintContext) ValueExpr {
|
|
if colorConstraintCtx == nil {
|
|
return nil
|
|
}
|
|
|
|
return parseValueExpr(
|
|
colorConstraintCtx.ValueExpr(),
|
|
)
|
|
}
|
|
|
|
func parseSource(sourceCtx antlrParser.ISourceContext) Source {
|
|
if sourceCtx == nil {
|
|
return nil
|
|
}
|
|
|
|
range_ := ctxToRange(sourceCtx)
|
|
|
|
switch sourceCtx := sourceCtx.(type) {
|
|
case *antlrParser.SrcAccountContext:
|
|
return &SourceAccount{
|
|
Range: range_,
|
|
Color: parseColorConstraint(sourceCtx.ColorConstraint()),
|
|
ValueExpr: parseValueExpr(sourceCtx.ValueExpr()),
|
|
}
|
|
|
|
case *antlrParser.SrcInorderContext:
|
|
var sources []Source
|
|
for _, sourceCtx := range sourceCtx.AllSource() {
|
|
sources = append(sources, parseSource(sourceCtx))
|
|
}
|
|
return &SourceInorder{
|
|
Range: range_,
|
|
Sources: sources,
|
|
}
|
|
|
|
case *antlrParser.SrcOneofContext:
|
|
var sources []Source
|
|
for _, sourceCtx := range sourceCtx.AllSource() {
|
|
sources = append(sources, parseSource(sourceCtx))
|
|
}
|
|
return &SourceOneof{
|
|
Range: range_,
|
|
Sources: sources,
|
|
}
|
|
|
|
case *antlrParser.SrcAllotmentContext:
|
|
var items []SourceAllotmentItem
|
|
for _, itemCtx := range sourceCtx.AllAllotmentClauseSrc() {
|
|
item := SourceAllotmentItem{
|
|
Range: ctxToRange(itemCtx),
|
|
Allotment: parseAllotment(itemCtx.Allotment()),
|
|
From: parseSource(itemCtx.Source()),
|
|
}
|
|
items = append(items, item)
|
|
}
|
|
return &SourceAllotment{
|
|
Range: range_,
|
|
Items: items,
|
|
}
|
|
|
|
case *antlrParser.SrcCappedContext:
|
|
return &SourceCapped{
|
|
Range: range_,
|
|
From: parseSource(sourceCtx.Source()),
|
|
Cap: parseValueExpr(sourceCtx.GetCap_()),
|
|
}
|
|
|
|
case *antlrParser.SrcAccountUnboundedOverdraftContext:
|
|
return &SourceOverdraft{
|
|
Range: ctxToRange(sourceCtx),
|
|
Color: parseColorConstraint(sourceCtx.ColorConstraint()),
|
|
Address: parseValueExpr(sourceCtx.GetAddress()),
|
|
}
|
|
|
|
case *antlrParser.SrcAccountWithScalingContext:
|
|
return &SourceWithScaling{
|
|
Range: ctxToRange(sourceCtx),
|
|
Address: parseValueExpr(sourceCtx.GetAddress()),
|
|
Through: parseValueExpr(sourceCtx.GetSwap()),
|
|
}
|
|
|
|
case *antlrParser.SrcAccountBoundedOverdraftContext:
|
|
varMon := parseValueExpr(sourceCtx.GetMaxOvedraft())
|
|
|
|
return &SourceOverdraft{
|
|
Range: ctxToRange(sourceCtx),
|
|
Color: parseColorConstraint(sourceCtx.ColorConstraint()),
|
|
Address: parseValueExpr(sourceCtx.GetAddress()),
|
|
Bounded: &varMon,
|
|
}
|
|
|
|
case *antlrParser.SourceContext:
|
|
return nil
|
|
|
|
default:
|
|
return utils.NonExhaustiveMatchPanic[Source](sourceCtx.GetText())
|
|
}
|
|
}
|
|
|
|
// Returns (a, b) representing a/(10^b)
|
|
func ParsePercentageRatio(source string) (*big.Int, int, error) {
|
|
source = strings.TrimSuffix(source, "%")
|
|
|
|
scale := 0
|
|
if i := strings.Index(source, "."); i != -1 {
|
|
scale = len(source) - i - 1
|
|
}
|
|
|
|
intPart := strings.ReplaceAll(source, ".", "")
|
|
num, ok := new(big.Int).SetString(intPart, 10)
|
|
if !ok {
|
|
return nil, 0, fmt.Errorf("unexpected invalid string literal: %s", source)
|
|
}
|
|
|
|
return num, scale, nil
|
|
}
|
|
|
|
func parsePercentageRatio(source string, range_ Range) *PercentageLiteral {
|
|
num, floatingDigits, err := ParsePercentageRatio(source)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
return &PercentageLiteral{
|
|
Range: range_,
|
|
Amount: num,
|
|
FloatingDigits: floatingDigits,
|
|
}
|
|
}
|
|
|
|
func parseAllotment(allotmentCtx antlrParser.IAllotmentContext) AllotmentValue {
|
|
switch allotmentCtx := allotmentCtx.(type) {
|
|
case *antlrParser.PortionedAllotmentContext:
|
|
expr := parseValueExpr(allotmentCtx.ValueExpr())
|
|
return &ValueExprAllotment{expr}
|
|
|
|
case *antlrParser.RemainingAllotmentContext:
|
|
return &RemainingAllotment{
|
|
Range: ctxToRange(allotmentCtx),
|
|
}
|
|
|
|
case *antlrParser.AllotmentContext:
|
|
return nil
|
|
|
|
default:
|
|
return utils.NonExhaustiveMatchPanic[AllotmentValue](allotmentCtx.GetText())
|
|
}
|
|
}
|
|
|
|
func parseStringTk(stringCtx antlr.Token) *StringLiteral {
|
|
rawStr := stringCtx.GetText()
|
|
// Remove leading and trailing '"'
|
|
innerStr := rawStr[1 : len(rawStr)-1]
|
|
return &StringLiteral{
|
|
Range: tokenToRange(stringCtx),
|
|
String: innerStr,
|
|
}
|
|
}
|
|
|
|
func parseValueExpr(valueExprCtx antlrParser.IValueExprContext) ValueExpr {
|
|
switch valueExprCtx := valueExprCtx.(type) {
|
|
case *antlrParser.ParenthesizedExprContext:
|
|
return parseValueExpr(valueExprCtx.ValueExpr())
|
|
|
|
case *antlrParser.AccountLiteralContext:
|
|
litRng := ctxToRange(valueExprCtx)
|
|
|
|
var parts []AccountNamePart
|
|
for _, accLit := range valueExprCtx.GetChildren() {
|
|
switch accLit := accLit.(type) {
|
|
case *antlrParser.AccountTextPartContext:
|
|
varPartText := accLit.GetText()
|
|
parts = append(parts, AccountTextPart{Name: varPartText})
|
|
case *antlrParser.AccountVarPartContext:
|
|
v := parseVarLiteral(accLit.VARIABLE_NAME_ACC().GetSymbol())
|
|
parts = append(parts, v)
|
|
|
|
case antlr.TerminalNode:
|
|
if accLit.GetSymbol().GetTokenType() == antlrParser.NumscriptParserCOLON {
|
|
parts = append(parts, AccountTextPart{Name: ":"})
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
return &AccountInterpLiteral{
|
|
Range: litRng,
|
|
Parts: parts,
|
|
}
|
|
|
|
case *antlrParser.MonetaryLiteralContext:
|
|
return parseMonetaryLit(valueExprCtx.MonetaryLit())
|
|
|
|
case *antlrParser.AssetLiteralContext:
|
|
return &AssetLiteral{
|
|
Range: ctxToRange(valueExprCtx),
|
|
Asset: valueExprCtx.GetText(),
|
|
}
|
|
|
|
case *antlrParser.NumberLiteralContext:
|
|
return parseNumberLiteral(valueExprCtx.NUMBER())
|
|
|
|
case *antlrParser.PercentagePortionLiteralContext:
|
|
return parsePercentageRatio(valueExprCtx.GetText(), ctxToRange(valueExprCtx))
|
|
|
|
case *antlrParser.VariableExprContext:
|
|
return variableLiteralFromCtx(valueExprCtx)
|
|
|
|
case *antlrParser.StringLiteralContext:
|
|
return parseStringTk(valueExprCtx.STRING().GetSymbol())
|
|
|
|
case *antlrParser.PrefixExprContext:
|
|
return &Prefix{
|
|
Range: ctxToRange(valueExprCtx),
|
|
Operator: PrefixOperator(valueExprCtx.GetOp().GetText()),
|
|
Expr: parseValueExpr(valueExprCtx.ValueExpr()),
|
|
}
|
|
|
|
case *antlrParser.InfixExprContext:
|
|
return &BinaryInfix{
|
|
Range: ctxToRange(valueExprCtx),
|
|
Operator: InfixOperator(valueExprCtx.GetOp().GetText()),
|
|
Left: parseValueExpr(valueExprCtx.GetLeft()),
|
|
Right: parseValueExpr(valueExprCtx.GetRight()),
|
|
}
|
|
|
|
case nil, *antlrParser.ValueExprContext:
|
|
return nil
|
|
|
|
case *antlrParser.ApplicationContext:
|
|
return parseFnCall(valueExprCtx.FunctionCall())
|
|
|
|
default:
|
|
return utils.NonExhaustiveMatchPanic[ValueExpr](valueExprCtx.GetText())
|
|
}
|
|
}
|
|
|
|
func variableLiteralFromCtx(ctx antlr.ParserRuleContext) *Variable {
|
|
// Discard the '$'
|
|
name := ctx.GetText()[1:]
|
|
|
|
return &Variable{
|
|
Range: ctxToRange(ctx),
|
|
Name: name,
|
|
}
|
|
}
|
|
|
|
func parseDestination(destCtx antlrParser.IDestinationContext) Destination {
|
|
if destCtx == nil {
|
|
return nil
|
|
}
|
|
|
|
range_ := ctxToRange(destCtx)
|
|
|
|
switch destCtx := destCtx.(type) {
|
|
case *antlrParser.DestAccountContext:
|
|
return &DestinationAccount{
|
|
ValueExpr: parseValueExpr(destCtx.ValueExpr()),
|
|
}
|
|
|
|
case *antlrParser.DestInorderContext:
|
|
var inorderClauses []CappedKeptOrDestination
|
|
for _, destInorderClause := range destCtx.AllDestinationInOrderClause() {
|
|
inorderClauses = append(inorderClauses, parseDestinationInorderClause(destInorderClause))
|
|
}
|
|
|
|
return &DestinationInorder{
|
|
Range: range_,
|
|
Clauses: inorderClauses,
|
|
Remaining: parseKeptOrDestination(destCtx.KeptOrDestination()),
|
|
}
|
|
|
|
case *antlrParser.DestOneofContext:
|
|
var inorderClauses []CappedKeptOrDestination
|
|
for _, destInorderClause := range destCtx.AllDestinationInOrderClause() {
|
|
inorderClauses = append(inorderClauses, parseDestinationInorderClause(destInorderClause))
|
|
}
|
|
|
|
return &DestinationOneof{
|
|
Range: range_,
|
|
Clauses: inorderClauses,
|
|
Remaining: parseKeptOrDestination(destCtx.KeptOrDestination()),
|
|
}
|
|
|
|
case *antlrParser.DestAllotmentContext:
|
|
var items []DestinationAllotmentItem
|
|
for _, itemCtx := range destCtx.AllAllotmentClauseDest() {
|
|
item := DestinationAllotmentItem{
|
|
Range: ctxToRange(itemCtx),
|
|
Allotment: parseDestinationAllotment(itemCtx.Allotment()),
|
|
To: parseKeptOrDestination(itemCtx.KeptOrDestination()),
|
|
}
|
|
items = append(items, item)
|
|
}
|
|
return &DestinationAllotment{
|
|
Range: range_,
|
|
Items: items,
|
|
}
|
|
|
|
case *antlrParser.DestinationContext:
|
|
return nil
|
|
|
|
default:
|
|
return utils.NonExhaustiveMatchPanic[Destination](destCtx.GetText())
|
|
}
|
|
|
|
}
|
|
|
|
func parseDestinationInorderClause(clauseCtx antlrParser.IDestinationInOrderClauseContext) CappedKeptOrDestination {
|
|
return CappedKeptOrDestination{
|
|
Range: ctxToRange(clauseCtx),
|
|
Cap: parseValueExpr(clauseCtx.ValueExpr()),
|
|
To: parseKeptOrDestination(clauseCtx.KeptOrDestination()),
|
|
}
|
|
}
|
|
|
|
func parseKeptOrDestination(clauseCtx antlrParser.IKeptOrDestinationContext) KeptOrDestination {
|
|
if clauseCtx == nil {
|
|
return nil
|
|
}
|
|
|
|
switch clauseCtx := clauseCtx.(type) {
|
|
case *antlrParser.DestinationToContext:
|
|
return &DestinationTo{
|
|
Destination: parseDestination(clauseCtx.Destination()),
|
|
}
|
|
case *antlrParser.DestinationKeptContext:
|
|
return &DestinationKept{
|
|
Range: ctxToRange(clauseCtx),
|
|
}
|
|
case *antlrParser.KeptOrDestinationContext:
|
|
return nil
|
|
|
|
default:
|
|
return utils.NonExhaustiveMatchPanic[KeptOrDestination](clauseCtx.GetText())
|
|
}
|
|
|
|
}
|
|
|
|
func parseDestinationAllotment(allotmentCtx antlrParser.IAllotmentContext) AllotmentValue {
|
|
switch allotmentCtx := allotmentCtx.(type) {
|
|
case *antlrParser.RemainingAllotmentContext:
|
|
return &RemainingAllotment{
|
|
Range: ctxToRange(allotmentCtx),
|
|
}
|
|
|
|
case *antlrParser.PortionedAllotmentContext:
|
|
expr := parseValueExpr(allotmentCtx.ValueExpr())
|
|
return &ValueExprAllotment{Value: expr}
|
|
|
|
case *antlrParser.AllotmentContext:
|
|
return nil
|
|
|
|
default:
|
|
return utils.NonExhaustiveMatchPanic[AllotmentValue](allotmentCtx.GetText())
|
|
}
|
|
}
|
|
|
|
func parseFnArgs(fnCallArgCtx antlrParser.IFunctionCallArgsContext) []ValueExpr {
|
|
if fnCallArgCtx == nil {
|
|
return nil
|
|
}
|
|
|
|
var args []ValueExpr
|
|
for _, valueExpr := range fnCallArgCtx.AllValueExpr() {
|
|
args = append(args, parseValueExpr(valueExpr))
|
|
}
|
|
return args
|
|
}
|
|
|
|
func parseFnCall(fnCallCtx antlrParser.IFunctionCallContext) *FnCall {
|
|
if fnCallCtx == nil {
|
|
return nil
|
|
}
|
|
|
|
ident := fnCallCtx.GetFnName()
|
|
if ident == nil {
|
|
return nil
|
|
}
|
|
|
|
allArgs := fnCallCtx.FunctionCallArgs()
|
|
|
|
return &FnCall{
|
|
Range: ctxToRange(fnCallCtx),
|
|
Caller: &FnCallIdentifier{
|
|
Range: tokenToRange(ident),
|
|
Name: ident.GetText(),
|
|
},
|
|
Args: parseFnArgs(allArgs),
|
|
}
|
|
}
|
|
|
|
func parseSaveStatement(saveCtx *antlrParser.SaveStatementContext) *SaveStatement {
|
|
return &SaveStatement{
|
|
Range: ctxToRange(saveCtx),
|
|
SentValue: parseSentValue(saveCtx.SentValue()),
|
|
Account: parseValueExpr(saveCtx.ValueExpr()),
|
|
}
|
|
}
|
|
|
|
func parseStatement(statementCtx antlrParser.IStatementContext) Statement {
|
|
switch statementCtx := statementCtx.(type) {
|
|
case *antlrParser.SendStatementContext:
|
|
return parseSendStatement(statementCtx)
|
|
|
|
case *antlrParser.SaveStatementContext:
|
|
return parseSaveStatement(statementCtx)
|
|
|
|
case *antlrParser.FnCallStatementContext:
|
|
return parseFnCall(statementCtx.FunctionCall())
|
|
|
|
case *antlrParser.StatementContext:
|
|
return nil
|
|
|
|
default:
|
|
return utils.NonExhaustiveMatchPanic[Statement](statementCtx.GetText())
|
|
}
|
|
}
|
|
|
|
func parseSentValue(statementCtx antlrParser.ISentValueContext) SentValue {
|
|
switch statementCtx := statementCtx.(type) {
|
|
case *antlrParser.SentLiteralContext:
|
|
return &SentValueLiteral{
|
|
Range: ctxToRange(statementCtx),
|
|
Monetary: parseValueExpr(statementCtx.ValueExpr()),
|
|
}
|
|
case *antlrParser.SentAllContext:
|
|
return &SentValueAll{
|
|
Range: ctxToRange(statementCtx),
|
|
Asset: parseValueExpr(statementCtx.SentAllLit().GetAsset()),
|
|
}
|
|
|
|
case *antlrParser.SentValueContext:
|
|
return nil
|
|
|
|
default:
|
|
return utils.NonExhaustiveMatchPanic[SentValue](statementCtx.GetText())
|
|
}
|
|
|
|
}
|
|
|
|
func parseSendStatement(statementCtx *antlrParser.SendStatementContext) *SendStatement {
|
|
return &SendStatement{
|
|
Source: parseSource(statementCtx.Source()),
|
|
Destination: parseDestination(statementCtx.Destination()),
|
|
Range: ctxToRange(statementCtx),
|
|
SentValue: parseSentValue(statementCtx.SentValue()),
|
|
}
|
|
}
|
|
|
|
func parseNumberLiteral(numNode antlr.TerminalNode) *NumberLiteral {
|
|
amtStr := numNode.GetText()
|
|
amtStr = strings.ReplaceAll(amtStr, "_", "")
|
|
|
|
amt, ok := new(big.Int).SetString(amtStr, 10)
|
|
if !ok {
|
|
panic("Invalid number: " + amtStr)
|
|
}
|
|
|
|
return &NumberLiteral{
|
|
Range: tokenToRange(numNode.GetSymbol()),
|
|
Number: amt,
|
|
}
|
|
}
|
|
|
|
func parseMonetaryLit(monetaryLitCtx antlrParser.IMonetaryLitContext) *MonetaryLiteral {
|
|
if monetaryLitCtx.GetAmt() == nil {
|
|
return nil
|
|
}
|
|
|
|
return &MonetaryLiteral{
|
|
Range: ctxToRange(monetaryLitCtx),
|
|
Asset: parseValueExpr(monetaryLitCtx.GetAsset()),
|
|
Amount: parseValueExpr(monetaryLitCtx.GetAmt()),
|
|
}
|
|
}
|
|
|
|
func ctxToRange(ctx antlr.ParserRuleContext) Range {
|
|
startTk := ctx.GetStart()
|
|
endTk := ctx.GetStop()
|
|
|
|
return Range{
|
|
Start: Position{
|
|
Line: startTk.GetLine() - 1,
|
|
Character: startTk.GetColumn(),
|
|
},
|
|
End: Position{
|
|
Line: endTk.GetLine() - 1,
|
|
|
|
// this is based on the assumption that a token cannot span multiple lines
|
|
Character: endTk.GetColumn() + len(endTk.GetText()),
|
|
},
|
|
}
|
|
}
|
|
|
|
func tokenToRange(tk antlr.Token) Range {
|
|
return Range{
|
|
Start: Position{
|
|
Line: tk.GetLine() - 1,
|
|
Character: tk.GetColumn(),
|
|
},
|
|
End: Position{
|
|
Line: tk.GetLine() - 1,
|
|
Character: tk.GetColumn() + len(tk.GetText()),
|
|
},
|
|
}
|
|
}
|