mirror of
https://github.com/go-sylixos/elvish.git
synced 2024-12-14 02:57:52 +08:00
pkg/parse: Turn most public methods of Node into functions.
This make the godoc of the parse package much cleaner and removes some boilerplate.
This commit is contained in:
parent
4b93d4579d
commit
21f5a6f7c3
|
@ -8,6 +8,8 @@ import (
|
|||
"github.com/elves/elvish/pkg/parse"
|
||||
)
|
||||
|
||||
var parent = parse.Parent
|
||||
|
||||
var completers = []completer{
|
||||
completeCommand,
|
||||
completeIndex,
|
||||
|
@ -28,7 +30,7 @@ type context struct {
|
|||
func completeArg(n parse.Node, cfg Config) (*context, []RawItem, error) {
|
||||
ev := cfg.PureEvaler
|
||||
if sep, ok := n.(*parse.Sep); ok {
|
||||
if form, ok := sep.Parent().(*parse.Form); ok && form.Head != nil {
|
||||
if form, ok := parent(sep).(*parse.Form); ok && form.Head != nil {
|
||||
// Case 1: starting a new argument.
|
||||
ctx := &context{"argument", "", parse.Bareword, range0(n.Range().To)}
|
||||
args := purelyEvalForm(form, "", n.Range().To, ev)
|
||||
|
@ -38,7 +40,7 @@ func completeArg(n parse.Node, cfg Config) (*context, []RawItem, error) {
|
|||
}
|
||||
if primary, ok := n.(*parse.Primary); ok {
|
||||
if compound, seed := primaryInSimpleCompound(primary, ev); compound != nil {
|
||||
if form, ok := compound.Parent().(*parse.Form); ok {
|
||||
if form, ok := parent(compound).(*parse.Form); ok {
|
||||
if form.Head != nil && form.Head != compound {
|
||||
// Case 2: in an incomplete argument.
|
||||
ctx := &context{"argument", seed, primary.Type, compound.Range()}
|
||||
|
@ -67,7 +69,7 @@ func completeCommand(n parse.Node, cfg Config) (*context, []RawItem, error) {
|
|||
return generateForEmpty(n.Range().To)
|
||||
}
|
||||
if is(n, aSep) {
|
||||
parent := n.Parent()
|
||||
parent := parent(n)
|
||||
switch {
|
||||
case is(parent, aChunk), is(parent, aPipeline):
|
||||
// Case 2: Just after a newline, semicolon, or a pipe.
|
||||
|
@ -83,7 +85,7 @@ func completeCommand(n parse.Node, cfg Config) (*context, []RawItem, error) {
|
|||
|
||||
if primary, ok := n.(*parse.Primary); ok {
|
||||
if compound, seed := primaryInSimpleCompound(primary, ev); compound != nil {
|
||||
if form, ok := compound.Parent().(*parse.Form); ok {
|
||||
if form, ok := parent(compound).(*parse.Form); ok {
|
||||
if form.Head == compound {
|
||||
// Case 4: At an already started command.
|
||||
ctx := &context{
|
||||
|
@ -107,20 +109,20 @@ func completeIndex(n parse.Node, cfg Config) (*context, []RawItem, error) {
|
|||
}
|
||||
|
||||
if is(n, aSep) {
|
||||
if is(n.Parent(), aIndexing) {
|
||||
if is(parent(n), aIndexing) {
|
||||
// We are just after an opening bracket.
|
||||
indexing := n.Parent().(*parse.Indexing)
|
||||
indexing := parent(n).(*parse.Indexing)
|
||||
if len(indexing.Indicies) == 1 {
|
||||
if indexee := ev.PurelyEvalPrimary(indexing.Head); indexee != nil {
|
||||
return generateForEmpty(indexee, n.Range().To)
|
||||
}
|
||||
}
|
||||
}
|
||||
if is(n.Parent(), aArray) {
|
||||
array := n.Parent()
|
||||
if is(array.Parent(), aIndexing) {
|
||||
if is(parent(n), aArray) {
|
||||
array := parent(n)
|
||||
if is(parent(array), aIndexing) {
|
||||
// We are after an existing index and spaces.
|
||||
indexing := array.Parent().(*parse.Indexing)
|
||||
indexing := parent(array).(*parse.Indexing)
|
||||
if len(indexing.Indicies) == 1 {
|
||||
if indexee := ev.PurelyEvalPrimary(indexing.Head); indexee != nil {
|
||||
return generateForEmpty(indexee, n.Range().To)
|
||||
|
@ -134,11 +136,11 @@ func completeIndex(n parse.Node, cfg Config) (*context, []RawItem, error) {
|
|||
primary := n.(*parse.Primary)
|
||||
compound, seed := primaryInSimpleCompound(primary, ev)
|
||||
if compound != nil {
|
||||
if is(compound.Parent(), aArray) {
|
||||
array := compound.Parent()
|
||||
if is(array.Parent(), aIndexing) {
|
||||
if is(parent(compound), aArray) {
|
||||
array := parent(compound)
|
||||
if is(parent(array), aIndexing) {
|
||||
// We are just after an incomplete index.
|
||||
indexing := array.Parent().(*parse.Indexing)
|
||||
indexing := parent(array).(*parse.Indexing)
|
||||
if len(indexing.Indicies) == 1 {
|
||||
if indexee := ev.PurelyEvalPrimary(indexing.Head); indexee != nil {
|
||||
ctx := &context{
|
||||
|
@ -156,7 +158,7 @@ func completeIndex(n parse.Node, cfg Config) (*context, []RawItem, error) {
|
|||
func completeRedir(n parse.Node, cfg Config) (*context, []RawItem, error) {
|
||||
ev := cfg.PureEvaler
|
||||
if is(n, aSep) {
|
||||
if is(n.Parent(), aRedir) {
|
||||
if is(parent(n), aRedir) {
|
||||
// Empty redirection target.
|
||||
ctx := &context{"redir", "", parse.Bareword, range0(n.Range().To)}
|
||||
items, err := generateFileNames("", false)
|
||||
|
@ -165,7 +167,7 @@ func completeRedir(n parse.Node, cfg Config) (*context, []RawItem, error) {
|
|||
}
|
||||
if primary, ok := n.(*parse.Primary); ok {
|
||||
if compound, seed := primaryInSimpleCompound(primary, ev); compound != nil {
|
||||
if is(compound.Parent(), &parse.Redir{}) {
|
||||
if is(parent(compound), &parse.Redir{}) {
|
||||
// Non-empty redirection target.
|
||||
ctx := &context{
|
||||
"redir", seed, primary.Type, compound.Range()}
|
||||
|
|
|
@ -25,11 +25,11 @@ var (
|
|||
)
|
||||
|
||||
func primaryInSimpleCompound(pn *parse.Primary, ev PureEvaler) (*parse.Compound, string) {
|
||||
indexing, ok := pn.Parent().(*parse.Indexing)
|
||||
indexing, ok := parent(pn).(*parse.Indexing)
|
||||
if !ok {
|
||||
return nil, ""
|
||||
}
|
||||
compound, ok := indexing.Parent().(*parse.Compound)
|
||||
compound, ok := parent(indexing).(*parse.Compound)
|
||||
if !ok {
|
||||
return nil, ""
|
||||
}
|
||||
|
|
|
@ -19,7 +19,7 @@ func initHighlighter(appSpec *cli.AppSpec, ev *eval.Evaler) {
|
|||
}
|
||||
|
||||
func check(ev *eval.Evaler, n *parse.Chunk) error {
|
||||
src := &parse.Source{Name: "[tty]", Code: n.SourceText()}
|
||||
src := &parse.Source{Name: "[tty]", Code: parse.SourceText(n)}
|
||||
_, err := ev.Compile(n, src)
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -7,6 +7,8 @@ import (
|
|||
"github.com/elves/elvish/pkg/parse"
|
||||
)
|
||||
|
||||
var sourceText = parse.SourceText
|
||||
|
||||
// Represents a region to be highlighted.
|
||||
type region struct {
|
||||
begin int
|
||||
|
@ -106,7 +108,7 @@ func emitRegions(n parse.Node, f func(parse.Node, regionKind, string)) {
|
|||
case *parse.Sep:
|
||||
emitRegionsInSep(n, f)
|
||||
}
|
||||
for _, child := range n.Children() {
|
||||
for _, child := range parse.Children(n) {
|
||||
emitRegions(child, f)
|
||||
}
|
||||
}
|
||||
|
@ -137,7 +139,7 @@ func emitRegionsInForm(n *parse.Form, f func(parse.Node, regionKind, string)) {
|
|||
// TODO: This only highlights bareword special commands, however currently
|
||||
// quoted special commands are also possible (e.g `"if" $true { }` is
|
||||
// accepted).
|
||||
switch n.Head.SourceText() {
|
||||
switch sourceText(n.Head) {
|
||||
case "if":
|
||||
emitRegionsInIf(n, f)
|
||||
case "for":
|
||||
|
@ -155,7 +157,7 @@ func emitRegionsInIf(n *parse.Form, f func(parse.Node, regionKind, string)) {
|
|||
// Highlight all "elif" and "else".
|
||||
for i := 2; i < len(n.Args); i += 2 {
|
||||
arg := n.Args[i]
|
||||
if arg.SourceText() == "elif" || arg.SourceText() == "else" {
|
||||
if s := sourceText(arg); s == "elif" || s == "else" {
|
||||
f(arg, semanticRegion, keywordRegion)
|
||||
}
|
||||
}
|
||||
|
@ -167,7 +169,7 @@ func emitRegionsInFor(n *parse.Form, f func(parse.Node, regionKind, string)) {
|
|||
f(n.Args[0].Indexings[0].Head, semanticRegion, variableRegion)
|
||||
}
|
||||
// Highlight "else".
|
||||
if 3 < len(n.Args) && n.Args[3].SourceText() == "else" {
|
||||
if 3 < len(n.Args) && sourceText(n.Args[3]) == "else" {
|
||||
f(n.Args[3], semanticRegion, keywordRegion)
|
||||
}
|
||||
}
|
||||
|
@ -177,7 +179,7 @@ func emitRegionsInTry(n *parse.Form, f func(parse.Node, regionKind, string)) {
|
|||
// "finally".
|
||||
i := 1
|
||||
matchKW := func(text string) bool {
|
||||
if i < len(n.Args) && n.Args[i].SourceText() == text {
|
||||
if i < len(n.Args) && sourceText(n.Args[i]) == text {
|
||||
f(n.Args[i], semanticRegion, keywordRegion)
|
||||
return true
|
||||
}
|
||||
|
@ -213,7 +215,7 @@ func emitRegionsInPrimary(n *parse.Primary, f func(parse.Node, regionKind, strin
|
|||
}
|
||||
|
||||
func emitRegionsInSep(n *parse.Sep, f func(parse.Node, regionKind, string)) {
|
||||
text := n.SourceText()
|
||||
text := sourceText(n)
|
||||
switch {
|
||||
case strings.TrimSpace(text) == "":
|
||||
// Don't do anything; whitespaces do not get highlighted.
|
||||
|
|
|
@ -40,7 +40,7 @@ func (aw *argsWalker) next() *parse.Compound {
|
|||
// nextIs returns whether the next argument's source matches the given text. It
|
||||
// also consumes the argument if it is.
|
||||
func (aw *argsWalker) nextIs(text string) bool {
|
||||
if aw.more() && aw.form.Args[aw.idx].SourceText() == text {
|
||||
if aw.more() && parse.SourceText(aw.form.Args[aw.idx]) == text {
|
||||
aw.idx++
|
||||
return true
|
||||
}
|
||||
|
|
|
@ -564,7 +564,7 @@ func compileTry(cp *compiler, fn *parse.Form) effectOpBody {
|
|||
logger.Println("compiling try")
|
||||
args := cp.walkArgs(fn)
|
||||
bodyNode := args.nextMustLambda()
|
||||
logger.Printf("body is %q", bodyNode.SourceText())
|
||||
logger.Printf("body is %q", parse.SourceText(bodyNode))
|
||||
var exceptVarNode *parse.Indexing
|
||||
var exceptNode *parse.Primary
|
||||
if args.nextIs("except") {
|
||||
|
|
|
@ -50,7 +50,7 @@ func (cp *compiler) pipelineOp(n *parse.Pipeline) effectOp {
|
|||
cp.newLocals = saveNewLocals
|
||||
|
||||
return makeEffectOp(n,
|
||||
&pipelineOp{n.Background, n.SourceText(), formOps, newLocals})
|
||||
&pipelineOp{n.Background, parse.SourceText(n), formOps, newLocals})
|
||||
}
|
||||
|
||||
func (cp *compiler) pipelineOps(ns []*parse.Pipeline) []effectOp {
|
||||
|
|
|
@ -255,7 +255,7 @@ func (cp *compiler) primaryOp(n *parse.Primary) valuesOp {
|
|||
}
|
||||
body = &variableOp{sigil != "", qname}
|
||||
case parse.Wildcard:
|
||||
seg, err := wildcardToSegment(n.SourceText())
|
||||
seg, err := wildcardToSegment(parse.SourceText(n))
|
||||
if err != nil {
|
||||
cp.errorpf(n, "%s", err)
|
||||
}
|
||||
|
@ -279,7 +279,7 @@ func (cp *compiler) primaryOp(n *parse.Primary) valuesOp {
|
|||
body = cp.braced(n)
|
||||
default:
|
||||
cp.errorpf(n, "bad PrimaryType; parser bug")
|
||||
body = literalStr(n.SourceText())
|
||||
body = literalStr(parse.SourceText(n))
|
||||
}
|
||||
return makeValuesOp(n, body)
|
||||
}
|
||||
|
|
|
@ -59,7 +59,7 @@ func checkAST(n Node, want ast) error {
|
|||
if i == len(wantnames)-1 {
|
||||
break
|
||||
}
|
||||
fields := n.Children()
|
||||
fields := Children(n)
|
||||
if len(fields) != 1 {
|
||||
return fmt.Errorf("want exactly 1 child, got %d (%s)", len(fields), summary(n))
|
||||
}
|
||||
|
@ -134,7 +134,7 @@ func checkField(got interface{}, want interface{}, ctx string) error {
|
|||
func checkNodeInField(got Node, want interface{}) error {
|
||||
switch want := want.(type) {
|
||||
case string:
|
||||
text := got.SourceText()
|
||||
text := SourceText(got)
|
||||
if want != text {
|
||||
return fmt.Errorf("want %q, got %q (%s)", want, text, summary(got))
|
||||
}
|
||||
|
|
|
@ -4,14 +4,14 @@ import "fmt"
|
|||
|
||||
// checkParseTree checks whether the parse tree part of a Node is well-formed.
|
||||
func checkParseTree(n Node) error {
|
||||
children := n.Children()
|
||||
children := Children(n)
|
||||
if len(children) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Parent pointers of all children should point to me.
|
||||
for i, ch := range children {
|
||||
if ch.Parent() != n {
|
||||
if Parent(ch) != n {
|
||||
return fmt.Errorf("parent of child %d (%s) is wrong: %s", i, summary(ch), summary(n))
|
||||
}
|
||||
}
|
||||
|
@ -33,7 +33,7 @@ func checkParseTree(n Node) error {
|
|||
}
|
||||
|
||||
// Check children recursively.
|
||||
for _, ch := range n.Children() {
|
||||
for _, ch := range Children(n) {
|
||||
err := checkParseTree(ch)
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
|
@ -4,18 +4,9 @@ import "github.com/elves/elvish/pkg/diag"
|
|||
|
||||
// Node represents a parse tree as well as an AST.
|
||||
type Node interface {
|
||||
parse(*parser)
|
||||
|
||||
diag.Ranger
|
||||
SourceText() string
|
||||
Parent() Node
|
||||
Children() []Node
|
||||
|
||||
setFrom(int)
|
||||
setTo(int)
|
||||
setSourceText(string)
|
||||
setParent(Node)
|
||||
addChild(Node)
|
||||
parse(*parser)
|
||||
n() *node
|
||||
}
|
||||
|
||||
type node struct {
|
||||
|
@ -25,34 +16,19 @@ type node struct {
|
|||
children []Node
|
||||
}
|
||||
|
||||
func (n *node) setFrom(begin int) { n.From = begin }
|
||||
|
||||
func (n *node) setTo(end int) { n.To = end }
|
||||
|
||||
func (n *node) setSourceText(source string) { n.sourceText = source }
|
||||
|
||||
func (n *node) setParent(p Node) { n.parent = p }
|
||||
func (n *node) n() *node { return n }
|
||||
|
||||
func (n *node) addChild(ch Node) { n.children = append(n.children, ch) }
|
||||
|
||||
// Parent returns the parent node. If the node is the root of the syntax tree,
|
||||
// the parent is nil.
|
||||
func (n *node) Parent() Node {
|
||||
return n.parent
|
||||
}
|
||||
// Range returns the range within the full source text that parses to the node.
|
||||
func (n *node) Range() diag.Ranging { return n.Ranging }
|
||||
|
||||
// Range returns the range within the original (full) source text that parses
|
||||
// to the node.
|
||||
func (n *node) Range() diag.Ranging {
|
||||
return diag.Ranging{n.From, n.To}
|
||||
}
|
||||
// Parent returns the parent of a node. It returns nil if the node is the root
|
||||
// of the parse tree.
|
||||
func Parent(n Node) Node { return n.n().parent }
|
||||
|
||||
// SourceText returns the part of the source text that parses to the node.
|
||||
func (n *node) SourceText() string {
|
||||
return n.sourceText
|
||||
}
|
||||
func SourceText(n Node) string { return n.n().sourceText }
|
||||
|
||||
// Children returns all children of the node in the parse tree.
|
||||
func (n *node) Children() []Node {
|
||||
return n.children
|
||||
}
|
||||
func Children(n Node) []Node { return n.n().children }
|
||||
|
|
|
@ -905,7 +905,7 @@ func (*Sep) parse(*parser) {
|
|||
|
||||
func addSep(n Node, ps *parser) {
|
||||
var begin int
|
||||
ch := n.Children()
|
||||
ch := Children(n)
|
||||
if len(ch) > 0 {
|
||||
begin = ch[len(ch)-1].Range().To
|
||||
} else {
|
||||
|
@ -971,6 +971,6 @@ func IsWhitespace(r rune) bool {
|
|||
}
|
||||
|
||||
func addChild(p Node, ch Node) {
|
||||
p.addChild(ch)
|
||||
ch.setParent(p)
|
||||
p.n().addChild(ch)
|
||||
ch.n().parent = p
|
||||
}
|
||||
|
|
|
@ -29,10 +29,10 @@ func newParser(srcname, src string) *parser {
|
|||
|
||||
func (ps *parser) parse(n Node) parsed {
|
||||
begin := ps.pos
|
||||
n.setFrom(begin)
|
||||
n.n().From = begin
|
||||
n.parse(ps)
|
||||
n.setTo(ps.pos)
|
||||
n.setSourceText(ps.src[begin:ps.pos])
|
||||
n.n().To = ps.pos
|
||||
n.n().sourceText = ps.src[begin:ps.pos]
|
||||
return parsed{n}
|
||||
}
|
||||
|
||||
|
|
|
@ -11,8 +11,8 @@ import (
|
|||
// position is out of bound.
|
||||
func FindLeafNode(n parse.Node, p int) parse.Node {
|
||||
descend:
|
||||
for len(n.Children()) > 0 {
|
||||
for _, ch := range n.Children() {
|
||||
for len(parse.Children(n)) > 0 {
|
||||
for _, ch := range parse.Children(n) {
|
||||
if ch.Range().From <= p && p <= ch.Range().To {
|
||||
n = ch
|
||||
continue descend
|
||||
|
@ -30,14 +30,14 @@ func Wordify(src string) []string {
|
|||
}
|
||||
|
||||
func wordifyInner(n parse.Node, words []string) []string {
|
||||
if len(n.Children()) == 0 || isCompound(n) {
|
||||
text := n.SourceText()
|
||||
if len(parse.Children(n)) == 0 || isCompound(n) {
|
||||
text := parse.SourceText(n)
|
||||
if strings.TrimFunc(text, parse.IsWhitespace) != "" {
|
||||
return append(words, text)
|
||||
}
|
||||
return words
|
||||
}
|
||||
for _, ch := range n.Children() {
|
||||
for _, ch := range parse.Children(n) {
|
||||
words = wordifyInner(ch, words)
|
||||
}
|
||||
return words
|
||||
|
|
|
@ -63,9 +63,9 @@ func pprintASTRec(n Node, wr io.Writer, indent int, leading string) {
|
|||
}
|
||||
|
||||
// has only one child and nothing more : coalesce
|
||||
if len(n.Children()) == 1 &&
|
||||
n.Children()[0].SourceText() == n.SourceText() {
|
||||
pprintASTRec(n.Children()[0], wr, indent, leading+nt.Name()+"/")
|
||||
if len(Children(n)) == 1 &&
|
||||
SourceText(Children(n)[0]) == SourceText(n) {
|
||||
pprintASTRec(Children(n)[0], wr, indent, leading+nt.Name()+"/")
|
||||
return
|
||||
}
|
||||
// print heading
|
||||
|
@ -113,19 +113,19 @@ func pprintParseTree(n Node, w io.Writer) {
|
|||
|
||||
func pprintParseTreeRec(n Node, wr io.Writer, indent int) {
|
||||
leading := ""
|
||||
for len(n.Children()) == 1 {
|
||||
for len(Children(n)) == 1 {
|
||||
leading += reflect.TypeOf(n).Elem().Name() + "/"
|
||||
n = n.Children()[0]
|
||||
n = Children(n)[0]
|
||||
}
|
||||
fmt.Fprintf(wr, "%*s%s%s\n", indent, "", leading, summary(n))
|
||||
for _, ch := range n.Children() {
|
||||
for _, ch := range Children(n) {
|
||||
pprintParseTreeRec(ch, wr, indent+indentInc)
|
||||
}
|
||||
}
|
||||
|
||||
func summary(n Node) string {
|
||||
return fmt.Sprintf("%s %s %d-%d", reflect.TypeOf(n).Elem().Name(),
|
||||
compactQuote(n.SourceText()), n.Range().From, n.Range().To)
|
||||
compactQuote(SourceText(n)), n.Range().From, n.Range().To)
|
||||
}
|
||||
|
||||
func compactQuote(text string) string {
|
||||
|
|
Loading…
Reference in New Issue
Block a user