Add Prolog/Erlang style list sugar. All tests pass.

--HG--
rename : mains/tamsin-grammar.tamsin => mains/grammar.tamsin
This commit is contained in:
Chris Pressey 2014-05-12 13:54:58 +01:00
parent e617bada09
commit 281ab6dc36
8 changed files with 81 additions and 8 deletions

View file

@ -2,11 +2,13 @@
import sys
WIDTH=120
for line in sys.stdin:
line = line.rstrip('\n')
while len(line) > 72:
print line[:72]
line = line[72:]
while len(line) > WIDTH:
print line[:WIDTH]
line = line[WIDTH:]
print line
# 123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890

View file

@ -1,10 +1,11 @@
TODO
----
* PROLOG/ERLANG'S LIST SUGAR. would be really really nice.
* `using` production x: x's scanner defaults to utf8, not x
* find different way to match variables in libtamsin, so that
struct term's can be const all the way down — then share terms
* don't consume stdin until asked to scan. demand_buffer. per-line loop.
or rather, per-inputconsumechunk
### testing ###
@ -12,6 +13,7 @@ TODO
* tests that `'V'` is not a variable
* tests for failing when utf8 scanner hits badly-encoded utf8
* tests for invalid escape codes
* test for mismatched # of formals in prod branches
### support for simulating machines and vms ###
@ -63,8 +65,6 @@ TODO
* `Table ← {} & fields → F@fields(H,T) & Table[H] ← T`
* on that topic — production values and/or lambda productions...
* pretty-print AST for error messages
* don't consume stdin until asked to scan. demand_buffer. per-line loop.
or rather, per-inputconsumechunk
### symbol fun ###
@ -91,6 +91,7 @@ TODO
(implement Bakerloo in Tamsin)
* analysis: always_succeeds(Rule)
* analysis: may_backtrack(Rule)
* analysis: may_consume_input(Rule)
* regex-like shortcuts: `\w` for "word", `\s` for "whitespace", etc.
* EOF and nil are the same? it would make sense... call it `end`?
* productions with names with arbitrary characters in them.

View file

@ -509,6 +509,31 @@ Note also that you can print a constructor.
= hi(there(I'm(a(constructor))))
= hi(there(I'm(a(constructor))))
### List sugar ###
In a term context, `[]` is sugar for a list structure.
| main = return [a, b, c].
= list(a, list(b, list(c, nil)))
The tail of the list default to the atom `nil`, but an "improper" list can
be given using the `|` syntax, like Prolog or Erlang.
| main = return [a, b | c].
= list(a, list(b, c))
An empty list is just `nil`.
| main = return [].
= nil
Only one term may appear after the `|`.
| main = return [a, b | c, d].
? expected
The list sugar syntax may also be used in match patterns (see far below.)
### Examples using Terms ###
This program accepts a pair of bits and evaluates to a term, a constructor
@ -1383,6 +1408,17 @@ parentheses.
| what() = "2".
? expected
Note that the list sugar syntax can also be used in patterns.
| main = expr([a, b, c]) & 'ok'.
| expr([]) = print 'end'.
| expr([H|T]) = print H & expr(T).
= a
= b
= c
= end
= ok
Advanced Scanning
-----------------

3
eg/list-sugar2.tamsin Normal file
View file

@ -0,0 +1,3 @@
main = expr([1,2|3]).
expr([1,2|3]) = "f".

View file

@ -81,6 +81,14 @@ tamsin_parser {
texpr = term → T & {"+" & term → S & T ← concat(T, S)} & T.
term = term0.
term0 = variable
| "[" & L ← atom(nil) &
[term → T & L ← constructor(list, list(T, list(L, nil))) &
{"," & term → T & L ← constructor(list, list(T, list(L, nil)))}] &
Tail ← atom(nil) &
["|" & term → Tail] &
"]" &
reverse_c(L, Tail) → L &
return L
| atom → A & L ← nil & ["(" &
term0 → T & L ← list(T, L) &
{"," & term0 → T & L ← list(T, L)} &
@ -134,4 +142,8 @@ tamsin_parser {
find_production_global(MN, PN, P) =
find_module(MN, P) → M & find_production(PN, M).
reverse_c(constructor(list, list(Fst, list(Snd, nil))), Acc) =
Acc ← constructor(list, list(Fst, list(Acc, nil))) &
reverse_c(Snd, Acc).
reverse_c(Other, Acc) = Acc.
}

View file

@ -41,6 +41,7 @@ expr5 = "(" & expr0 & ")"
| prodref & ["(" & texpr & {"," & texpr} & ")"].
texpr = term & {"+" & term}.
term = atom & ["(" & [term & {"," & term}] & ")"]
| "[" & [term & {"," & term}] & ["|" & term] & "]"
| variable.
atom = word | sq_string.
terminal = dq_string

View file

@ -251,7 +251,25 @@ class Parser(EventProducer):
return self.term1()
def term1(self):
if self.peek()[0].isupper():
if self.consume('['):
t = AtomNode('nil')
if self.consume(']'):
return t
term = self.term()
t = ConstructorNode('list', [term, t])
while self.consume(","):
term = self.term()
t = ConstructorNode('list', [term, t])
new = AtomNode('nil')
if self.consume('|'):
new = self.term()
self.expect(']')
# reverse, with specified tail
while isinstance(t, ConstructorNode):
new = ConstructorNode('list', [t.contents[0], new])
t = t.contents[1]
return new
elif self.peek()[0].isupper():
return self.variable()
elif (self.peek()[0].isalnum() or
self.peek()[0] == "'"):

View file

@ -117,7 +117,7 @@ elif [ x$1 = xinterpreter -o x$1 = xi ]; then
echo "*** Testing Python interpreter..."
falderal $VERBOSE --substring-error fixture/tamsin.py.markdown $FILES
elif [ x$1 = xgrammar ]; then
test_it $MODE "mains/tamsin-grammar.tamsin" \
test_it $MODE "mains/grammar.tamsin" \
"lib/tamsin_scanner.tamsin" \
"ok" \
"bin/tamsin-grammar"