-
Notifications
You must be signed in to change notification settings - Fork 0
/
parse_sans_offsides.py
112 lines (85 loc) · 3.56 KB
/
parse_sans_offsides.py
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
"""
A version of the parser setting aside the indent-sensitive part, for now.
"""
from parson import Grammar, alter
from absyntax import Constant, Fetch, Actor, Call, Then, Define, Nest, Method, Parenthesize
parser_grammar = r"""
program : '' sequence :end :mk_body.
sequence : big ++ ';'.
big : make
| id '::=' big :Define
| body
| binsend
| small.
make : id [method_decl '::' body :Method :hug :Actor]
:Define
| id '::' actor :Define
| '::' actor.
actor : '{' method ++ ';' '}' :hug :Actor.
method : method_decl ':' body :Method.
body : '{' sequence '}' :mk_body.
method_decl : ( opid id
| (id id)+
| id) :unzip.
binsend : small ([opid small :unzip] :Call)*.
small : tiny
( [((id tiny)+ | id) :unzip] :Call)?.
tiny : number :Constant
| string :Constant
| id :Fetch
| block
| '(' big ')' :Parenthesize.
block : ('`' id)* :hug ':' body :mk_block_method :hug :Actor.
id : /([A-Za-z][_A-Za-z0-9-]*)/.
opid : /([~!@%&*\-+=|\\<>,?\\\/]+)/.
number : /(-?\d+)/ :int.
string : /'((?:''|[^'])*)'/.
FNORD ~: whitespace*.
whitespace ~: /\s+|-- .*/.
"""
# XXX string literals with '' need unescaping
def mk_block_method(params, body):
cue = ('of',) + ('and',)*(len(params)-1) if params else ('run',)
return Method(cue, params, body)
def mk_body(*exprs): return Nest(reduce(Then, exprs))
unzip = alter(lambda *parts: (parts[0::2], parts[1::2]))
parse = Grammar(parser_grammar)(**globals()).program
# Smoke test
## parse('adjoining of (k + 5) to empty')[0]
#. {adjoining of (k + 5) to empty}
## parse(': { 1 }')[0]
#. {:: {run: {1}}}
text1 = """
empty ::
{ is-empty: { yes }
; has k: { no }
; adjoin k: { adjoining of k to empty }
; merge s: { s }
}
"""
## parse(text1)[0]
#. {empty :: {is-empty: {yes}; has k: {no}; adjoin k: {adjoining of k to empty}; merge s: {s}}}
text2 = """
empty-stack ::
{ is-empty: { yes }
; top: { complain of 'Underflow' }
; pop: { complain of 'Underflow' }
; size: { 0 }
};
push of element on stack ::
{ ::
{ is-empty: { no }
; top: { element }
; pop: { stack }
; size: { 1 + stack size }
} }
"""
## parse(text2)[0]
#. {empty-stack :: {is-empty: {yes}; top: {complain of 'Underflow'}; pop: {complain of 'Underflow'}; size: {0}}; push :: {of element on stack: {:: {is-empty: {no}; top: {element}; pop: {stack}; size: {1 + stack size}}}}}
## parse("foo of 42 + bar of 137")[0]
#. {foo of 42 + bar of 137}
## parse('a ::= 2; a + 3')[0]
#. {a ::= 2; a + 3}
## sets = open('sets.squee').read()
## parse(sets)[0]
#. {empty :: {is-empty: {yes}; has k: {no}; adjoin k: {adjoining of k to empty}; merge s: {s}}; adjoining :: {of n to s: {(s has n) if-so :: {run: {s}} if-not :: {run: {extension :: {is-empty: {no}; has k: {n = k || :: {run: {s has k}}}; adjoin k: {adjoining of k to extension}; merge t: {merging of extension with t}}}}}}; merging :: {of s1 with s2: {meld :: {is-empty: {s1 is-empty && :: {run: {s2 is-empty}}}; has k: {s1 has k || :: {run: {s2 has k}}}; adjoin k: {adjoining of k to meld}; merge s: {merging of meld with s}}}}; make-list of (empty has 42) and ((empty adjoin 42) has 42)}