mirror of
https://github.com/gonum/gonum.git
synced 2025-10-07 16:11:03 +08:00
161 lines
4.5 KiB
Go
161 lines
4.5 KiB
Go
// This file is dual licensed under CC0 and The gonum license.
|
|
//
|
|
// Copyright ©2017 The gonum Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
//
|
|
// Copyright ©2017 Robin Eklind.
|
|
// This file is made available under a Creative Commons CC0 1.0
|
|
// Universal Public Domain Dedication.
|
|
|
|
package dot
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"gonum.org/v1/gonum/graph/formats/dot/ast"
|
|
)
|
|
|
|
// check validates the semantics of the given DOT file.
|
|
func check(file *ast.File) error {
|
|
for _, graph := range file.Graphs {
|
|
// TODO: Check graph.ID for duplicates?
|
|
if err := checkGraph(graph); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// check validates the semantics of the given graph.
|
|
func checkGraph(graph *ast.Graph) error {
|
|
for _, stmt := range graph.Stmts {
|
|
if err := checkStmt(graph, stmt); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// check validates the semantics of the given statement.
|
|
func checkStmt(graph *ast.Graph, stmt ast.Stmt) error {
|
|
switch stmt := stmt.(type) {
|
|
case *ast.NodeStmt:
|
|
return checkNodeStmt(graph, stmt)
|
|
case *ast.EdgeStmt:
|
|
return checkEdgeStmt(graph, stmt)
|
|
case *ast.AttrStmt:
|
|
return checkAttrStmt(graph, stmt)
|
|
case *ast.Attr:
|
|
// TODO: Verify that the attribute is indeed of graph component kind.
|
|
return checkAttr(graph, ast.KindGraph, stmt)
|
|
case *ast.Subgraph:
|
|
return checkSubgraph(graph, stmt)
|
|
default:
|
|
panic(fmt.Sprintf("support for statement of type %T not yet implemented", stmt))
|
|
}
|
|
}
|
|
|
|
// checkNodeStmt validates the semantics of the given node statement.
|
|
func checkNodeStmt(graph *ast.Graph, stmt *ast.NodeStmt) error {
|
|
if err := checkNode(graph, stmt.Node); err != nil {
|
|
return err
|
|
}
|
|
for _, attr := range stmt.Attrs {
|
|
// TODO: Verify that the attribute is indeed of node component kind.
|
|
if err := checkAttr(graph, ast.KindNode, attr); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// checkEdgeStmt validates the semantics of the given edge statement.
|
|
func checkEdgeStmt(graph *ast.Graph, stmt *ast.EdgeStmt) error {
|
|
// TODO: if graph.Strict, check for multi-edges.
|
|
if err := checkVertex(graph, stmt.From); err != nil {
|
|
return err
|
|
}
|
|
for _, attr := range stmt.Attrs {
|
|
// TODO: Verify that the attribute is indeed of edge component kind.
|
|
if err := checkAttr(graph, ast.KindEdge, attr); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return checkEdge(graph, stmt.From, stmt.To)
|
|
}
|
|
|
|
// checkEdge validates the semantics of the given edge.
|
|
func checkEdge(graph *ast.Graph, from ast.Vertex, to *ast.Edge) error {
|
|
if !graph.Directed && to.Directed {
|
|
return fmt.Errorf("undirected graph %q contains directed edge from %q to %q", graph.ID, from, to.Vertex)
|
|
}
|
|
if err := checkVertex(graph, to.Vertex); err != nil {
|
|
return err
|
|
}
|
|
if to.To != nil {
|
|
return checkEdge(graph, to.Vertex, to.To)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// checkAttrStmt validates the semantics of the given attribute statement.
|
|
func checkAttrStmt(graph *ast.Graph, stmt *ast.AttrStmt) error {
|
|
for _, attr := range stmt.Attrs {
|
|
if err := checkAttr(graph, stmt.Kind, attr); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// checkAttr validates the semantics of the given attribute for the given
|
|
// component kind.
|
|
func checkAttr(graph *ast.Graph, kind ast.Kind, attr *ast.Attr) error {
|
|
switch kind {
|
|
case ast.KindGraph:
|
|
// TODO: Validate key-value pairs for graphs.
|
|
return nil
|
|
case ast.KindNode:
|
|
// TODO: Validate key-value pairs for nodes.
|
|
return nil
|
|
case ast.KindEdge:
|
|
// TODO: Validate key-value pairs for edges.
|
|
return nil
|
|
default:
|
|
panic(fmt.Sprintf("support for component kind %v not yet supported", kind))
|
|
}
|
|
}
|
|
|
|
// checkSubgraph validates the semantics of the given subgraph.
|
|
func checkSubgraph(graph *ast.Graph, subgraph *ast.Subgraph) error {
|
|
// TODO: Check subgraph.ID for duplicates?
|
|
for _, stmt := range subgraph.Stmts {
|
|
// TODO: Refine handling of subgraph statements?
|
|
// checkSubgraphStmt(graph, subgraph, stmt)
|
|
if err := checkStmt(graph, stmt); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// checkVertex validates the semantics of the given vertex.
|
|
func checkVertex(graph *ast.Graph, vertex ast.Vertex) error {
|
|
switch vertex := vertex.(type) {
|
|
case *ast.Node:
|
|
return checkNode(graph, vertex)
|
|
case *ast.Subgraph:
|
|
return checkSubgraph(graph, vertex)
|
|
default:
|
|
panic(fmt.Sprintf("support for vertex of type %T not yet supported", vertex))
|
|
}
|
|
}
|
|
|
|
// checNode validates the semantics of the given node.
|
|
func checkNode(graph *ast.Graph, node *ast.Node) error {
|
|
// TODO: Check node.ID for duplicates?
|
|
// TODO: Validate node.Port.
|
|
return nil
|
|
}
|