mirror of
https://github.com/alda-lang/alda.git
synced 2026-02-27 18:24:13 +01:00
224 lines
5.2 KiB
Go
224 lines
5.2 KiB
Go
package cmd
|
|
|
|
import (
|
|
"fmt"
|
|
"time"
|
|
|
|
"alda.io/client/color"
|
|
"alda.io/client/help"
|
|
"alda.io/client/json"
|
|
log "alda.io/client/logging"
|
|
"alda.io/client/model"
|
|
"alda.io/client/parser"
|
|
"alda.io/client/system"
|
|
"github.com/spf13/cobra"
|
|
)
|
|
|
|
var outputType string
|
|
|
|
func init() {
|
|
parseCmd.Flags().StringVarP(
|
|
&file, "file", "f", "", "Read Alda source code from a file",
|
|
)
|
|
|
|
parseCmd.Flags().StringVarP(
|
|
&code, "code", "c", "", "Supply Alda source code as a string",
|
|
)
|
|
|
|
parseCmd.Flags().StringVarP(
|
|
&outputType, "output", "o", "data", "The desired parse output",
|
|
)
|
|
}
|
|
|
|
var parseCmd = &cobra.Command{
|
|
Use: "parse",
|
|
Short: "Display the result of parsing Alda source code",
|
|
Long: fmt.Sprintf(`Display the result of parsing Alda source code
|
|
|
|
---
|
|
|
|
%s
|
|
|
|
---
|
|
|
|
The -o / --output parameter determines what output is displayed.
|
|
Options include:
|
|
|
|
ast:
|
|
|
|
The AST that results from parsing the source code, represented as a JSON
|
|
object.
|
|
|
|
ast-human:
|
|
|
|
The AST that results from parsing the source code, displayed in a more
|
|
human-readable way.
|
|
|
|
events:
|
|
|
|
A JSON array of objects, each of which represents an "event" parsed from the
|
|
source code.
|
|
|
|
data (default):
|
|
|
|
A JSON object representing the score that is constructed after parsing the
|
|
source code into events and evaluating them in order within the context of a
|
|
new score.
|
|
|
|
---`,
|
|
sourceCodeInputOptions("parse", false),
|
|
),
|
|
RunE: func(_ *cobra.Command, args []string) error {
|
|
switch outputType {
|
|
case "ast", "ast-human", "events", "data": // OK to proceed
|
|
default:
|
|
return help.UserFacingErrorf(
|
|
`%s is not a supported output type.
|
|
|
|
Please choose one of:
|
|
|
|
%s
|
|
The AST that results from parsing the source code, represented as a JSON
|
|
object.
|
|
|
|
%s
|
|
The AST that results from parsing the source code, displayed in a more
|
|
human-readable way.
|
|
|
|
%s
|
|
A JSON array of objects, each of which represents an "event" parsed from the
|
|
source code.
|
|
|
|
%s (default)
|
|
A JSON object representing the score that is constructed after parsing the
|
|
source code into events and evaluating them in order within the context of a
|
|
new score.`,
|
|
color.Aurora.BrightYellow(outputType),
|
|
color.Aurora.BrightYellow("ast"),
|
|
color.Aurora.BrightYellow("ast-human"),
|
|
color.Aurora.BrightYellow("events"),
|
|
color.Aurora.BrightYellow("data"),
|
|
)
|
|
}
|
|
|
|
var ast parser.ASTNode
|
|
var scoreUpdates []model.ScoreUpdate
|
|
var err error
|
|
|
|
switch {
|
|
case file != "":
|
|
ast, err = parser.ParseFile(file)
|
|
|
|
case code != "":
|
|
ast, err = parser.ParseString(code)
|
|
|
|
default:
|
|
ast, err = parseStdin()
|
|
}
|
|
|
|
if err == system.ErrNoInputSupplied {
|
|
return userFacingNoInputSuppliedError("parse")
|
|
}
|
|
|
|
// Errors with source context are presented to the user as-is.
|
|
//
|
|
// TODO: Consider writing better user-facing error messages with suggestions
|
|
// for the users to try, etc. The Elm compiler is a good source of
|
|
// inspiration.
|
|
//
|
|
// TODO: If we do the above, we should also consider providing a flag that
|
|
// keeps the error messages terse/parseable, the way they are now. That way,
|
|
// tooling can be built around the parseable error messages with source
|
|
// context, e.g. an editor plugin can easily parse out the line and column
|
|
// number and display the error message at the relevant position in the
|
|
// file.
|
|
switch err.(type) {
|
|
case *model.AldaSourceError:
|
|
err = &help.UserFacingError{Err: err}
|
|
}
|
|
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if outputType == "ast" {
|
|
fmt.Println(ast.JSON().String())
|
|
|
|
return nil
|
|
}
|
|
|
|
if outputType == "ast-human" {
|
|
// HACK: Instead of the code below, we should really just be able to have
|
|
// this one line:
|
|
//
|
|
// fmt.Println(parser.HumanReadableAST(ast.JSON()))
|
|
//
|
|
// However, for some bizarre reason, it only seems to work if we serialize
|
|
// the JSON directly emitted by ast.JSON() as a string, then parse the
|
|
// string to get a new object. I guess something about ast.JSON() is doing
|
|
// something weird with nested gabs.Containers, or something like that.
|
|
jsonObj := ast.JSON()
|
|
jsonStr := jsonObj.String()
|
|
parsedJsonObj, err := json.ParseJSON([]byte(jsonStr))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
fmt.Println(parser.HumanReadableAST(parsedJsonObj))
|
|
|
|
return nil
|
|
}
|
|
|
|
scoreUpdates, err = ast.Updates()
|
|
|
|
// Errors with source context are presented to the user as-is.
|
|
//
|
|
// See TODO notes above.
|
|
switch err.(type) {
|
|
case *model.AldaSourceError:
|
|
err = &help.UserFacingError{Err: err}
|
|
}
|
|
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if outputType == "events" {
|
|
updates := json.Array()
|
|
for _, update := range scoreUpdates {
|
|
updates.ArrayAppend(update.JSON())
|
|
}
|
|
|
|
fmt.Println(updates.String())
|
|
|
|
return nil
|
|
}
|
|
|
|
score := model.NewScore()
|
|
start := time.Now()
|
|
err = score.Update(scoreUpdates...)
|
|
|
|
// Errors with source context are presented to the user as-is.
|
|
//
|
|
// TODO: See TODO comment above about writing better user-facing error
|
|
// messages.
|
|
switch err.(type) {
|
|
case *model.AldaSourceError:
|
|
err = &help.UserFacingError{Err: err}
|
|
}
|
|
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
log.Info().
|
|
Int("updates", len(scoreUpdates)).
|
|
Str("took", time.Since(start).String()).
|
|
Msg("Constructed score.")
|
|
|
|
fmt.Println(score.JSON().String())
|
|
|
|
return nil
|
|
},
|
|
}
|