S-expressions: Difference between revisions

Content added Content deleted
(→‎{{header|TXR}}: Added explicit parser.)
Line 2,739: Line 2,739:
However, note that the <code>@</code> character has a special meaning: <code>@#</code> turns into <code>(sys:var #)</code>. TXR's printer right now does not convert this back to <code>@</code> notation upon printing. (The purpose of this notation is to support Lisp code that requires meta-variables: variables distinguished from variables. For instance, logic pattern matching or unification code. Instead of hacks like name-based conventions (for instance <code>x?</code> is a meta-variable, <code>x</code> is ordinary), why not build it into the language: <code>@x</code> is a meta-var, identifiable by special abstract syntax, and <code>x</code> is just an atom, a symbol. There is also <code>@(foo ...)</code> which expands into <code>(sys:expr foo ...)</code>, doing a similar thing for expressions.
However, note that the <code>@</code> character has a special meaning: <code>@#</code> turns into <code>(sys:var #)</code>. TXR's printer right now does not convert this back to <code>@</code> notation upon printing. (The purpose of this notation is to support Lisp code that requires meta-variables: variables distinguished from variables. For instance, logic pattern matching or unification code. Instead of hacks like name-based conventions (for instance <code>x?</code> is a meta-variable, <code>x</code> is ordinary), why not build it into the language: <code>@x</code> is a meta-var, identifiable by special abstract syntax, and <code>x</code> is just an atom, a symbol. There is also <code>@(foo ...)</code> which expands into <code>(sys:expr foo ...)</code>, doing a similar thing for expressions.


(TODO: A solution will appear here soon which avoids "cheating" in this way with the built-in parser: a from-scratch S-exp parser which treats <code>!@#</code> as just a symbol. We can beat other languages with our built-in S-exp parser tied behind our back.)
The following solution avoids "cheating" in this way with the built-in parser; it implements a from-scratch S-exp parser which treats <code>!@#</code> as just a symbol.

The grammar is as follows:

<pre>
expr := ws? atom
| ws? ( ws? expr* ws? )

atom := float | int | sym | str

float := sign? digit+ . digit* exponent?
| sign? digit* . digit+ exponent?
| sign? digit+ exponent

int := sign? digit+

str := " (\" | anychar )* "

sym := sym-char +

sym-char := /* non-whitespace, but not ( and not ) */</pre>

Code:

<lang txr>@(define float (f))@\
@(local (tok))@\
@(cases)@\
@{tok /[+\-]?\d+\.\d*([Ee][+\-]?\d+)?/}@\
@(or)@\
@{tok /[+\-]?\d*\.\d+([Ee][+\-]?\d+)?/}@\
@(or)@\
@{tok /[+\-]?\d+[Ee][+\-]?\d+/}@\
@(end)@\
@(bind f @(flo-str tok))@\
@(end)
@(define int (i))@\
@(local (tok))@\
@{tok /[+\-]?\d+/}@\
@(bind i @(int-str tok))@\
@(end)
@(define sym (s))@\
@(local (tok))@\
@{tok /[^\s()]+/}@\
@(bind s @(intern tok))@\
@(end)
@(define str (s))@\
@(local (tok))@\
@{tok /"(\\"|[^"])*"/}@\
@(bind s @[tok 1..-1])@\
@(end)
@(define atom (a))@\
@(cases)@\
@(float a)@(or)@(int a)@(or)@(str a)@(or)@(sym a)@\
@(end)@\
@(end)
@(define expr (e))@\
@(cases)@\
@/\s*/@(atom e)@\
@(or)@\
@/\s*\(\s*/@(coll :vars (e))@(expr e)@/\s*/@(until))@(end))@\
@(end)@\
@(end)
@(freeform)
@(expr e)@junk
@(output)
expr: @(format nil "~s" e)
junk: @junk
@(end)</lang>

Run:

<pre>$ txr s-expressions.txr -
()
expr: nil
junk:
$ txr s-expressions.txr -
3e3
expr: 3000.0
junk:
$ txr s-expressions.txr -
+3
expr: 3
junk:
$ txr s-expressions.txr -
abc*
expr: abc*
junk:
$ txr s-expressions.txr -
abc*)
expr: abc*
junk: )
$ txr s-expressions.txr -
((data "quoted data" 123 4.5)
(data (!@# (4.5) "(more" "data)")))
expr: ((data "quoted data" 123 4.5) (data (!@# (4.5) "(more" "data)")))
junk:
</pre>


{{omit from|Brlcad}}
{{omit from|Brlcad}}