logica icon indicating copy to clipboard operation
logica copied to clipboard

Grammar railroad diagram

Open mingodad opened this issue 1 year ago • 4 comments

After manually converting the grammar shown here https://github.com/EvgSkv/logica/blob/main/docs/syntax.md to an EBNF understood by https://github.com/GuntherRademacher/rr we can have a nice navigable railroad diagram (see bellow).

And looking at the railroad diagram I suspect that the grammar can be refined to more closely resembles that hard coded grammar in https://github.com/EvgSkv/logica/blob/main/parser_py/parse.py .

//
// EBNF to be viewd at
//    (IPV6) https://www.bottlecaps.de/rr/ui
//    (IPV4) https://rr.red-dove.com/ui
//
// Copy and paste this at one of the urls shown above in the 'Edit Grammar' tab
// then click the 'View Diagram' tab.
//
//# Logica Program Syntax

//Here is a semi-formal [BNF](https://en.wikipedia.org/wiki/Backus%E2%80%93Naur_form) of Logica program.

//```

// Program is a sequence of entries separated by semicolon.
program ::= program_entry (';' program_entry)* ';'

// Each entry is an import, a rule, or a functor application.
program_entry ::= import | rule | functor_application

// Example of an import -- import path.to.file.Predicate as AnotherPredicate
import ::=
  'import' dot_separated_path '.' logica_predicate ('as' logica_predicate)?
dot_separated_path ::= [^<newline>]+

// Predicate defined by the program is alphanumeric starting with an
// uppercase letter, or '@' if it is an imperative predicate.
logica_predicate ::= ordinary_logica_predicate | imperative_predicate
// Ordinary predicates is what Logica is mostly about, example -- Grandparent
ordinary_logica_predicate ::= [A-Z_][0-9a-zA-Z_]*
// Imperative predicates are used for annotations, example -- @Ground
imperative_predicate ::= '@' [0-9a-zA-Z_]*

// You can also use database tables as predicates. 
predicate ::= logica_predicate | '`'[^`]+'`' | [A-Za-z_0-9.]+

// Variable must be lowercase numeric.
variable ::= [a-z0-9_]

// Rule is a head with an optional body.
rule ::= rule_head ( ':-' rule_body )?

// Body of the rule is a proposition.
rule_body ::= proposition

// Head of a rule is a call with an optional assignment and
// an optional 'distinct' denotation.
rule_head ::= head_call assignment? 'distinct'?

// Example of a simple assignment is -- = 2 * x
// and example of an aggregating assignment is -- List= 2 * x.
assignment ::= simple_assignment | aggregating_assignment
simple_assignment ::= '=' expression
aggregating_operator ::= ('+' | logica_predicate) '='
aggregating_assignment ::= aggregating_operator expression

// No space is allowed between predicate name and the opening
// parenthesis.
call ::= predicate '(' record_internal ')'
head_call ::= logica_predicate '(' aggregating_record_internal ')'

// Example of record_internal -- a: 5, b: 2 * x
record_field_value ::= field ':' expression
record_internal ::= 
  (record_field_value (',' record_field_value)* (',' '..' variable)?)? |
  ('..' variable)

// Example of aggregating_field_value -- x? += 5
aggregating_field_value ::= field '?' aggregating_assignment
aggregating_record_internal  ::=
  (record_field_value | aggregating_field_value)?
  (',' (record_field_value | aggregating_field_value))*

// Expression is a predicate call, operation, combine,
// list inclusion, implication or an object description.
expression ::=
  call |
  unary_operator_call |
  binary_operator_call |
  combine |
  inclusion |
  implication |
  string_literal |
  number_literal |
  boolean_literal |
  null_literal |
  list |
  record |
  ('(' expression ')')

operator ::= '+'|'-'|'/'|'>'|'<'|'<='|'>='|'=='|'->'|'&&'|'||'

unary_operator ::= '!'|'-'

unary_operator_call ::= unary_operator expression
binary_operator_call ::= expression operator expression

// Example of inclusion -- x in [1,2,3,4]
inclusion ::= expression 'in' expression

// Example of an implication -- if a == b then 7 else 9
implication ::=
  '(' 'if' expression then expression
  ('else if' expression 'then' expression)*
  'else' expression ')'

// If combine has a body then it must be enclosed in parenthesis.
combine ::= 'combine' aggregating_assignment (':-' rule_body)?

// Concrete object specification.
string_literal ::= '"'[^"<newline>]'"'
number_literal ::= [0-9]+ ( '.'[0-9]+ )?
boolean_literal ::= 'true'|'false'
null_literal ::= 'null'
list ::= '[' (expression (','expression)*)? ']'
record ::= '{' record_internal '}'

// Proposition is a conjunction, disjunction, negation,
// a predicate call, operation, or list inclusion.
proposition ::=
  conjunction |
  disjunction |
  negation |
  call |
  binary_operator_call |
  unary_operator_call |
  assign_combination |
  inclusion |
  ('(' proposition ')')

conjunction ::= proposition (',' proposition)*
disjunction ::= proposition ('|' proposition)*
negation ::= '~' proposition

// Example of assign combination -- l List= (2 * x :- x in [1,2,3])
assign_combination ::= variable
  aggregating_assignment |
  (aggregating_operator '(' expression ':-' proposition ')')

// Example of a functor application -- F := G(A: B)
functor_application ::= logica_predicate ':=' logica_predicate '(' functor_record_internal ')'
functor_record_internal ::=
  (logica_predicate ':' logica_predicate)?
  (',' logica_predicate ':' logica_predicate )*


//```

//**Comments**: A symbol `#` occurring outside of a string starts a comment that
//goes to the end of the line. A combination `/*` occurring anywhere outside a string
//starts a comment that goes to the first occurrence of `/*`.

mingodad avatar Aug 26 '24 14:08 mingodad

@ZiQiangZhou your comment feels like span/fishing

mingodad avatar Aug 26 '24 14:08 mingodad

@mingodad I agree, having railroad diagrams would be useful.

I've started getting them, but didn't organize into a page yet: https://colab.research.google.com/drive/1MQtu5aAFy9SK8y2-FW3noOvqrLakr04O?usp=sharing

I was using railroad-diagrams python package.

Your message says "see below", but I don't see any diagrams. Do I get it right that you actually produced those?

EvgSkv avatar Aug 28 '24 01:08 EvgSkv

Thank you for reply ! Sorry by the confusion ! Normally I add this to the top of the EBNF:

//
// EBNF to be viewd at
//    (IPV6) https://www.bottlecaps.de/rr/ui
//    (IPV4) https://rr.red-dove.com/ui
//
// Copy and paste this at one of the urls shown above in the 'Edit Grammar' tab
// then click the 'View Diagram' tab.
//

But I forgot to do so on the first message (I'm editing it now and adding it).

mingodad avatar Aug 28 '24 07:08 mingodad

Oh wow, this is great! Somehow I didn't know you can simply render BNFs. Thank you!

@mingodad do you want to send a pull-request to merge your changes to syntax file?

EvgSkv avatar Aug 29 '24 16:08 EvgSkv