123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406 |
- /*
- * ECAL
- *
- * Copyright 2020 Matthias Ladkau. All rights reserved.
- *
- * This Source Code Form is subject to the terms of the MIT
- * License, If a copy of the MIT License was not distributed with this
- * file, You can obtain one at https://opensource.org/licenses/MIT.
- */
- /*
- Package interpreter contains the ECAL interpreter.
- */
- package interpreter
- import (
- "fmt"
- "strconv"
- "strings"
- "devt.de/krotik/ecal/parser"
- "devt.de/krotik/ecal/util"
- )
- /*
- DebugCommandsMap contains the mapping of inbuild debug commands.
- */
- var DebugCommandsMap = map[string]util.DebugCommand{
- "breakonstart": &breakOnStartCommand{&inbuildDebugCommand{}},
- "break": &setBreakpointCommand{&inbuildDebugCommand{}},
- "rmbreak": &rmBreakpointCommand{&inbuildDebugCommand{}},
- "disablebreak": &disableBreakpointCommand{&inbuildDebugCommand{}},
- "cont": &contCommand{&inbuildDebugCommand{}},
- "describe": &describeCommand{&inbuildDebugCommand{}},
- "status": &statusCommand{&inbuildDebugCommand{}},
- "extract": &extractCommand{&inbuildDebugCommand{}},
- "inject": &injectCommand{&inbuildDebugCommand{}},
- }
- /*
- inbuildDebugCommand is the base structure for inbuild debug commands providing some
- utility functions.
- */
- type inbuildDebugCommand struct {
- }
- /*
- AssertNumParam converts a parameter into a number.
- */
- func (ibf *inbuildDebugCommand) AssertNumParam(index int, val string) (uint64, error) {
- if resNum, err := strconv.ParseInt(fmt.Sprint(val), 10, 0); err == nil {
- return uint64(resNum), nil
- }
- return 0, fmt.Errorf("Parameter %v should be a number", index)
- }
- // break
- // =====
- /*
- setBreakpointCommand sets a breakpoint
- */
- type setBreakpointCommand struct {
- *inbuildDebugCommand
- }
- /*
- Execute the debug command and return its result. It must be possible to
- convert the output data into a JSON string.
- */
- func (c *setBreakpointCommand) Run(debugger util.ECALDebugger, args []string) (interface{}, error) {
- if len(args) == 0 {
- return nil, fmt.Errorf("Need a break target (<source>:<line>) as first parameter")
- }
- targetSplit := strings.Split(args[0], ":")
- if len(targetSplit) > 1 {
- if line, err := strconv.Atoi(targetSplit[1]); err == nil {
- debugger.SetBreakPoint(targetSplit[0], line)
- return nil, nil
- }
- }
- return nil, fmt.Errorf("Invalid break target - should be <source>:<line>")
- }
- /*
- DocString returns a descriptive text about this command.
- */
- func (c *setBreakpointCommand) DocString() string {
- return "Set a breakpoint specifying <source>:<line>"
- }
- // breakOnStartCommand
- // ===================
- /*
- breakOnStartCommand breaks on the start of the next execution.
- */
- type breakOnStartCommand struct {
- *inbuildDebugCommand
- }
- /*
- Execute the debug command and return its result. It must be possible to
- convert the output data into a JSON string.
- */
- func (c *breakOnStartCommand) Run(debugger util.ECALDebugger, args []string) (interface{}, error) {
- b := true
- if len(args) > 0 {
- b, _ = strconv.ParseBool(args[0])
- }
- debugger.BreakOnStart(b)
- return nil, nil
- }
- /*
- DocString returns a descriptive text about this command.
- */
- func (c *breakOnStartCommand) DocString() string {
- return "Break on the start of the next execution."
- }
- // rmbreak
- // =======
- /*
- rmBreakpointCommand removes a breakpoint
- */
- type rmBreakpointCommand struct {
- *inbuildDebugCommand
- }
- /*
- Execute the debug command and return its result. It must be possible to
- convert the output data into a JSON string.
- */
- func (c *rmBreakpointCommand) Run(debugger util.ECALDebugger, args []string) (interface{}, error) {
- if len(args) == 0 {
- return nil, fmt.Errorf("Need a break target (<source>[:<line>]) as first parameter")
- }
- targetSplit := strings.Split(args[0], ":")
- if len(targetSplit) > 1 {
- if line, err := strconv.Atoi(targetSplit[1]); err == nil {
- debugger.RemoveBreakPoint(targetSplit[0], line)
- return nil, nil
- }
- } else {
- debugger.RemoveBreakPoint(args[0], -1)
- }
- return nil, nil
- }
- /*
- DocString returns a descriptive text about this command.
- */
- func (c *rmBreakpointCommand) DocString() string {
- return "Remove a breakpoint specifying <source>:<line>"
- }
- // disablebreak
- // ============
- /*
- disableBreakpointCommand temporarily disables a breakpoint
- */
- type disableBreakpointCommand struct {
- *inbuildDebugCommand
- }
- /*
- Execute the debug command and return its result. It must be possible to
- convert the output data into a JSON string.
- */
- func (c *disableBreakpointCommand) Run(debugger util.ECALDebugger, args []string) (interface{}, error) {
- if len(args) == 0 {
- return nil, fmt.Errorf("Need a break target (<source>:<line>) as first parameter")
- }
- targetSplit := strings.Split(args[0], ":")
- if len(targetSplit) > 1 {
- if line, err := strconv.Atoi(targetSplit[1]); err == nil {
- debugger.DisableBreakPoint(targetSplit[0], line)
- return nil, nil
- }
- }
- return nil, fmt.Errorf("Invalid break target - should be <source>:<line>")
- }
- /*
- DocString returns a descriptive text about this command.
- */
- func (c *disableBreakpointCommand) DocString() string {
- return "Temporarily disable a breakpoint specifying <source>:<line>"
- }
- // cont
- // ====
- /*
- contCommand continues a suspended thread
- */
- type contCommand struct {
- *inbuildDebugCommand
- }
- /*
- Execute the debug command and return its result. It must be possible to
- convert the output data into a JSON string.
- */
- func (c *contCommand) Run(debugger util.ECALDebugger, args []string) (interface{}, error) {
- var cmd util.ContType
- if len(args) != 2 {
- return nil, fmt.Errorf("Need a thread ID and a command Resume, StepIn, StepOver or StepOut")
- }
- threadID, err := c.AssertNumParam(1, args[0])
- if err == nil {
- cmdString := strings.ToLower(args[1])
- switch cmdString {
- case "resume":
- cmd = util.Resume
- case "stepin":
- cmd = util.StepIn
- case "stepover":
- cmd = util.StepOver
- case "stepout":
- cmd = util.StepOut
- default:
- return nil, fmt.Errorf("Invalid command %v - must be resume, stepin, stepover or stepout", cmdString)
- }
- debugger.Continue(threadID, cmd)
- }
- return nil, err
- }
- /*
- DocString returns a descriptive text about this command.
- */
- func (c *contCommand) DocString() string {
- return "Continues a suspended thread. Specify <threadID> <Resume | StepIn | StepOver | StepOut>"
- }
- // describe
- // ========
- /*
- describeCommand describes a suspended thread
- */
- type describeCommand struct {
- *inbuildDebugCommand
- }
- /*
- Execute the debug command and return its result. It must be possible to
- convert the output data into a JSON string.
- */
- func (c *describeCommand) Run(debugger util.ECALDebugger, args []string) (interface{}, error) {
- var res interface{}
- if len(args) != 1 {
- return nil, fmt.Errorf("Need a thread ID")
- }
- threadID, err := c.AssertNumParam(1, args[0])
- if err == nil {
- res = debugger.Describe(threadID)
- }
- return res, err
- }
- /*
- DocString returns a descriptive text about this command.
- */
- func (c *describeCommand) DocString() string {
- return "Describes a suspended thread."
- }
- // status
- // ======
- /*
- statusCommand shows breakpoints and suspended threads
- */
- type statusCommand struct {
- *inbuildDebugCommand
- }
- /*
- Execute the debug command and return its result. It must be possible to
- convert the output data into a JSON string.
- */
- func (c *statusCommand) Run(debugger util.ECALDebugger, args []string) (interface{}, error) {
- return debugger.Status(), nil
- }
- /*
- DocString returns a descriptive text about this command.
- */
- func (c *statusCommand) DocString() string {
- return "Shows breakpoints and suspended threads."
- }
- // extract
- // =======
- /*
- extractCommand copies a value from a suspended thread into the
- global variable scope
- */
- type extractCommand struct {
- *inbuildDebugCommand
- }
- /*
- Execute the debug command and return its result. It must be possible to
- convert the output data into a JSON string.
- */
- func (c *extractCommand) Run(debugger util.ECALDebugger, args []string) (interface{}, error) {
- if len(args) != 3 {
- return nil, fmt.Errorf("Need a thread ID, a variable name and a destination variable name")
- }
- threadID, err := c.AssertNumParam(1, args[0])
- if err == nil {
- if !parser.NamePattern.MatchString(args[1]) || !parser.NamePattern.MatchString(args[2]) {
- err = fmt.Errorf("Variable names may only contain [a-zA-Z] and [a-zA-Z0-9] from the second character")
- }
- if err == nil {
- err = debugger.ExtractValue(threadID, args[1], args[2])
- }
- }
- return nil, err
- }
- /*
- DocString returns a descriptive text about this command.
- */
- func (c *extractCommand) DocString() string {
- return "Copies a value from a suspended thread into the global variable scope."
- }
- // inject
- // =======
- /*
- injectCommand copies a value from the global variable scope into
- a suspended thread
- */
- type injectCommand struct {
- *inbuildDebugCommand
- }
- /*
- Execute the debug command and return its result. It must be possible to
- convert the output data into a JSON string.
- */
- func (c *injectCommand) Run(debugger util.ECALDebugger, args []string) (interface{}, error) {
- if len(args) < 3 {
- return nil, fmt.Errorf("Need a thread ID, a variable name and an expression")
- }
- threadID, err := c.AssertNumParam(1, args[0])
- if err == nil {
- varName := args[1]
- expression := strings.Join(args[2:], " ")
- err = debugger.InjectValue(threadID, varName, expression)
- }
- return nil, err
- }
- /*
- DocString returns a descriptive text about this command.
- */
- func (c *injectCommand) DocString() string {
- return "Copies a value from the global variable scope into a suspended thread."
- }
|