diff --git a/examples/nolol/array.nolol b/examples/nolol/array.nolol index 09f207a..cfb4206 100644 --- a/examples/nolol/array.nolol +++ b/examples/nolol/array.nolol @@ -2,6 +2,8 @@ // Set :d to the value you want to storem :a to the address to store it into and :m to 1 in order to store a value // Set :m to 0 and :a to the index to retrieve a value into :d +// is good +// is best define data = :d define addr = :a define write = :m @@ -20,4 +22,3 @@ $ _if write then mem3 = data else data = mem3 end; addr = 0; goto start $ $ _if write then mem4 = data else data = mem4 end; addr = 0; goto start $ $ _if write then mem5 = data else data = mem5 end; addr = 0; goto start $ $ _if write then mem6 = data else data = mem6 end; addr = 0; goto start $ - diff --git a/examples/nolol/array_user.nolol b/examples/nolol/array_user.nolol index 7445d7a..ef988d3 100644 --- a/examples/nolol/array_user.nolol +++ b/examples/nolol/array_user.nolol @@ -7,11 +7,11 @@ define write = :m // store value at idx in array macro set(idx, value) write = 1 - data = value + data = value addr = idx // this will block until addr is 0. // the array will set addr to 0 once it stored the value - wait addr + wait addr end // retrieve value from idx diff --git a/pkg/langserver/cache.go b/pkg/langserver/cache.go index b7ef1e4..d2793df 100644 --- a/pkg/langserver/cache.go +++ b/pkg/langserver/cache.go @@ -5,7 +5,7 @@ import ( "sync" "github.com/dbaumgarten/yodk/pkg/lsp" - "github.com/dbaumgarten/yodk/pkg/nolol/nast" + "github.com/dbaumgarten/yodk/pkg/nolol" ) var NotFoundError = fmt.Errorf("File not found in cache") @@ -17,9 +17,8 @@ type Cache struct { } type DiagnosticResults struct { - Macros map[string]*nast.MacroDefinition - Definitions map[string]*nast.Definition - Variables []string + Variables []string + AnalysisReport *nolol.AnalysisReport } func NewCache() *Cache { diff --git a/pkg/langserver/completions.go b/pkg/langserver/completions.go index 64721f4..504aefe 100644 --- a/pkg/langserver/completions.go +++ b/pkg/langserver/completions.go @@ -1,74 +1,98 @@ package langserver import ( + "strconv" "strings" "github.com/dbaumgarten/yodk/pkg/lsp" "github.com/dbaumgarten/yodk/pkg/parser/ast" ) -var completionItemGroups = map[string][]string{ - "unaryOps": {"abs", "sqrt", "sin", "cos", "tan", "asin", "acos", "atan", "not"}, - "keywords": {"if", "then", "else", "end", "goto"}, - "binaryOps": {"and", "or"}, -} +func (s *LangServer) GetCompletions(params *lsp.CompletionParams) (*lsp.CompletionList, error) { + var items []lsp.CompletionItem + + if strings.HasSuffix(string(params.TextDocument.URI), ".yolol") { + items = make([]lsp.CompletionItem, 0, len(DefaultYololCompletions)+15) + items = append(items, DefaultYololCompletions...) + items = append(items, s.getVariableCompletions(params)...) + } else if strings.HasSuffix(string(params.TextDocument.URI), ".nolol") { + items = make([]lsp.CompletionItem, 0, len(DefaultNololCompletions)+30) + items = append(items, DefaultNololCompletions...) + items = append(items, s.getNololCompletions(params)...) + } else { + return nil, nil + } -// see here: https://microsoft.github.io/language-server-protocol/specifications/specification-current/ -var completionItemTypes = map[string]float64{ - "unaryOps": 24, - "keywords": 14, - "binaryOps": 24, + return &lsp.CompletionList{ + IsIncomplete: false, + Items: items, + }, nil } -var completionItemDocs = map[string]string{ - "abs": "abs X: Returns the absolute value of X", - "sqrt": "sqrt X: Returns the square-root of X", - "sin": "sin X: Return the sine (degree) of X", - "cos": "cos X: Return the cosine (degree) of X", - "tan": "sin X: Return the tangent (degree) of X", - "asin": "asin X: Return the inverse sine (degree) of X", - "acos": "asin X: Return the inverse cosine (degree) of X", - "atan": "asin X: Return the inverse tanget (degree) of X", - "not": "not X: Returns 1 if X is 0, otherwise it returns 0", - "and": "X and Y: Returns true if X and Y are true", - "or": "X or Y: Returns true if X or Y are true", -} +func (s *LangServer) getNololCompletions(params *lsp.CompletionParams) []lsp.CompletionItem { + diags, err := s.cache.GetDiagnostics(params.TextDocument.URI) + if err != nil { + return []lsp.CompletionItem{} + } -func buildDefaultCompletionItems() []lsp.CompletionItem { - items := make([]lsp.CompletionItem, 0, 50) - for k, v := range completionItemGroups { - kind := completionItemTypes[k] - for _, str := range v { - docs, hasDocs := completionItemDocs[str] - item := lsp.CompletionItem{ - Label: str, - Kind: kind, - } - if hasDocs { - item.Detail = docs - } - items = append(items, item) - } + analysis := diags.AnalysisReport + if analysis == nil { + return []lsp.CompletionItem{} } - return items -} -var defaultCompletionItems []lsp.CompletionItem + items := make([]lsp.CompletionItem, len(analysis.Variables)+len(analysis.Definitions)+len(analysis.Macros)) -func init() { - defaultCompletionItems = buildDefaultCompletionItems() -} + for _, v := range analysis.GetVarsAtLine(int(params.Position.Line) + 1) { + items = append(items, lsp.CompletionItem{ + Label: v, + Kind: 6, + }) + } -func (s *LangServer) GetCompletions(params *lsp.CompletionParams) (*lsp.CompletionList, error) { - items := make([]lsp.CompletionItem, 0, len(defaultCompletionItems)+15) + for _, m := range analysis.Macros { + item := lsp.CompletionItem{ + Detail: m.Name + "(" + strings.Join(m.Arguments, ",") + ")", + Label: m.Name, + Kind: 15, + InsertText: m.Name + argsToSnippet(m.Arguments), + InsertTextFormat: 2, + } + if doc, exists := analysis.Docstrings[m.Name]; exists { + item.Documentation = doc + } + items = append(items, item) + } - items = append(items, defaultCompletionItems...) - items = append(items, s.getVariableCompletions(params)...) + for _, d := range analysis.Definitions { + kind := 21.0 + insert := "" + detail := "" + if len(d.Placeholders) > 0 { + kind = 3.0 + detail += d.Name + "(" + strings.Join(d.Placeholders, ",") + ")" + insert = d.Name + argsToSnippet(d.Placeholders) + } + item := lsp.CompletionItem{ + Label: d.Name, + Kind: kind, + InsertText: insert, + Detail: detail, + InsertTextFormat: 2, + } + if doc, exists := analysis.Docstrings[d.Name]; exists { + item.Documentation = doc + } + items = append(items, item) + } - return &lsp.CompletionList{ - IsIncomplete: true, - Items: items, - }, nil + for _, l := range analysis.Labels { + items = append(items, lsp.CompletionItem{ + Label: l, + Kind: 13, + }) + } + + return items } func (s *LangServer) getVariableCompletions(params *lsp.CompletionParams) []lsp.CompletionItem { @@ -115,3 +139,15 @@ func findUsedVariables(prog *ast.Program) []string { return vars } + +func argsToSnippet(args []string) string { + snip := "(" + for i, arg := range args { + snip += "${" + strconv.Itoa(i+1) + ":" + arg + "}" + if i != len(args)-1 { + snip += "," + } + } + snip += ")$0" + return snip +} diff --git a/pkg/langserver/completions_defaults.go b/pkg/langserver/completions_defaults.go new file mode 100644 index 0000000..8d1d503 --- /dev/null +++ b/pkg/langserver/completions_defaults.go @@ -0,0 +1,245 @@ +package langserver + +import "github.com/dbaumgarten/yodk/pkg/lsp" + +// DefaultYololCompletions contains completion-items for yolol-builtins +var DefaultYololCompletions = []lsp.CompletionItem{ + { + Label: "if", + Kind: 14, + }, + { + Label: "then", + Kind: 14, + }, + { + Label: "else", + Kind: 14, + }, + { + Label: "end", + Kind: 14, + }, + { + Label: "goto", + Kind: 14, + }, + { + Label: "not", + Detail: "not X", + Kind: 24, + Documentation: "Returns 1 if X is 0, otherwise it returns 0", + }, + { + Label: "and", + Detail: "X and Y", + Kind: 24, + Documentation: "Returns true if X and Y are true", + }, + { + Label: "or", + Detail: "X or Y", + Kind: 24, + Documentation: "Returns true if X or Y are true", + }, + + { + Label: "abs", + Detail: "abs X", + Kind: 3, + Documentation: "Returns the absolute value of X", + }, + { + Label: "sqrt", + Detail: "sqrt X", + Kind: 3, + Documentation: "Returns the square-root of X", + }, + { + Label: "sin", + Detail: "sin X", + Kind: 3, + Documentation: "Returns the sine (degree) of X", + }, + { + Label: "cos", + Detail: "cos X", + Kind: 3, + Documentation: "Returns the cosine (degree) of X", + }, + { + Label: "tan", + Detail: "tan X", + Kind: 3, + Documentation: "Returns the tangent (degree) of X", + }, + { + Label: "asin", + Detail: "asin X", + Kind: 3, + Documentation: "Returns the inverse sine (degree) of X", + }, + { + Label: "acos", + Detail: "acos X", + Kind: 3, + Documentation: "Returns the inverse cosine (degree) of X", + }, + { + Label: "atan", + Detail: "atan X", + Kind: 3, + Documentation: "Returns the inverse tangent (degree) of X", + }, +} + +// DefaultNololCompletions contains completion-items for nolol-builtins +var DefaultNololCompletions = []lsp.CompletionItem{ + { + Label: "if", + Kind: 14, + }, + { + Label: "then", + Kind: 14, + }, + { + Label: "else", + Kind: 14, + }, + { + Label: "end", + Kind: 14, + }, + { + Label: "goto", + Kind: 14, + }, + { + Label: "while", + Kind: 14, + }, + { + Label: "do", + Kind: 14, + }, + { + Label: "continue", + Kind: 14, + }, + { + Label: "break", + Kind: 14, + }, + { + Label: "wait", + Kind: 14, + }, + { + Label: "define", + Kind: 14, + }, + { + Label: "include", + Kind: 14, + }, + { + Label: "insert", + Kind: 14, + }, + { + Label: "macro", + Kind: 14, + }, + { + Label: "_if", + Kind: 14, + }, + { + Label: "_goto", + Kind: 14, + }, + { + Label: "not", + Detail: "not X", + Kind: 24, + Documentation: "Returns 1 if X is 0, otherwise it returns 0", + }, + { + Label: "and", + Detail: "X and Y", + Kind: 24, + Documentation: "Returns true if X and Y are true", + }, + { + Label: "or", + Detail: "X or Y", + Kind: 24, + Documentation: "Returns true if X or Y are true", + }, + + { + Label: "abs", + Detail: "abs(X)", + InsertText: "abs(${1:x})$0", + InsertTextFormat: 2, + Kind: 3, + Documentation: "Returns the absolute value of X", + }, + { + Label: "sqrt", + Detail: "sqrt(X)", + InsertText: "sqrt(${1:x})$0", + InsertTextFormat: 2, + Kind: 3, + Documentation: "Returns the square-root of X", + }, + { + Label: "sin", + Detail: "sin(X)", + InsertText: "sin(${1:x})$0", + InsertTextFormat: 2, + Kind: 3, + Documentation: "Returns the sine (degree) of X", + }, + { + Label: "cos", + Detail: "cos(X)", + InsertText: "cos(${1:x})$0", + InsertTextFormat: 2, + Kind: 3, + Documentation: "Returns the cosine (degree) of X", + }, + { + Label: "tan", + Detail: "tan(X)", + InsertText: "tan(${1:x})$0", + InsertTextFormat: 2, + Kind: 3, + Documentation: "Returns the tangent (degree) of X", + }, + { + Label: "asin", + Detail: "asin(X)", + InsertText: "asin(${1:x})$0", + InsertTextFormat: 2, + Kind: 3, + Documentation: "Returns the inverse sine (degree) of X", + }, + { + Label: "acos", + Detail: "acos(X)", + InsertText: "acos(${1:x})$0", + InsertTextFormat: 2, + Kind: 3, + Documentation: "Returns the inverse cosine (degree) of X", + }, + { + Label: "atan", + Detail: "atan(X)", + InsertText: "atan(${1:x})$0", + InsertTextFormat: 2, + Kind: 3, + Documentation: "Returns the inverse tangent (degree) of X", + }, +} diff --git a/pkg/langserver/diagnostics.go b/pkg/langserver/diagnostics.go index 66f368a..6638409 100644 --- a/pkg/langserver/diagnostics.go +++ b/pkg/langserver/diagnostics.go @@ -166,8 +166,11 @@ func (s *LangServer) Diagnose(ctx context.Context, uri lsp.DocumentURI) { conv := nolol.NewConverter() mainfile := string(uri) _, errs = conv.ConvertFileEx(mainfile, newfs(s, uri)) - diagRes.Definitions = conv.GetDefinitions() - diagRes.Macros = conv.GetMacros() + + analysis, err := nolol.AnalyseFileEx(mainfile, newfs(s, uri)) + if err == nil { + diagRes.AnalysisReport = analysis + } } else { return diff --git a/pkg/nolol/analyzer.go b/pkg/nolol/analyzer.go new file mode 100644 index 0000000..3c60635 --- /dev/null +++ b/pkg/nolol/analyzer.go @@ -0,0 +1,206 @@ +package nolol + +import ( + "path/filepath" + "strings" + + "github.com/dbaumgarten/yodk/pkg/nolol/nast" + "github.com/dbaumgarten/yodk/pkg/parser/ast" +) + +// AnalysisReport contains collected information about a nolol-programm +type AnalysisReport struct { + Definitions map[string]*nast.Definition + Macros map[string]*nast.MacroDefinition + Variables []string + Labels []string + Docstrings map[string]string +} + +// AnalyseFile returns an AnalysisReport for the given file +func AnalyseFile(mainfile string) (*AnalysisReport, error) { + files := DiskFileSystem{ + Dir: filepath.Dir(mainfile), + } + return AnalyseFileEx(filepath.Base(mainfile), files) +} + +// AnalyseFileEx returns an AnalysisReport for the given file +func AnalyseFileEx(mainfile string, files FileSystem) (*AnalysisReport, error) { + file, err := files.Get(mainfile) + if err != nil { + return nil, err + } + p := NewParser() + parsed, err := p.Parse(file) + if err != nil { + return nil, err + } + return Analyse(parsed, files) +} + +// Analyse returns an AnalysisReport for the given program +func Analyse(prog *nast.Program, files FileSystem) (*AnalysisReport, error) { + res := &AnalysisReport{ + Definitions: make(map[string]*nast.Definition), + Macros: make(map[string]*nast.MacroDefinition), + Docstrings: make(map[string]string), + Labels: make([]string, 0), + } + + includecount := 0 + prevDocstrings := "" + + vars := make(map[string]bool) + + f := func(node ast.Node, visitType int) error { + switch n := node.(type) { + case *nast.Definition: + res.Definitions[n.Name] = n + if prevDocstrings != "" { + res.Docstrings[n.Name] = prevDocstrings + } + return ast.NewNodeReplacementSkip() + case *nast.MacroDefinition: + res.Macros[n.Name] = n + if prevDocstrings != "" { + res.Docstrings[n.Name] = prevDocstrings + } + return ast.NewNodeReplacementSkip() + case *ast.Assignment: + if _, isDef := res.Definitions[n.Variable]; !isDef { + vars[n.Variable] = true + } + return nil + case *ast.Dereference: + if _, isDef := res.Definitions[n.Variable]; !isDef { + vars[n.Variable] = true + } + return nil + case *nast.IncludeDirective: + return convertInclude(n, files, &includecount) + case *nast.StatementLine: + if visitType == ast.PostVisit { + if isDocsLine(n) { + prevDocstrings += strings.TrimLeft(n.Comment, "/ \t") + "\n" + } else { + prevDocstrings = "" + } + if n.Label != "" { + res.Labels = append(res.Labels, n.Label) + } + } + case *nast.FuncCall: + if visitType == ast.PreVisit && n.Function == "line" { + n.Arguments = []ast.Expression{} + } + default: + //prevDocstrings = "" + } + return nil + } + + err := prog.Accept(ast.VisitorFunc(f)) + if err != nil { + return nil, err + } + + res.Variables = make([]string, 0, len(vars)) + for k := range vars { + res.Variables = append(res.Variables, k) + } + + return res, err +} + +// GetMacroLocalVars returns the local variables for the given macro +func (a AnalysisReport) GetMacroLocalVars(mac *nast.MacroDefinition) []string { + variables := make(map[string]bool) + f := func(node ast.Node, visitType int) error { + if assign, is := node.(*ast.Assignment); visitType == ast.PreVisit && is { + variables[assign.Variable] = true + } + if deref, is := node.(*ast.Dereference); visitType == ast.SingleVisit && is { + variables[deref.Variable] = true + } + return nil + } + err := mac.Accept(ast.VisitorFunc(f)) + if err != nil { + panic(err) + } + + for _, arg := range mac.Arguments { + variables[arg] = true + } + + for _, arg := range mac.Externals { + variables[arg] = true + } + + vars := make([]string, 0, len(variables)) + for k := range variables { + vars = append(vars, k) + } + + return vars +} + +// GetDefinitionLocalVars returns the placeholders for the given Definition +func (a AnalysisReport) GetDefinitionLocalVars(def *nast.Definition) []string { + return def.Placeholders +} + +// GetVarsAtLine returns all variables that are in scope at the given line +func (a AnalysisReport) GetVarsAtLine(line int) []string { + vars := make([]string, 0, len(a.Variables)+10) + for _, def := range a.Definitions { + if def.Start().Line == line { + vars = append(vars, a.Variables...) + vars = append(vars, a.GetDefinitionLocalVars(def)...) + return vars + } + } + for _, mac := range a.Macros { + if mac.Start().Line < line && mac.End().Line >= line { + vars = append(vars, a.GetMacroLocalVars(mac)...) + return vars + } + } + vars = append(vars, a.Variables...) + return vars +} + +// checks if the given line consist purely of a comment +func isDocsLine(thisStmtLine *nast.StatementLine) bool { + if thisStmtLine.Comment != "" && len(thisStmtLine.Statements) == 0 && !thisStmtLine.HasBOL && !thisStmtLine.HasEOL { + return true + } + return false +} + +// TODO: reduce code-duplication between here and the converter +func convertInclude(include *nast.IncludeDirective, files FileSystem, count *int) error { + p := NewParser() + + *count++ + if *count > 20 { + return nil + } + + file, err := files.Get(include.File) + if err != nil { + return nil + } + p.SetFilename(include.File) + parsed, err := p.Parse(file) + if err != nil { + return nil + } + + replacements := make([]ast.Node, len(parsed.Elements)) + for i := range parsed.Elements { + replacements[i] = parsed.Elements[i] + } + return ast.NewNodeReplacement(replacements...) +} diff --git a/pkg/nolol/converter.go b/pkg/nolol/converter.go index bb4d845..28b0805 100644 --- a/pkg/nolol/converter.go +++ b/pkg/nolol/converter.go @@ -57,16 +57,6 @@ func (c *Converter) GetVariableTranslations() map[string]string { return c.varnameOptimizer.GetReversalTable() } -// GetDefinitions returns all definitions found during parsing -func (c *Converter) GetDefinitions() map[string]*nast.Definition { - return c.definitions -} - -// GetMacros returns all macros found during parsing -func (c *Converter) GetMacros() map[string]*nast.MacroDefinition { - return c.macros -} - // ConvertFile is a shortcut that loads a file from the file-system, parses it and directly convertes it. // mainfile is the path to the file on the disk. // All included are loaded relative to the mainfile. @@ -333,10 +323,10 @@ func (c *Converter) mergeStatementElements(lines []*nast.StatementLine) ([]*nast current := &nast.StatementLine{ Line: ast.Line{ Statements: []ast.Statement{}, + Position: lines[i].Position, }, - Label: lines[i].Label, - Position: lines[i].Position, - HasEOL: lines[i].HasEOL, + Label: lines[i].Label, + HasEOL: lines[i].HasEOL, } current.Statements = append(current.Statements, lines[i].Statements...) newElements = append(newElements, current) diff --git a/pkg/nolol/converter_gotos.go b/pkg/nolol/converter_gotos.go index 8f9af88..2788a47 100644 --- a/pkg/nolol/converter_gotos.go +++ b/pkg/nolol/converter_gotos.go @@ -193,7 +193,6 @@ func (c *Converter) addFinalGoto(prog *nast.Program) error { } prog.Elements = append(prog.Elements, &nast.StatementLine{ - Position: pos, Line: ast.Line{ Position: pos, Statements: []ast.Statement{ diff --git a/pkg/nolol/converter_ifs.go b/pkg/nolol/converter_ifs.go index 3b0298b..0243ce7 100644 --- a/pkg/nolol/converter_ifs.go +++ b/pkg/nolol/converter_ifs.go @@ -47,9 +47,9 @@ func (c *Converter) convertIf(mlif *nast.MultilineIf, visitType int) error { } repl = append(repl, &nast.StatementLine{ - Position: mlif.End(), - Label: endif, + Label: endif, Line: ast.Line{ + Position: mlif.End(), Statements: []ast.Statement{}, }, }) @@ -87,8 +87,8 @@ func (c *Converter) convertIfTrivial(mlif *nast.MultilineIf) (ast.Node, error) { } repl := &nast.StatementLine{ - Position: mlif.Positions[0], Line: ast.Line{ + Position: mlif.Positions[0], Statements: []ast.Statement{ &ast.IfStatement{ Position: mlif.Positions[0], @@ -133,8 +133,8 @@ func (c *Converter) convertConditionInline(mlif *nast.MultilineIf, index int, en } repl := &nast.StatementLine{ - Position: mlif.Positions[index], Line: ast.Line{ + Position: mlif.Positions[index], Statements: []ast.Statement{ &ast.IfStatement{ Position: mlif.Positions[index], @@ -165,8 +165,8 @@ func (c *Converter) convertConditionMultiline(mlif *nast.MultilineIf, index int, }) repl := []ast.Node{ &nast.StatementLine{ - Position: mlif.Positions[index], Line: ast.Line{ + Position: mlif.Positions[index], Statements: []ast.Statement{ &ast.IfStatement{ Position: mlif.Positions[index], @@ -189,7 +189,6 @@ func (c *Converter) convertConditionMultiline(mlif *nast.MultilineIf, index int, if endlabel != "" { repl = append(repl, &nast.StatementLine{ - Position: mlif.Blocks[index].End(), Line: ast.Line{ Position: mlif.Blocks[index].End(), Statements: []ast.Statement{ @@ -203,9 +202,9 @@ func (c *Converter) convertConditionMultiline(mlif *nast.MultilineIf, index int, } repl = append(repl, &nast.StatementLine{ - Position: mlif.Blocks[index].End(), - Label: skipIf, + Label: skipIf, Line: ast.Line{ + Position: mlif.Blocks[index].End(), Statements: []ast.Statement{}, }, }) diff --git a/pkg/nolol/converter_loops.go b/pkg/nolol/converter_loops.go index e20bd4e..5b0d9f0 100644 --- a/pkg/nolol/converter_loops.go +++ b/pkg/nolol/converter_loops.go @@ -38,9 +38,9 @@ func (c *Converter) convertWhileLoop(loop *nast.WhileLoop, visitType int) error repl := []ast.Node{ &nast.StatementLine{ - Position: loop.Position, - Label: startLabel, + Label: startLabel, Line: ast.Line{ + Position: loop.Position, Statements: []ast.Statement{}, }, }, @@ -83,8 +83,8 @@ func (c *Converter) convertWhileLoop(loop *nast.WhileLoop, visitType int) error repl = append(repl, blockline) } repl = append(repl, &nast.StatementLine{ - Position: loop.Block.End(), Line: ast.Line{ + Position: loop.Block.End(), Statements: []ast.Statement{ &nast.GoToLabelStatement{ Position: loop.Block.End(), @@ -95,9 +95,9 @@ func (c *Converter) convertWhileLoop(loop *nast.WhileLoop, visitType int) error }) repl = append(repl, &nast.StatementLine{ - Position: loop.Position, - Label: endLabel, + Label: endLabel, Line: ast.Line{ + Position: loop.Position, Statements: []ast.Statement{}, }, }) diff --git a/pkg/nolol/nast/ast.go b/pkg/nolol/nast/ast.go index af731ce..efb28c6 100644 --- a/pkg/nolol/nast/ast.go +++ b/pkg/nolol/nast/ast.go @@ -45,15 +45,9 @@ type StatementLine struct { // If true, do not append this line to any other line during conversion to yolol HasBOL bool // If true, no other lines may be appended to this line during conversion to yolol - HasEOL bool - Label string - Position ast.Position - Comment string -} - -// Start is needed to implement ast.Node -func (n *StatementLine) Start() ast.Position { - return n.Position + HasEOL bool + Label string + Comment string } // El implements the type-marker method @@ -205,7 +199,7 @@ func (n *WaitDirective) Start() ast.Position { // End is needed to implement ast.Node func (n *WaitDirective) End() ast.Position { - if n.Statements == nil || len(n.Statements) == 0 { + if len(n.Statements) == 0 { if n.Condition == nil { return n.Position } diff --git a/pkg/nolol/parser.go b/pkg/nolol/parser.go index 0ae8c0f..821e8c9 100644 --- a/pkg/nolol/parser.go +++ b/pkg/nolol/parser.go @@ -233,9 +233,9 @@ func (p *Parser) ParseStatementLine() *nast.StatementLine { p.Log() ret := nast.StatementLine{ Line: ast.Line{ + Position: p.CurrentToken.Position, Statements: make([]ast.Statement, 0, 1), }, - Position: p.CurrentToken.Position, } // get line-label if it exists