summaryrefslogtreecommitdiffstats
path: root/tcllib/apps/pt
blob: 7389604ed6b343ead10c03acb87fc5c70045c317 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
#!/usr/bin/env tclsh
# -*- tcl -*-

package require Tcl 8.5
# activate commands below for execution from within the pt directory
set self    [file normalize [info script]]
set selfdir [file dirname $self]
lappend auto_path $selfdir [file dirname $selfdir]
# When debugging package loading trouble, show the search paths
#puts [join $auto_path \n]

# # ## ### ##### ######## ############# #####################

package require pt::pgen 1.0.3
package require pt::util
package require fileutil
package require try

namespace eval ::pt::app {
    namespace export generate help
    namespace ensemble create
}

# # ## ### ##### ######## ############# #####################

proc main {} {
    global argv argv0 errorInfo
    if {![llength $argv]} { lappend argv help }
    if {[catch {
	set status [::pt::app {*}$argv]
    } msg]} {
	set elines [split $errorInfo \n]
	if {[llength $elines] == 3} {
	    if {[string match *unknown* $msg]} {
		#puts stderr "$argv0 $msg"
		::pt::app help
		exit 1
	    } elseif {[string match {*wrong # args*} $msg]} {
		#puts $msg
		# Extracting the command name from the error message,
		# because there a prefix will have been expanded to
		# the actual command.  <lindex argv 0> OTOH would be a
		# possible prefix, without a properly matching topic.
		puts stderr Usage:
		::pt::app help [lindex $msg 5 1]
		exit 1
	    }
	}
	set prefix {INTERNAL ERROR :: }
	puts ${prefix}[join $elines \n$prefix]
	exit 1
    }
    exit $status
}

# # ## ### ##### ######## ############# #####################

proc ::pt::app::helpHelp {} {
    return {
	@ help ?TOPIC?

	Provides general help, or specific to the given topic.
    }
}
proc ::pt::app::help {{topic {}}} {
    global argv0
    if {[llength [info level 0]] == 1} {
	puts stderr "Usage: $argv0 command ...\n\nKnown commands:\n"
	foreach topic [Topics] {
	    ::pt::app help $topic
	}
    } elseif {$topic ni [Topics]} {
	puts stderr "$argv0: Unknown help topic '$topic'"
	puts stderr "\tUse one of [linsert [join [Topics] {, }] end-1 or]"
	puts stderr ""
    } else {
	puts stderr \t[join [split [string map [list @ $argv0] [string trim [::pt::app::${topic}Help]]] \n] \n\t]
	puts stderr ""
    }
    return 0
}

proc ::pt::app::Topics {} {
    namespace eval ::TEMP { namespace import ::pt::app::* }
    set commands [info commands ::TEMP::*]
    namespace delete ::TEMP

    set res {}
    foreach c $commands {
	lappend res [regsub ^::TEMP:: $c {}]
    }
    proc ::pt::app::Topics {} [list return $res]
    return $res
}

# # ## ### ##### ######## ############# #####################

proc ::pt::app::generateHelp {} {
    return {
	@ generate PFORMAT ?-option value...? PFILE INFORMAT GFILE

	Generate data in format PFORMAT and write it to PFILE.  Read
	the grammar to be processed from GFILE (assuming the format
	GFORMAT). Use any options to configure the generator. The are
	dependent on PFORMAT.
    }
}
proc ::pt::app::generate {args} {
    # args = parserformat ?...? parserfile grammarformat grammarfile 

    if {[llength $args] < 4} {
	# Just enough that the help code can extract the method name
	return -code error "wrong # args, should be \"@ generate ...\""
    }

    set args [lassign $args parserformat]
    lassign [lrange $args end-2 end] \
	parserfile grammarformat grammarfile
    set args [Template [lrange $args 0 end-3]]
    lappend args -file $grammarfile

    puts "Reading $grammarformat $grammarfile ..."
    set grammar [fileutil::cat $grammarfile]

    puts "Generating a $parserformat parser ..."
    try {
	set parser [::pt::pgen $grammarformat $grammar $parserformat {*}$args]
    } trap {PT RDE SYNTAX} {e o} {
	puts [pt::util error2readable $e $grammar]
	return 1
    }

    puts "Saving to  $parserfile ..."
    fileutil::writeFile $parserfile $parser

    puts OK
    return 0
}

# Lift template specifications from file paths to the file's contents.

proc ::pt::app::Template {optiondict} {
    set res {}
    foreach {option value} $optiondict {
	if {$option eq "-template"} {
	    set value [fileutil::cat $value]
	}
	lappend res $option $value
    }
    return $res
}

# # ## ### ##### ######## ############# #####################

main
exit