Halunke, or: How and why I built a programming language

Halunke, or: How and why I built a programming language

Lucas Dohmen will tell you the story of why and how he built Halunke. We will first learn about the motivation for building a toy programming language that explores the boundaries between functional and object oriented programming. We will then dive into the language, its features and see a quick demo in the interactive REPL. In the second part of the talk, we will get into the nitty-gritty of Halunke’s Open Source Ruby implementation.

B049f961d55097ef9104ff4b275a517b?s=128

Lucas Dohmen

March 14, 2018
Tweet

Transcript

  1. None
  2. Lucas faucet-pipeline.org complate

  3. Part 1: Motivation

  4. “FP vs. OO”

  5. Identity Behavior State In Clojure…

  6. Identity Behavior State In Halunke…

  7. Part 2: The Language

  8. (1 + 2) Receiver Argument Message

  9. (“Halunke” replace “Ha” with “Spe”) Receiver Argument Message

  10. (“Halunke” replace “Ha” with “Spe”) Sends “Halunke” the message “replace

    with” with the arguments [“Ha” “Spe”]
  11. (‘a = 2) Receiver Argument Message

  12. •All objects are immutable •A reference created with ‘a can’t

    be reassigned •In the future, there will be reassignable references with @a
  13. No nil/null REPL Define own functions Define own classes Conditionals

    are messages, not syntax Number, String, Array, Dictionary, Regexp Web Module & a tiny Routing Lib
  14. ('a = 12) ('my = "str") ("hello" replace (my replac

    "i" wit "a") with "mumpf") (stdio puts "a") Currently in Development 4 | ("hello" replace (my replac "i" wit "a") with "mumpf") ^^^^^^^^^^^^^^^^^^^^^^^ The String "str" received the message `replac wit`. It doesn't know how to handle that. Did you mean `replace with`?
  15. Live Demo :)

  16. Part 3: Implementation Written in Ruby!

  17. (“Halunke” replace “Ha” with “Spe”)

  18. => [[:OPEN_PAREN, “(“], [:STRING, “Halunke“], [:BAREWORD, “replace“], [:STRING, “Ha“], [:BAREWORD,

    “with“], [:STRING, “Spe“], [:CLOSE_PAREN, “)“]] a = Halunke ::Lexer.new a.tokenize(‘ ‘) (“Halunke“ replace “Ha“ with “Spe“) Lexer
  19. Lexer (with Ragel) string = '"' [^"]* '"'; bareword =

    [a-zA-Z_]+ | '+' | '-' | '*' | '/' | '<' | '>' | '=' | '@'; open_paren = '('; close_paren = ')'; 1. Define Tokens
  20. Lexer (with Ragel) 2. React to tokens string => {

    emit(:STRING, data[ts+1 ...te-1]) }; bareword => { emit(:BAREWORD, data[ts ...te]) }; open_paren => { emit(:OPEN_PAREN, data[ts ...te]) }; close_paren => { emit(:CLOSE_PAREN, data[ts ...te]) }; space; any => { raise "Could not lex ' #{ data[ts ...te] }'" };
  21. Parser b = Halunke ::Parser.new b.parse('("Halunke" replace "Ha" with “Spe")')

    => #<struct Halunke ::Nodes nodes=[ #<struct Halunke ::MessageSendNode receiver=#<struct Halunke ::StringNode value=“Halunke">, message=#<struct Halunke ::MessageNode nodes=[ #<struct Halunke ::BarewordNode value=“replace">, #<struct Halunke ::StringNode value=“Ha">, #<struct Halunke ::BarewordNode value=“with">, #<struct Halunke ::StringNode value=“Spe"> ]> > ]>
  22. Parser (with racc) Program: Expressions { val[0] } ; Expressions:

    /* empty */ { Nodes.new([]) } | Expression Expressions { Nodes.new([val[0]].concat(val[1].nodes)) } ; Expression: STRING { StringNode.new(val[0]) } | BAREWORD { BarewordNode.new(val[0]) } | OPEN_PAREN Expression Expressions CLOSE_PAREN { MessageSendNode.new(val[1], val[2].to_message) } ;
  23. Why doesn’t the parser just lex as well?

  24. Regular C ontext Free C ontext Sensitive Recursively Enum erable

    Chomsky hierarchy Finite Statem achine Pushdow n Autom aton Linear-bounded non-determ inistic Turing m achine Turing M achine More powerful, less guarantees, less performance
  25. Regular C ontext Free Chomsky hierarchy Finite Statem achine Pushdow

    n Autom aton More powerful, less guarantees, less performance
  26. Regular C ontext Free Chomsky hierarchy Regular Expressions C ontext

    Free G ram m ar More powerful, less guarantees, less performance
  27. We have nodes now. What’s next?

  28. Tree-Walk Interpreter

  29. Every node has an eval method

  30. Nodes = Struct.new(:nodes) do def eval(context) nodes.map { |node| node.eval(context)

    }.last end end
  31. What happens now? Let’s dive into the code

  32. ⌨ https://try.halunke.jetzt @moonbeamlabs