Concrete syntax no longer indentation-based (nightmare to parse). Add indented multi-line code terminals.
This commit is contained in:
parent
59de61d0a3
commit
e875821e70
8 changed files with 119 additions and 73 deletions
16
concrete_syntax/common.py
Normal file
16
concrete_syntax/common.py
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
def indent(multiline_string, how_much):
|
||||
lines = multiline_string.split('\n')
|
||||
return '\n'.join([' '*how_much+l for l in lines])
|
||||
|
||||
def display_value(val: any, type_name: str, indentation=0):
|
||||
if type_name == "ActionCode":
|
||||
if '\n' in val:
|
||||
return '```\n'+indent(val, indentation+4)+'\n'+' '*indentation+'```'
|
||||
else:
|
||||
return '`'+val+'`'
|
||||
elif type_name == "String":
|
||||
return '"'+val+'"'
|
||||
elif type_name == "Integer" or type_name == "Boolean":
|
||||
return str(val)
|
||||
else:
|
||||
raise Exception("don't know how to display value" + type_name)
|
||||
|
|
@ -3,7 +3,7 @@
|
|||
from services import scd, od
|
||||
from services.bottom.V0 import Bottom
|
||||
from transformation import ramify
|
||||
import json
|
||||
from concrete_syntax.common import display_value
|
||||
from uuid import UUID
|
||||
|
||||
def render_class_diagram(state, model, prefix_ids=""):
|
||||
|
|
@ -97,7 +97,8 @@ def render_object_diagram(state, m, mm, render_attributes=True, prefix_ids=""):
|
|||
for attr_name, attr_edge in attributes:
|
||||
slot = m_od.get_slot(obj_node, attr_name)
|
||||
if slot != None:
|
||||
output += f"\n{attr_name} => {json.dumps(od.read_primitive_value(bottom, slot, mm)[0])}"
|
||||
val, type_name = od.read_primitive_value(bottom, slot, mm)
|
||||
output += f"\n{attr_name} => {display_value(val, type_name)}"
|
||||
output += '\n}'
|
||||
|
||||
output += '\n'
|
||||
|
|
|
|||
|
|
@ -7,46 +7,34 @@ from services.scd import SCD
|
|||
from uuid import UUID
|
||||
|
||||
grammar = r"""
|
||||
%import common.WS_INLINE
|
||||
%ignore WS_INLINE
|
||||
%import common.WS
|
||||
%ignore WS
|
||||
%ignore COMMENT
|
||||
|
||||
%declare _INDENT _DEDENT
|
||||
|
||||
?start: (_NL | object )*
|
||||
?start: object*
|
||||
|
||||
IDENTIFIER: /[A-Za-z_][A-Za-z_0-9]*/
|
||||
COMMENT: /#.*/
|
||||
|
||||
# newline
|
||||
_NL: /(\r?\n[\t ]*)+/
|
||||
COMMENT: /#[^\n]*\n/
|
||||
|
||||
literal: INT
|
||||
| STR
|
||||
| BOOL
|
||||
| CODE
|
||||
| INDENTED_CODE
|
||||
|
||||
INT: /[0-9]+/
|
||||
STR: /"[^"]*"/
|
||||
| /'[^']*'/
|
||||
BOOL: "True" | "False"
|
||||
CODE: /`[^`]*`/
|
||||
INDENTED_CODE: /```[^`]*```/
|
||||
|
||||
object: [IDENTIFIER] ":" IDENTIFIER [link_spec] _NL [_INDENT slot+ _DEDENT]
|
||||
object: [IDENTIFIER] ":" IDENTIFIER [link_spec] ["{" slot* "}"]
|
||||
link_spec: "(" IDENTIFIER "->" IDENTIFIER ")"
|
||||
slot: IDENTIFIER "=" literal _NL
|
||||
slot: IDENTIFIER "=" literal ";"
|
||||
"""
|
||||
|
||||
|
||||
class TreeIndenter(Indenter):
|
||||
NL_type = '_NL'
|
||||
OPEN_PAREN_types = []
|
||||
CLOSE_PAREN_types = []
|
||||
INDENT_type = '_INDENT'
|
||||
DEDENT_type = '_DEDENT'
|
||||
tab_len = 4
|
||||
|
||||
parser = Lark(grammar, parser='lalr', postlex=TreeIndenter())
|
||||
parser = Lark(grammar, parser='lalr')
|
||||
|
||||
# internal use only
|
||||
# just a dumb wrapper to distinguish between code and string
|
||||
|
|
@ -83,6 +71,18 @@ def parse_od(state, cs_text, mm):
|
|||
def CODE(self, token):
|
||||
return _Code(str(token[1:-1])) # strip the ``
|
||||
|
||||
def INDENTED_CODE(self, token):
|
||||
skip = 4 # strip the ``` and the following newline character
|
||||
space_count = 0
|
||||
while token[skip+space_count] == " ":
|
||||
space_count += 1
|
||||
lines = token.split('\n')[1:-1]
|
||||
for line in lines:
|
||||
if line[0:space_count] != ' '*space_count:
|
||||
raise Exception("wrong indentation of INDENTED_CODE")
|
||||
unindented_lines = [l[space_count:] for l in lines]
|
||||
return _Code('\n'.join(unindented_lines))
|
||||
|
||||
def literal(self, el):
|
||||
return el[0]
|
||||
|
||||
|
|
|
|||
|
|
@ -2,17 +2,8 @@
|
|||
|
||||
from services import od
|
||||
from services.bottom.V0 import Bottom
|
||||
import json
|
||||
from concrete_syntax.common import display_value
|
||||
|
||||
def display_value(val: any, type_name: str):
|
||||
if type_name == "ActionCode":
|
||||
return '`'+val+'`'
|
||||
elif type_name == "String":
|
||||
return '"'+val+'"'
|
||||
elif type_name == "Integer" or type_name == "Boolean":
|
||||
return str(val)
|
||||
else:
|
||||
raise Exception("don't know how to display value" + type_name)
|
||||
|
||||
def render_od(state, m_id, mm_id, hide_names=True):
|
||||
bottom = Bottom(state)
|
||||
|
|
@ -26,19 +17,23 @@ def render_od(state, m_id, mm_id, hide_names=True):
|
|||
|
||||
def write_attributes(object_node):
|
||||
o = ""
|
||||
for attr_name, slot_node in m_od.get_slots(object_node):
|
||||
value, type_name = m_od.read_slot(slot_node)
|
||||
o += f" {attr_name} = {display_value(value, type_name)}\n"
|
||||
slots = m_od.get_slots(object_node)
|
||||
if len(slots) > 0:
|
||||
o += " {"
|
||||
for attr_name, slot_node in slots:
|
||||
value, type_name = m_od.read_slot(slot_node)
|
||||
o += f"\n {attr_name} = {display_value(value, type_name, indentation=4)};"
|
||||
o += "\n}"
|
||||
return o
|
||||
|
||||
for class_name, objects in m_od.get_all_objects().items():
|
||||
for object_name, object_node in objects.items():
|
||||
output += f"{display_name(object_name)}:{class_name}\n"
|
||||
output += f"\n{display_name(object_name)}:{class_name}"
|
||||
output += write_attributes(object_node)
|
||||
|
||||
for assoc_name, links in m_od.get_all_links().items():
|
||||
for link_name, (link_edge, src_name, tgt_name) in links.items():
|
||||
output += f"{display_name(link_name)}:{assoc_name} ({src_name} -> {tgt_name})\n"
|
||||
output += f"\n{display_name(link_name)}:{assoc_name} ({src_name} -> {tgt_name})"
|
||||
# links can also have slots:
|
||||
output += write_attributes(link_edge)
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue