sitef.go 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409
  1. package sitef
  2. import "os"
  3. import "io"
  4. import "strings"
  5. import "fmt"
  6. import "bytes"
  7. import "bufio"
  8. import "strconv"
  9. // Sitef format for serialization
  10. // Sitef is a simple text format for serializing data to
  11. // It's intent is to be human readable and easy to
  12. // use for multi line text.
  13. // It is quite similar to recfiles, though not compatible because
  14. // in sitef files the first character on the line determines the meaning,
  15. // and there is no backslash escaping.
  16. //
  17. // Sitef is a line based syntax where the first character on the line
  18. // determines the meaning of the line.
  19. // Several lines together form a record.
  20. // A line that starts with # is a comment. There may be no whitespace
  21. // in front of the comment.
  22. // A newline character by itself (that is, an empty line),
  23. // or a - ends a record.
  24. // A plus character, an escape on the previous line or a tab or
  25. // a space continues a value.
  26. // A Continues value gets a newline inserted only when a space or tab was used.
  27. // + supresses the newline.
  28. // Anything else signifies the beginning of the next key.
  29. // % is allowed for special keys for recfile compatibility.
  30. // However % directives are not implemented.
  31. // Keys may not be nested, however, you could use spaces or dots,
  32. // or array indexes to emulate nexted keys.
  33. // A # at the start optionally after whitespace is a comment
  34. //
  35. type Record map[string]string
  36. func (me * Record) Put(key string, val string) {
  37. (*me)[key] = val
  38. }
  39. func (me * Record) Putf(key string, format string, values ...interface{}) {
  40. me.Put(key, fmt.Sprintf(format, values...))
  41. }
  42. func (me * Record) PutArray(key string, values []string) {
  43. for i, value := range values {
  44. realkey := fmt.Sprintf("%s[%d]", key, i)
  45. me.Put(realkey, value)
  46. }
  47. }
  48. func (me * Record) PutInt(key string, val int) {
  49. me.Putf(key, "%d", val)
  50. }
  51. func (me * Record) PutFloat64(key string, val float64) {
  52. me.Putf(key, "%lf", val)
  53. }
  54. func (me Record) Get(key string) (result string) {
  55. return me[key]
  56. }
  57. func (me Record) Getf(key string, format string,
  58. values ...interface{}) (amount int, ok bool) {
  59. val := me.Get(key)
  60. count, err := fmt.Sscanf(val, format, values...)
  61. if err != nil {
  62. return 0, false
  63. }
  64. return count, true
  65. }
  66. func (me Record) GetInt(key string) (val int, err error) {
  67. i, err := strconv.ParseInt(me.Get(key), 0, 0)
  68. return int(i), err
  69. }
  70. func (me Record) GetIntDefault(key string, def int) (val int) {
  71. i, err := strconv.ParseInt(me.Get(key), 0, 0)
  72. if err != nil {
  73. return def;
  74. }
  75. return int(i);
  76. }
  77. func (me Record) GetFloat(key string) (val float64, error error) {
  78. return strconv.ParseFloat(me.Get(key), 64)
  79. }
  80. type Error struct {
  81. error string
  82. lineno int
  83. }
  84. func (me Error) Error() string {
  85. return fmt.Sprintf("%d: %s", me.Lineno, me.error)
  86. }
  87. func (me Error) Lineno() int {
  88. return me.lineno
  89. }
  90. type ParserState int
  91. const (
  92. PARSER_STATE_INIT ParserState = iota
  93. PARSER_STATE_KEY
  94. PARSER_STATE_VALUE
  95. )
  96. type RecordList []Record
  97. func ParseReader(read io.Reader) (RecordList, error) {
  98. var records RecordList
  99. var record Record = make(Record)
  100. var err Error
  101. lineno := 0
  102. scanner := bufio.NewScanner(read)
  103. var key bytes.Buffer
  104. var value bytes.Buffer
  105. for scanner.Scan() {
  106. lineno++
  107. line := scanner.Text()
  108. // End of record?
  109. if (len(line) < 1) || line[0] == '-' {
  110. // save the record and make a new one
  111. records = append(records, record)
  112. record = make(Record)
  113. // comment?
  114. } else if line[0] == '#' {
  115. continue;
  116. // continue value?
  117. } else if line[0] == '\t' || line[0] == ' '|| line[0] == '+' {
  118. /* Add a newline unless + is used */
  119. if (line[0] != '+') {
  120. value.WriteRune('\n')
  121. }
  122. // continue the value, skipping the first character
  123. value.WriteString(line[1:])
  124. // new key
  125. } else if strings.ContainsRune(line, ':') {
  126. // save the previous key/value pair if needed
  127. if len(key.String()) > 0 {
  128. record[key.String()] = value.String()
  129. }
  130. key.Reset()
  131. value.Reset()
  132. parts := strings.SplitN(line, ":", 2)
  133. key.WriteString(parts[0])
  134. if len(parts) > 1 {
  135. value.WriteString(parts[1])
  136. }
  137. // Not a key. Be lenient and assume this is a continued value.
  138. } else {
  139. value.WriteString(line)
  140. }
  141. }
  142. // Append last record if needed.
  143. if len(key.String()) > 0 {
  144. record[key.String()] = value.String()
  145. }
  146. if (len(record) > 0) {
  147. records = append(records, record)
  148. }
  149. if serr := scanner.Err(); serr != nil {
  150. err.lineno = lineno
  151. err.error = serr.Error()
  152. return records, err
  153. }
  154. return records, nil
  155. }
  156. func ParseFilename(filename string) (RecordList, error) {
  157. file, err := os.Open(filename)
  158. if err != nil {
  159. return nil, err
  160. }
  161. defer file.Close()
  162. return ParseReader(file)
  163. }
  164. func WriteField(writer io.Writer, key string, value string) {
  165. replacer := strings.NewReplacer("\n", "\n\t")
  166. writer.Write([]byte(key))
  167. writer.Write([]byte{':'})
  168. writer.Write([]byte(replacer.Replace(value)))
  169. writer.Write([]byte{'\n'})
  170. }
  171. func WriteRecord(writer io.Writer, record Record) {
  172. for key, value := range record {
  173. WriteField(writer, key, value);
  174. }
  175. writer.Write([]byte{'-', '-', '-', '-', '\n'})
  176. }
  177. func WriteRecordList(writer io.Writer, records RecordList) {
  178. for _, record := range records {
  179. WriteRecord(writer, record);
  180. }
  181. }
  182. func SaveRecord(filename string, record Record) (error) {
  183. file, err := os.Create(filename)
  184. if err != nil {
  185. return err
  186. }
  187. defer file.Close()
  188. WriteRecord(file, record)
  189. return nil
  190. }
  191. func SaveRecordList(filename string, records RecordList) (error) {
  192. file, err := os.Create(filename)
  193. if err != nil {
  194. return err
  195. }
  196. defer file.Close()
  197. WriteRecordList(file, records)
  198. return nil
  199. }
  200. /*
  201. func ParseFile(file)
  202. lineno = 0
  203. results = []
  204. errors = []
  205. record = {}
  206. key = nil
  207. value = nil
  208. until file.eof?
  209. lineno += 1
  210. line = file.gets(256)
  211. break if line.nil?
  212. next if line.empty?
  213. // new record
  214. if line[0,2] == '--'
  215. // Store last key used if any.
  216. if key
  217. record[key] = value.chomp
  218. key = nil
  219. end
  220. results << record
  221. record = {}
  222. elsif line[0] == '//'
  223. // Comments start with //
  224. elsif line[0] == ':'
  225. // a key/value pair
  226. key, value = line[1,line.size].split(':', 2)
  227. record[key] = value.chomp
  228. key = value = nil
  229. elsif line[0, 2] == '..'
  230. // end of multiline value
  231. record[key] = value.chomp
  232. key = value = nil
  233. elsif (line[0] == '.') && key.nil?
  234. // Multiline key/value starts here (but is ignored
  235. // until .. is encountered)
  236. key = line[1, line.size]
  237. key.chomp!
  238. value = ""
  239. // multiline value
  240. elsif key
  241. if line[0] == '\\'
  242. // remove any escapes
  243. line.slice!(0)
  244. end
  245. // continue the value
  246. value << line
  247. else
  248. // Not in a key, sntax error.
  249. errors << "//{lineno}: Don't know how to process line"
  250. end
  251. end
  252. // Store last key used if any.
  253. if key
  254. record[key] = value.chomp
  255. end
  256. // store last record
  257. results << record unless record.empty?
  258. return results, errors
  259. end
  260. func load_filename(filename)
  261. results, errors = nil, nil, nil;
  262. file = File.open(filename, 'rt') rescue nil
  263. return nil, ["Could not open //{filename}"] unless file
  264. begin
  265. results, errors = parse_file(file)
  266. ensure
  267. file.close
  268. end
  269. return results, errors
  270. end
  271. // Loads a Sitef fileas obejcts. Uses the ruby_klass atribute to load the object
  272. // If that is missing, uses defklass
  273. func load_objects(filename, defklass=nil)
  274. results, errors = load_filename(filename)
  275. p filename, results, errors
  276. unless errors.nil? || errors.empty?
  277. return nil, errors
  278. end
  279. objres = []
  280. results.each do | result |
  281. klassname = result['ruby_class'] || defklass
  282. return nil unless klassname
  283. klass = klassname.split('::').inject(Kernel) { |klass, name| klass.const_get(name) rescue nil }
  284. return nil unless klass
  285. if klass.respond_to? :from_sitef
  286. objres << klass.from_sitef(result)
  287. else
  288. objres << klass.new(result)
  289. end
  290. end
  291. return objres, errors
  292. end
  293. // Saves a single field to a file in Sitef format.
  294. func save_field(file, key, value)
  295. if value.is_a? String
  296. sval = value.dup
  297. else
  298. sval = value.to_s
  299. end
  300. if sval["\n"]
  301. file.puts(".//{key}\n")
  302. // Escape everything that could be misinterpreted with a \\
  303. sval.gsub!(/\n([\.\-\:\//\\]+)/, "\n\\\\\\1")
  304. sval.gsub!(/\A([\.\-\:\//\\]+)/, "\\\\\\1")
  305. file.printf("%s", sval)
  306. file.printf("\n..\n")
  307. else
  308. file.printf("://{key}://{sval}\n")
  309. end
  310. end
  311. func save_object(file, object, *fields)
  312. save_field(file, :ruby_class, object.class.to_s)
  313. fields.each do | field |
  314. value = object.send(field.to_sym)
  315. save_field(file, field, value)
  316. end
  317. end
  318. func save_record(file, record, *fields)
  319. record.each do | key, value |
  320. next if fields && !fields.empty? && !fields.member?(key)
  321. save_field(file, key, value)
  322. end
  323. end
  324. func save_file(file, records, *fields)
  325. records.each do | record |
  326. if record.is_a? Hash
  327. save_record(file, record, *fields)
  328. else
  329. save_object(file, record, *fields)
  330. end
  331. file.puts("--\n")
  332. end
  333. end
  334. func save_filename(filename, records, *fields)
  335. results , errors = nil, nil
  336. file = File.open(filename, 'wt')
  337. return false, ["Could not open //{filename}"] unless file
  338. begin
  339. save_file(file, records, *fields)
  340. ensure
  341. file.close
  342. end
  343. return true, []
  344. end
  345. end
  346. */