Skip to content

Instantly share code, notes, and snippets.

@paul
Created November 7, 2010 17:12
Show Gist options
  • Save paul/666253 to your computer and use it in GitHub Desktop.
Save paul/666253 to your computer and use it in GitHub Desktop.
vendor/ruby
.bundle
*.rbc
if [[ -d "${rvm_path:-$HOME/.rvm}/environments" \
&& -s "${rvm_path:-$HOME/.rvm}/environments/rbx-head@europa" ]] ; then
\. "${rvm_path:-$HOME/.rvm}/environments/rbx-head@europa"
else
rvm --create "rbx-head@europa"
fi
EXP : { MESSAGE | TERMINATOR }
MESSAGE : SYMBOL [ARGUMENTS]
ARGUMENTS : "(" [EXP [ { "," EXP } ]] ")"
SYMBOL : IDENTIFIER | LITERAL
IDENTIFER : ( "_" | alphabetic_char ) [ any_char* ]
LITERAL : string | numeric
TERMINATOR : "\n" | ";"
alphabetic_char : [a-zA-Z]
numeric_char : [ "-" ] [0-9] [\.0-9]
white_space : [ \n\t]
any_char : !white_space

Europa

Abstract

Europa is an attempt at an object-oriented, reflective, modern language. As computers get faster with more cores and memory, it is important that parallelization and concurrency be made as simple as possible. Additionally, most modern network applications spend the majority of their time waiting on and manipulating IO. A web framework gets an HTTP request, processes it, turns it into one or several database queries that run sequentially, builds a response document in memory, then sends it back out on the network. All of these operations could be done in parallel on a multi-core system, which is the purpose behind functional languages. However, functional languages are missing the equivalent of Ruby or Python, that is, a language that optimizes for developer time.

Managing IO

Since IO is so fundamental to the performance and operation of every network server, it is absolutely imperative that the language support it at a very low level, and make it transparent to the developer. Some features in functional languages make this easier.

Exemplar Sample

def sleep_and_print(time)
  x = sleep(time)
  print x
end

print_and_sleep(4)
print_and_sleep(2)

Notes

Given:

  1. The #sleep method sleeps for the given number of seconds. However, since all methods are asynchronous, it might take a little longer, and so returns the actual time slept.
  2. Method calls always execute in order, so even though the first sleeps for longer, they output is printed in the correct order.
  3. The method calls happen in parallel, so the total run time is slightly more than 4 seconds.

Output

$ time europa sample.eur
4.0021
2.0003
europa sample.eur 0.00s user 0.00s system 0% cpu 4.083 total

What I don't want

  • eval of any kind
  • This is a high-level server language, not a command-line utility language, like Perl and its descendants. I'd like to avoid things like environment variables and command like options, if it can at all be helped. JavaScript seems to be able to get away with it...
  • No "protected" or "private" methods. In Ruby, its just a formality, because you can always #send the method. Instead, the public API should be exposed via documentation.

Syntax

Syntax is mostly inspired by Ruby and Python, with some other sugar taken from Lua and JavaScript.

Trailing commas are legal

fruit = [
  apple,
  banana,
  orange,
]

Function overloading

Simplify this common idiom:

def find(*args)
  case args.shift
  when :first
    find_one(*args)
  when :all
    find_many(*args)
  end
end

def find_one(*args)
  # find one
end

def find_many(*args)
  # find many
end

With this:

def find(:first, *args)
  # find one
end

def find(:all, *args)
  # find all
end

Sugar

"keywords" like class, module, def are just sugar. The following are equivalent:

class MyClass

  def foo(bar, baz)
    # ...
  end

end

uses the sugar methods, but the actual language specification is:

MyClass = Object.clone do

  foo = method(bar, baz) do
    # ...
  end

end
# Autogenerated from a Treetop grammar. Edits may be lost.
module Europa
include Treetop::Runtime
def root
@root ||= :exp
end
def _nt_exp
start_index = index
if node_cache[:exp].has_key?(index)
cached = node_cache[:exp][index]
if cached
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
@index = cached.interval.end
end
return cached
end
i0 = index
r1 = _nt_message
if r1
r0 = r1
else
r2 = _nt_terminator
if r2
r0 = r2
else
@index = i0
r0 = nil
end
end
node_cache[:exp][start_index] = r0
r0
end
module Message0
def symbol
elements[0]
end
end
def _nt_message
start_index = index
if node_cache[:message].has_key?(index)
cached = node_cache[:message][index]
if cached
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
@index = cached.interval.end
end
return cached
end
i0, s0 = index, []
r1 = _nt_symbol
s0 << r1
if r1
if has_terminal?('\G[arguments]', true, index)
r2 = true
@index += 1
else
r2 = nil
end
s0 << r2
end
if s0.last
r0 = instantiate_node(SyntaxNode,input, i0...index, s0)
r0.extend(Message0)
else
@index = i0
r0 = nil
end
node_cache[:message][start_index] = r0
r0
end
def _nt_symbol
start_index = index
if node_cache[:symbol].has_key?(index)
cached = node_cache[:symbol][index]
if cached
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
@index = cached.interval.end
end
return cached
end
i0 = index
r1 = _nt_identifier
if r1
r0 = r1
else
r2 = _nt_literal
if r2
r0 = r2
else
@index = i0
r0 = nil
end
end
node_cache[:symbol][start_index] = r0
r0
end
module Identifier0
def alphabetic_char
elements[0]
end
end
def _nt_identifier
start_index = index
if node_cache[:identifier].has_key?(index)
cached = node_cache[:identifier][index]
if cached
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
@index = cached.interval.end
end
return cached
end
i0, s0 = index, []
r1 = _nt_alphabetic_char
s0 << r1
if r1
s2, i2 = [], index
loop do
r3 = _nt_alphanumeric_char
if r3
s2 << r3
else
break
end
end
r2 = instantiate_node(SyntaxNode,input, i2...index, s2)
s0 << r2
end
if s0.last
r0 = instantiate_node(SyntaxNode,input, i0...index, s0)
r0.extend(Identifier0)
else
@index = i0
r0 = nil
end
node_cache[:identifier][start_index] = r0
r0
end
def _nt_literal
start_index = index
if node_cache[:literal].has_key?(index)
cached = node_cache[:literal][index]
if cached
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
@index = cached.interval.end
end
return cached
end
i0 = index
r1 = _nt_string
if r1
r0 = r1
else
r2 = _nt_number
if r2
r0 = r2
else
@index = i0
r0 = nil
end
end
node_cache[:literal][start_index] = r0
r0
end
module String0
end
module String1
end
def _nt_string
start_index = index
if node_cache[:string].has_key?(index)
cached = node_cache[:string][index]
if cached
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
@index = cached.interval.end
end
return cached
end
i0, s0 = index, []
if has_terminal?('"', false, index)
r1 = instantiate_node(SyntaxNode,input, index...(index + 1))
@index += 1
else
terminal_parse_failure('"')
r1 = nil
end
s0 << r1
if r1
s2, i2 = [], index
loop do
i3 = index
i4, s4 = index, []
i5 = index
if has_terminal?('"', false, index)
r6 = instantiate_node(SyntaxNode,input, index...(index + 1))
@index += 1
else
terminal_parse_failure('"')
r6 = nil
end
if r6
r5 = nil
else
@index = i5
r5 = instantiate_node(SyntaxNode,input, index...index)
end
s4 << r5
if r5
if index < input_length
r7 = instantiate_node(SyntaxNode,input, index...(index + 1))
@index += 1
else
terminal_parse_failure("any character")
r7 = nil
end
s4 << r7
end
if s4.last
r4 = instantiate_node(SyntaxNode,input, i4...index, s4)
r4.extend(String0)
else
@index = i4
r4 = nil
end
if r4
r3 = r4
else
if has_terminal?('\"', false, index)
r8 = instantiate_node(SyntaxNode,input, index...(index + 2))
@index += 2
else
terminal_parse_failure('\"')
r8 = nil
end
if r8
r3 = r8
else
@index = i3
r3 = nil
end
end
if r3
s2 << r3
else
break
end
end
r2 = instantiate_node(SyntaxNode,input, i2...index, s2)
s0 << r2
if r2
if has_terminal?('"', false, index)
r9 = instantiate_node(SyntaxNode,input, index...(index + 1))
@index += 1
else
terminal_parse_failure('"')
r9 = nil
end
s0 << r9
end
end
if s0.last
r0 = instantiate_node(SyntaxNode,input, i0...index, s0)
r0.extend(String1)
else
@index = i0
r0 = nil
end
node_cache[:string][start_index] = r0
r0
end
module Number0
end
module Number1
end
def _nt_number
start_index = index
if node_cache[:number].has_key?(index)
cached = node_cache[:number][index]
if cached
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
@index = cached.interval.end
end
return cached
end
i0, s0 = index, []
s1, i1 = [], index
loop do
if has_terminal?('\G[0-9]', true, index)
r2 = true
@index += 1
else
r2 = nil
end
if r2
s1 << r2
else
break
end
end
if s1.empty?
@index = i1
r1 = nil
else
r1 = instantiate_node(SyntaxNode,input, i1...index, s1)
end
s0 << r1
if r1
s3, i3 = [], index
loop do
i4, s4 = index, []
if has_terminal?('.', false, index)
r5 = instantiate_node(SyntaxNode,input, index...(index + 1))
@index += 1
else
terminal_parse_failure('.')
r5 = nil
end
s4 << r5
if r5
if has_terminal?('\G[0-9]', true, index)
r6 = true
@index += 1
else
r6 = nil
end
s4 << r6
end
if s4.last
r4 = instantiate_node(SyntaxNode,input, i4...index, s4)
r4.extend(Number0)
else
@index = i4
r4 = nil
end
if r4
s3 << r4
else
break
end
end
r3 = instantiate_node(SyntaxNode,input, i3...index, s3)
s0 << r3
end
if s0.last
r0 = instantiate_node(SyntaxNode,input, i0...index, s0)
r0.extend(Number1)
else
@index = i0
r0 = nil
end
node_cache[:number][start_index] = r0
r0
end
def _nt_alphabetic_char
start_index = index
if node_cache[:alphabetic_char].has_key?(index)
cached = node_cache[:alphabetic_char][index]
if cached
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
@index = cached.interval.end
end
return cached
end
if has_terminal?('\G[A-Za-z_]', true, index)
r0 = instantiate_node(SyntaxNode,input, index...(index + 1))
@index += 1
else
r0 = nil
end
node_cache[:alphabetic_char][start_index] = r0
r0
end
def _nt_alphanumeric_char
start_index = index
if node_cache[:alphanumeric_char].has_key?(index)
cached = node_cache[:alphanumeric_char][index]
if cached
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
@index = cached.interval.end
end
return cached
end
i0 = index
r1 = _nt_alphabetic_char
if r1
r0 = r1
else
if has_terminal?('\G[0-9]', true, index)
r2 = true
@index += 1
else
r2 = nil
end
if r2
r0 = r2
else
@index = i0
r0 = nil
end
end
node_cache[:alphanumeric_char][start_index] = r0
r0
end
def _nt_whitespace_char
start_index = index
if node_cache[:whitespace_char].has_key?(index)
cached = node_cache[:whitespace_char][index]
if cached
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
@index = cached.interval.end
end
return cached
end
if has_terminal?('\G[ \\t\\n\\r]', true, index)
r0 = instantiate_node(SyntaxNode,input, index...(index + 1))
@index += 1
else
r0 = nil
end
node_cache[:whitespace_char][start_index] = r0
r0
end
def _nt_non_whitespace_char
start_index = index
if node_cache[:non_whitespace_char].has_key?(index)
cached = node_cache[:non_whitespace_char][index]
if cached
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
@index = cached.interval.end
end
return cached
end
i0 = index
r1 = _nt_whitespace_char
if r1
r0 = nil
else
@index = i0
r0 = instantiate_node(SyntaxNode,input, index...index)
end
node_cache[:non_whitespace_char][start_index] = r0
r0
end
def _nt_whitespace
start_index = index
if node_cache[:whitespace].has_key?(index)
cached = node_cache[:whitespace][index]
if cached
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
@index = cached.interval.end
end
return cached
end
s0, i0 = [], index
loop do
r1 = _nt_whitespace_char
if r1
s0 << r1
else
break
end
end
r0 = instantiate_node(SyntaxNode,input, i0...index, s0)
node_cache[:whitespace][start_index] = r0
r0
end
def _nt_non_whitespace
start_index = index
if node_cache[:non_whitespace].has_key?(index)
cached = node_cache[:non_whitespace][index]
if cached
cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
@index = cached.interval.end
end
return cached
end
i0 = index
r1 = _nt_whitespace
if r1
r0 = nil
else
@index = i0
r0 = instantiate_node(SyntaxNode,input, index...index)
end
node_cache[:non_whitespace][start_index] = r0
r0
end
end
class EuropaParser < Treetop::Runtime::CompiledParser
include Europa
end
grammar Europa
rule script
statement+
end
rule statement
exp (whitespace+ exp)*
end
rule exp
(message ( "." message)*) / terminator / comment
end
rule message
symbol arguments*
end
rule symbol
identifier / literal
end
rule arguments
"(" whitespace* ( exp whitespace* ( "," whitespace* exp whitespace* )* )* whitespace* ")"
end
rule identifier
[a-zA-Z_] [\w]*
end
rule literal
string / number / label
end
rule string
single_quoted_string / double_quoted_string
end
rule single_quoted_string
'"' (!'"' . / '\"')* '"'
end
rule double_quoted_string
"'" (!"'" . )* "'"
end
rule number
integer ( "." integer )*
end
rule integer
[0-9]+ {
def value
text_value.to_i
end
}
end
rule label
":" ([\w]+ / string)
end
rule terminator
space* (";" / newline)
end
rule comment
'#' ( !newline . )+ ( newline / !. )
end
rule newline
[\r\n]
end
rule space
[ \t]
end
rule whitespace
(space / newline)
end
end
# A comment is a # at the start of a line, to the end of the line
# Integer
42
# Number
3.1415
# Single-quoted String
'foo'
# Double-quoted String
"bar"
# labels
:label
:"quoted label"
:'another label'
# variable
foo
foo_bar
_bar
Baz
# method calls
foo.bar
foo.bar(42)
# more complex syntax
if(something,
do_when_true,
do_when_false
)
if(something)
then(do_when_true)
else(do_when_false)
# suffix conditional
do_stuff unless(dont_do_stuff)
source :rubygems
gem "ruby-llvm"
gem "parslet"
gem "minitest"
GEM
remote: http://rubygems.org/
specs:
blankslate (2.1.2.3)
ffi (1.0.5)
rake (>= 0.8.7)
minitest (2.0.2)
parslet (1.0.0)
blankslate (~> 2.1.2.3)
rake (0.8.7)
ruby-llvm (2.7.0)
ffi (>= 0.5.4)
PLATFORMS
ruby
DEPENDENCIES
minitest
parslet
ruby-llvm
digraph object_model {
graph [
rankdir = "RL"
];
node [
shape = "record"
];
edge [
];
empty [
label = "EmptyObject|\
__clone\l\
__send\l\
"
];
object [
label = "Object|\
clone\l\
send\l\
object_id\l\
==\l\
===\l\
eql?\l\
equal?\l\
[]\l\
[]=\l\
"
];
object -> empty;
number [
label = "Number|\
+\l\
-\l\
*\l\
/\l\
"
];
number -> object;
integer [
label = "Integer|\
\l\
"
];
integer -> number;
buffer [
label = "Buffer|\
each\l\
"
];
buffer -> object;
string [
label = "String|\
\l\
"
];
string -> buffer;
"label" [
label = "Label|\
\l\
"
];
"label" -> object;
kernel [
label = "Kernel|\
print\l\
"
];
kernel -> object;
array [
label = "Array|\
[]\l\
[]=\l\
each\l\
"
];
array -> object;
boolean [
label = "Boolean|\
"
];
boolean -> object;
"true" [
label = "true|\
"
];
"true" -> boolean;
"false" [
label = "false|\
"
];
"false" -> boolean;
nil [
label = "nil|\
nil?\l\
blank?\l\
"
];
nil -> object;
undefined [
label = "undefined|\
blank?\l\
"
];
undefined -> object;
}
Display the source blob
Display the rendered blob
Raw
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
require 'parslet'
class EuropaParser < Parslet::Parser
rule(:script) { (statement | comment).repeat }
rule(:statement) { atom.repeat(1) >> eol }
rule(:atom) { identifier | literal }
rule(:identifier) { match['a-z_'].repeat(1) }
rule(:literal) { number | string | symbol }
rule(:number) { integer >> ( str('.') >> integer ).maybe }
rule(:integer) { match['0-9'].repeat(1) }
rule(:string) { single_quoted_string | double_quoted_string }
rule(:single_quoted_string) {
single_quote >> (single_quote.absnt? >> any).repeat >> single_quote
}
rule(:double_quoted_string) {
double_quote >> (double_quote.absnt? >> any).repeat >> double_quote
}
rule(:symbol) { str(':') >> (identifier | string) }
rule(:comment) {
(space.maybe >> str('#') >> (eol.absnt? >> any).repeat >> eol).repeat(1)
}
rule(:eol) { match('[\r\n]').repeat(1) }
rule(:space) { match['\\s'].repeat(1) }
rule(:space?) { space.maybe }
rule(:single_quote) { str("'") }
rule(:double_quote) { str('"') }
root(:script)
end
if $0 == __FILE__
require 'minitest/autorun'
class ParserTest < MiniTest::Unit::TestCase
def setup
@parser = EuropaParser.new
end
def assert_parses(code)
begin
@parser.parse code
rescue Parslet::ParseFailed => err
raise MiniTest::Assertion, err.to_s + "\n" + @parser.root.error_tree.to_s
end
end
def test_comment
assert_parses <<-CODE
# comment
CODE
end
def test_multiline_comment
assert_parses <<-CODE
#######################
# Fancy Comment Block #
#######################
CODE
end
def test_indented_comment
assert_parses <<-CODE
# indent
CODE
end
def test_integer
assert_parses <<-CODE
42
CODE
end
def test_number
assert_parses <<-CODE
42.0
CODE
end
def test_single_quoted_string
assert_parses <<-CODE
'hello, world!'
CODE
end
def test_double_quoted_string
assert_parses <<-CODE
"hello, world!"
CODE
end
def test_escaping_in_strings
skip "Implement escaped quotes"
assert_parses <<-CODE
"hello, \\\"world\\\""
CODE
end
def test_symbol
assert_parses <<-CODE
:symbol
CODE
end
def test_string_symbols
assert_parses <<-CODE
:"42"
:'illegal-identifier'
CODE
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment