lib/parsers: Added S-Expression parser
authorLucas Bajolet <r4pass@hotmail.com>
Wed, 10 Jun 2015 17:20:51 +0000 (13:20 -0400)
committerLucas Bajolet <r4pass@hotmail.com>
Wed, 8 Jul 2015 14:22:34 +0000 (10:22 -0400)
Signed-off-by: Lucas Bajolet <r4pass@hotmail.com>

lib/sexp.nit [new file with mode: 0644]

diff --git a/lib/sexp.nit b/lib/sexp.nit
new file mode 100644 (file)
index 0000000..491abdc
--- /dev/null
@@ -0,0 +1,153 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# This file is free software, which comes along with NIT.  This software is
+# distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+# without  even  the implied warranty of  MERCHANTABILITY or  FITNESS FOR A
+# PARTICULAR PURPOSE.  You can modify it is you want,  provided this header
+# is kept unaltered, and a notification of the changes is added.
+# You  are  allowed  to  redistribute it and sell it, alone or is a part of
+# another product.
+
+# S-Expression parsing facilities
+module sexp
+
+intrude import parser_base
+
+# Any S-Expression entity
+abstract class SExpEntity
+
+       # Location in the source document
+       var location: nullable Location
+end
+
+# A full S-Expression, delimited by `(` and `)`
+class SExp
+       super SExpEntity
+
+       # Children of a SExp
+       var content = new Array[SExpEntity]
+
+       redef fun to_s do return "({content.join(" ")})"
+
+       # Returns a pretty-printable version of self
+       #
+       #     assert "( ( sp 12.3 ) \"DQString\")".to_sexp.as(SExp).pretty_to_s == "(\n\t(\n\t\tsp\n\t\t12.30\n\t)\n\t\"DQString\"\n)"
+       fun pretty_to_s: String do return recurse_to_s(0)
+
+       private fun recurse_to_s(depth: Int): String do
+               var s = "{"\t" * depth}(\n"
+               for i in content do
+                       if i isa SExp then
+                               s += i.recurse_to_s(depth + 1)
+                               s += "\n"
+                               continue
+                       end
+                       s += "\t" * (depth + 1)
+                       s += i.to_s
+                       s += "\n"
+               end
+               return s + "{"\t" * depth})"
+       end
+end
+
+# A Double-quoted String
+class SExpDQString
+       super SExpEntity
+
+       # Double-quoted string
+       var content: String
+
+       redef fun to_s do return content
+end
+
+# A float-value
+class SExpFloat
+       super SExpEntity
+
+       # Floating-point value
+       var content: Float
+
+       redef fun to_s do return content.to_precision(2)
+end
+
+# Any Identifier, non string and non-float
+class SExpId
+       super SExpEntity
+
+       # S-Exp compatible identifier
+       var content: String
+
+       redef fun to_s do return content
+end
+
+# An error parsing S-Expressions
+class SExpError
+       super SExpEntity
+
+       # Cause of the error
+       var message: String
+
+       redef fun to_s do return "S-Expression error: {message} at {location or else "unknown location"}"
+end
+
+# S-Expression processor
+class SExpProcessor
+       super StringProcessor
+
+       # Parses an S-Expression entity
+       fun parse_entity: SExpEntity do
+               var srclen = src.length
+               var delims = once ['(', ')', '"']
+               ignore_whitespaces
+               if pos >= srclen then return new SExpError("Empty S-Expression", location = new Location(line, line_offset))
+               var c = src[pos]
+               if pos >= srclen then return new SExpError("Empty S-Expression")
+               if c == '(' then
+                       var cnt = new SExp
+                       var loc = new Location(line, line_offset)
+                       pos += 1
+                       while pos < srclen and src[pos] != ')' do
+                               var p = parse_entity
+                               if p isa SExpError then break
+                               cnt.content.add p
+                               ignore_whitespaces
+                       end
+                       if pos < srclen and src[pos] == ')' then
+                               pos += 1
+                               return cnt
+                       else
+                               return new SExpError("Incomplete S-Expression", location = loc)
+                       end
+               else if c == '"' then
+                       var stdq = pos
+                       pos += 1
+                       ignore_until("\"")
+                       pos += 1
+                       var endq = pos
+                       return new SExpDQString(src.substring(stdq, endq - stdq))
+               else
+                       var stid = pos
+                       while pos < srclen and not c.is_whitespace and not delims.has(c) do
+                               c = src[pos]
+                               pos += 1
+                       end
+                       if delims.has(c) or c.is_whitespace then pos -= 1
+                       if pos >= srclen then return new SExpError("Invalid S-Expression")
+                       var endid = pos
+                       var cntstr = src.substring(stid, endid - stid)
+                       var cnt: SExpEntity
+                       if cntstr.is_numeric then
+                               cnt = new SExpFloat(cntstr.to_f)
+                       else
+                               cnt = new SExpId(cntstr)
+                       end
+                       return cnt
+               end
+       end
+end
+
+redef class Text
+
+       # Tries to parse `self` as an S-Expression
+       fun to_sexp: SExpEntity do return (new SExpProcessor(self.to_s)).parse_entity
+end