gogotanaka / hilbert

:dancers: Implement mathematics.
http://hilbert-lang.org/
553 stars 36 forks source link

Refactor #33

Open gogotanaka opened 10 years ago

gogotanaka commented 10 years ago

First of all....

yonkeltron commented 9 years ago

I suggest you begin with the parser. Hilbert language looks regular enough that you might want to look at the parslet gem: https://github.com/kschiess/parslet

To follow up, please look at the following example code which I cooked up in about an hour:

# coding: utf-8

require 'parslet'
require 'awesome_print'

class Hilbert < Parslet::Parser
  rule(:space)  { match('\s').repeat(1) }
  rule(:space?) { space.maybe }
  rule(:comma)  { str(',') }
  rule(:lparen) { str('(') }
  rule(:rparen) { str(')') }
  rule(:lbracket) { str('[') }
  rule(:rbracket) { str(']') }

  # things
  rule(:integer) { match('[0-9]').repeat(1) >> space? }
  rule(:proposition) { negation.maybe >> match('[A-Z]').as(:identifier) >> space? }
  rule(:variable) { match['a-z'] >> space? }
  rule(:infinity) { str('oo') }
  rule(:pi) { str('pi') }
  rule(:e) { str('e') }

  # embedded functions
  rule(:sin) { str('sin') }
  rule(:cos) { str('cos') }
  rule(:tan) { str('tan') }
  rule(:log) { str('log') }
  rule(:embedded_function) { (sin | cos | tan | log) }

  # operators
  rule(:conjunction) { str('&') >> space? }
  rule(:disjunction) { str('|') >> space? }
  rule(:material_implication) { str('->') >> space? }
  rule(:negation) { str('~') }
  rule(:logical_connective) { conjunction | disjunction | material_implication }
  rule(:sigma) { str('∑') }

  # expressions
  rule(:compound_proposition) {
    negation.as(:operator) >> proposition.as(:operand) |
      proposition.as(:lhs) >> logical_connective.as(:operator) >> logical_expression.as(:rhs)
  }

  rule(:logical_expression) {
    lparen.maybe >> compound_proposition.as(:proposition) >> rparen.maybe |
      lparen.maybe >> proposition.as(:proposition) >> rparen.maybe
  }

  rule(:summation) { sigma >> lbracket >> variable >> str('=') >> integer.as(:start) >> comma >> integer.as(:end) >> rbracket >> space? >> variable }

  rule(:numeric_expression) { integer | lparen >> integer >> rparen | summation.as(:summation) }

  rule(:expression) { logical_expression | numeric_expression }
  root(:expression)
end

def parse(txt)
  Hilbert.new.parse(txt)
rescue Parslet::ParseFailed => e
  puts e.cause.ascii_tree
end

ap parse('123')
ap parse('(123)')
ap parse('A')
ap parse('B | C')
ap parse('B & C | D -> E')
ap parse('(A -> (B))')
ap parse('~A')
ap parse('∑[x=0,10] x')

This is far from complete and far from ideal but it should certainly show how you can quickly use PEG parsers made with parslet to generate ASTs from simple expressions. This looks like a major improvement over the regex-based solutions you currently have. Please let me know how else I can help.

gogotanaka commented 9 years ago

@yonkeltron I'm so sorry for the delay in response. Actually I didn't wanna use parse which depend on other library, because using parser which written by Ruby itself is not so popular. So I was afraid of using it.

But now, I found parslet which you suggest to me is reliable enough to use and I think even if parslet has problem, it's not difficult to send patch.

So I'm gonna use it and code you wrote. I'm really appreciate it.

gogotanaka commented 9 years ago

@yonkeltron

To follow up, please look at the following example code which I cooked up in about an hour:

oh... you'er so awesome! even use parslet I think your job is so quick and good quality!

And now it's obvious fact I've wasted time so far...!