Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Bug fixes in Neotoma - New build and several other changes

Compiler with Neotoma/memory seems to be working - all tests pass !
  • Loading branch information...
commit 20125bead184f383a6ae5d963a1742fca76b17be 1 parent 71c898f
@graeme-defty graeme-defty authored
View
104 examples/lolcat.re
@@ -0,0 +1,104 @@
+#
+# lolcat.re: irccat for Reia
+# Copyright (C)2010 Tony Arcieri
+#
+# Redistribution is permitted under the MIT license. See LICENSE for details.
+#
+
+host = "irc.freenode.net"
+port = 6667
+nick = "lolcat"
+channel = "#reia"
+
+listen_addr = "0.0.0.0"
+listen_port = 3210
+
+class Lolcat
+ def initialize(@server, @port, @nick, @channel)
+ @sock = TCPSocket(@server, @port, {:packet => :line, :active => true})
+ end
+
+ # attr_reader ftw
+ def server; @server; end
+ def port; @port; end
+ def nick; @nick; end
+ def channel; @channel; end
+
+ def register
+ @sock.write("USER #{@nick} * * #{@nick}\n")
+ @sock.write("NICK #{@nick}\n")
+ @sock.write("JOIN #{@channel}\n")
+ end
+
+ def run
+ receive
+ when (:tcp, _, line)
+ process_line(line.to_s().chop())
+ when (:line, line)
+ send_message("PRIVMSG #{@channel} :#{line}")
+ when msg
+ # Ignore unrecognized messages
+ end
+
+ run()
+ end
+
+ def process_line(line)
+ line.puts()
+
+ if %r/^PING /.match(line)
+ handle_ping(line)
+ elseif %r/PRIVMSG (#[a-z0-0]+) :ohai/.match(line)
+ send_message("PRIVMSG #{@channel} :OHAI!!")
+ end
+ end
+
+ def handle_ping(line)
+ # The colors, Duke! The colors!
+ [_, message] = %r/PING :(.*?)[\r\n]*$/.match(line)
+
+ # Lulzy debug message
+ "OHAI THAR SERVAR #{message}! PONG!!!".puts()
+
+ # Oh yeah here's where we actually do the important thing
+ send_message("PONG :#{message}")
+ end
+
+ def send_message(msg)
+ @sock.write("#{msg}\r\n")
+ end
+end
+
+class Server
+ def initialize(@pid, @addr, @port)
+ @server = TCPServer(@addr, @port, {:packet => :line})
+ "*** TCP server listening on #{@addr}:#{@port}".puts()
+ end
+
+ def run
+ connection = ConnectionHandler(@pid, @server.accept())
+ Process.spawn { connection.run() }
+ run()
+ end
+end
+
+class ConnectionHandler
+ def initialize(@pid, @sock); end
+
+ def run()
+ line = @sock.read()
+ if line
+ line = line.to_s().chop()
+ @pid ! (:line, line)
+ run()
+ end
+ end
+end
+
+cat = Lolcat(host, port, nick, channel)
+cat.register()
+
+server = Server(Process.pid(), listen_addr, listen_port)
+Process.spawn_link { server.run() }
+
+cat.run()
View
13,741 src/compiler/reia_yecc_parse.erl
13,741 additions, 0 deletions not shown
View
904 src/compiler/reia_yecc_parse.yrl
@@ -0,0 +1,904 @@
+%
+% reia_parse: Yecc grammar for the Reia language
+% Copyright (C)2008-10 Tony Arcieri
+%
+% Redistribution is permitted under the MIT license. See LICENSE for delist_tails.
+%
+
+Nonterminals
+ grammar
+ expr_list
+ exprs
+ inline_exprs
+ expr
+ inline_if_expr
+ match_expr
+ ternary_expr
+ send_expr
+ bool_expr
+ comp_expr
+ range_expr
+ add_expr
+ mult_expr
+ pow_expr
+ unary_expr
+ call_expr
+ funref_expr
+ max_expr
+ block
+ block_args
+ clauses
+ clause
+ case_expr
+ if_expr
+ if_clause
+ elseif_clauses
+ elseif_clause
+ else_clause
+ receive_expr
+ after_clause
+ throw_expr
+ try_expr
+ catch_clauses
+ catch_clause
+ function_identifier
+ function_name
+ ivar
+ bound_var
+ rebind_op
+ bool_op
+ comp_op
+ add_op
+ mult_op
+ pow_op
+ unary_op
+ module_decl
+ class_decl
+ methods
+ method
+ class_method
+ functions
+ function
+ body
+ args
+ args_tail
+ block_capture
+ boolean
+ class_inst
+ call
+ funref
+ number
+ list
+ list_tail
+ splat
+ list_comprehension
+ lc_exprs
+ lc_expr
+ binary
+ bin_elements
+ bin_element
+ bit_size
+ bit_type_list
+ bit_type_elements
+ bit_type
+ lambda
+ tuple
+ dict
+ dict_entries
+ .
+
+Terminals
+ '(' ')' '[' ']' '{' '}' '<[' ']>' def eol
+ float integer string atom regexp true false nil self
+ module class module_name identifier punctuated_identifier erl
+ 'case' 'when' 'end' 'if' 'unless' 'elseif' 'else' fun do throw
+ 'and' 'or' 'not' 'try' 'catch' for in 'receive' 'after'
+ '+' '-' '*' '/' '%' '**' ',' '.' '..' '@'
+ '=' '=>' ':' '?' '!' '~' '&' '|' '^' '<<' '>>'
+ '===' '==' '!=' '>' '<' '>=' '<='
+ '+=' '-=' '*=' '/=' '**=' '&=' '|=' '^=' '<<=' '>>='
+ .
+
+Rootsymbol grammar.
+
+grammar -> expr_list : '$1'.
+grammar -> '$empty' : [].
+
+%% Expression lists (eol delimited)
+expr_list -> eol : [].
+expr_list -> expr : ['$1'].
+expr_list -> expr eol : ['$1'].
+expr_list -> eol expr_list : '$2'.
+expr_list -> expr eol expr_list : ['$1'|'$3'].
+
+%% Expression lists (comma delimited)
+exprs -> expr : ['$1'].
+exprs -> expr eol : ['$1'].
+exprs -> eol exprs : '$2'.
+exprs -> expr ',' exprs : ['$1'|'$3'].
+
+%% Inline expression lists
+inline_exprs -> expr ',' inline_exprs : ['$1'|'$3'].
+inline_exprs -> expr : ['$1'].
+
+%% Expressions
+expr -> inline_if_expr : '$1'.
+
+inline_if_expr -> match_expr 'if' match_expr :
+ #'if'{line=?line('$2'), clauses=[
+ #clause{line=?line('$2'), patterns=['$3'], exprs=['$1']}
+ ]}.
+inline_if_expr -> match_expr 'unless' match_expr :
+ #'if'{line=?line('$2'), clauses=[
+ #clause{
+ line = ?line('$2'),
+ patterns = [#unary_op{line=?line('$1'), type='not', expr='$3'}],
+ exprs = ['$1']
+ }
+ ]}.
+inline_if_expr -> match_expr : '$1'.
+
+match_expr -> ternary_expr '=' match_expr :
+ #match{
+ line = ?line('$2'),
+ left = '$1',
+ right = '$3'
+ }.
+match_expr -> ternary_expr rebind_op ternary_expr:
+ #binary_op{
+ line = ?line('$1'),
+ type = ?op('$2'),
+ left = '$1',
+ right = '$3'
+ }.
+match_expr -> ternary_expr : '$1'.
+
+ternary_expr -> send_expr '?' send_expr ':' ternary_expr :
+ #'if'{line=?line('$1'), clauses=[
+ #clause{line=?line('$1'), patterns=['$1'], exprs=['$3']},
+ #clause{line=?line('$1'), patterns=[#true{line=?line('$1')}], exprs=['$5']}
+ ]}.
+
+ternary_expr -> send_expr : '$1'.
+
+send_expr -> bool_expr '!' send_expr :
+ #send{line=?line('$2'), receiver='$1', message='$3'}.
+send_expr -> bool_expr : '$1'.
+
+bool_expr -> bool_expr bool_op comp_expr :
+ #binary_op{
+ line = ?line('$1'),
+ type = ?op('$2'),
+ left = '$1',
+ right = '$3'
+ }.
+bool_expr -> comp_expr : '$1'.
+
+comp_expr -> range_expr comp_op range_expr :
+ #binary_op{
+ line = ?line('$1'),
+ type = ?op('$2'),
+ left = '$1',
+ right = '$3'
+ }.
+comp_expr -> range_expr : '$1'.
+
+range_expr -> add_expr '..' add_expr :
+ #range{
+ line = ?line('$1'),
+ from = '$1',
+ to = '$3'
+ }.
+range_expr -> add_expr : '$1'.
+
+add_expr -> add_expr add_op mult_expr :
+ #binary_op{
+ line = ?line('$1'),
+ type = ?op('$2'),
+ left = '$1',
+ right = '$3'
+ }.
+add_expr -> mult_expr : '$1'.
+
+mult_expr -> mult_expr mult_op unary_expr :
+ #binary_op{
+ line = ?line('$1'),
+ type = ?op('$2'),
+ left = '$1',
+ right = '$3'
+ }.
+mult_expr -> unary_expr : '$1'.
+
+unary_expr -> unary_op unary_expr :
+ #unary_op{
+ line = ?line('$1'),
+ type = ?op('$1'),
+ expr = '$2'
+ }.
+unary_expr -> pow_expr : '$1'.
+
+pow_expr -> funref_expr pow_op pow_expr :
+ #binary_op{
+ line = ?line('$1'),
+ type = ?op('$2'),
+ left = '$1',
+ right = '$3'
+ }.
+pow_expr -> funref_expr : '$1'.
+
+funref_expr -> funref : '$1'.
+funref_expr -> call_expr : '$1'.
+
+call_expr -> class_inst : '$1'.
+call_expr -> call : '$1'.
+call_expr -> max_expr : '$1'.
+
+max_expr -> number : '$1'.
+max_expr -> list : '$1'.
+max_expr -> binary : '$1'.
+max_expr -> lambda : '$1'.
+max_expr -> tuple : '$1'.
+max_expr -> dict : '$1'.
+max_expr -> atom : '$1'.
+max_expr -> boolean : '$1'.
+max_expr -> regexp : '$1'.
+max_expr -> self : '$1'.
+max_expr -> case_expr : '$1'.
+max_expr -> if_expr : '$1'.
+max_expr -> receive_expr : '$1'.
+max_expr -> throw_expr : '$1'.
+max_expr -> try_expr : '$1'.
+max_expr -> module_name : '$1'.
+max_expr -> module_decl : '$1'.
+max_expr -> class_decl : '$1'.
+max_expr -> ivar : '$1'.
+max_expr -> bound_var : '$1'.
+max_expr -> identifier : #var{line=?line('$1'), name=?identifier_name('$1')}.
+max_expr -> string : interpolate_string('$1').
+max_expr -> list_comprehension : '$1'.
+max_expr -> '(' expr ')' : '$2'.
+
+%% Assignment operators
+rebind_op -> '+=' : '$1'.
+rebind_op -> '-=' : '$1'.
+rebind_op -> '*=' : '$1'.
+rebind_op -> '/=' : '$1'.
+rebind_op -> '**=' : '$1'.
+rebind_op -> '&=' : '$1'.
+rebind_op -> '|=' : '$1'.
+rebind_op -> '^=' : '$1'.
+rebind_op -> '<<=' : '$1'.
+rebind_op -> '>>=' : '$1'.
+
+%% Boolean operators
+bool_op -> 'and' : '$1'.
+bool_op -> 'or' : '$1'.
+
+%% Comparison operators
+comp_op -> '===' : '$1'.
+comp_op -> '==' : '$1'.
+comp_op -> '!=' : '$1'.
+comp_op -> '>' : '$1'.
+comp_op -> '<' : '$1'.
+comp_op -> '>=' : '$1'.
+comp_op -> '<=' : '$1'.
+
+%% Addition operators
+add_op -> '+' : '$1'.
+add_op -> '-' : '$1'.
+add_op -> '|' : '$1'.
+add_op -> '^' : '$1'.
+add_op -> '<<' : '$1'.
+add_op -> '>>' : '$1'.
+
+%% Multiplication operators
+mult_op -> '*' : '$1'.
+mult_op -> '/' : '$1'.
+mult_op -> '%' : '$1'.
+mult_op -> '&' : '$1'.
+
+%% Exponentation operator
+pow_op -> '**' : '$1'.
+
+%% Unary operators
+unary_op -> '+' : '$1'.
+unary_op -> '-' : '$1'.
+unary_op -> 'not' : '$1'.
+unary_op -> '!' : '$1'.
+unary_op -> '~' : '$1'.
+
+%% Module declarations
+module_decl -> module module_name eol functions 'end' :
+ #module{
+ line = ?line('$1'),
+ name = element(3, '$2'),
+ exprs = begin validate_module_body('$4'), '$4' end
+ }.
+
+%% Functions
+functions -> eol : [].
+functions -> function : ['$1'].
+functions -> function eol : ['$1'].
+functions -> eol functions : '$2'.
+functions -> function eol functions : ['$1'|'$3'].
+
+%% Function definitions
+function -> def function_name eol body 'end' :
+ #function{
+ line = ?line('$1'),
+ name = '$2',
+ body = '$4'
+ }.
+function -> def function_name args eol body 'end' :
+ #function{
+ line = ?line('$1'),
+ name = '$2',
+ args = '$3'#args.args,
+ block = '$3'#args.block,
+ body = '$5'
+ }.
+
+%% Class declarations
+class_decl -> class module_name methods 'end' :
+ #class{
+ line = ?line('$1'),
+ name = ?identifier_name('$2'),
+ exprs = begin validate_class_body('$3'), '$3' end
+ }.
+class_decl -> class module_name '<' module_name methods 'end' :
+ #class{
+ line = ?line('$1'),
+ name = ?identifier_name('$2'),
+ parent = ?identifier_name('$4'),
+ exprs = begin validate_class_body('$5'), '$5' end
+ }.
+
+%% Methods
+methods -> eol : [].
+methods -> method : ['$1'].
+methods -> method eol : ['$1'].
+methods -> eol methods : '$2'.
+methods -> method eol methods : ['$1'|'$3'].
+
+%% Method declarations
+method -> function : '$1'.
+method -> class_method : '$1'.
+method -> expr : '$1'.
+
+%% Class methods
+class_method -> def self '.' function_name eol body 'end' :
+ #class_method{
+ line = ?line('$1'),
+ name = '$4',
+ body = '$6'
+ }.
+class_method -> def self '.' function_name args eol body 'end' :
+ #class_method{
+ line = ?line('$1'),
+ name = '$4',
+ args = '$5'#args.args,
+ block = '$5'#args.block,
+ body = '$7'
+ }.
+
+%% Valid function names
+function_name -> function_identifier : ?identifier_name('$1').
+function_name -> '[' ']' : '[]'.
+function_name -> '[' ']' '=' : '[]='.
+
+%% Function identifiers
+function_identifier -> identifier : '$1'.
+function_identifier -> punctuated_identifier : '$1'.
+function_identifier -> class : {identifier, ?line('$1'), class}.
+function_identifier -> self : {identifier, ?line('$1'), self}.
+
+body -> '$empty' : [#nil{}].
+body -> expr_list : '$1'.
+
+%% Arguments
+args -> '(' ')' : #args{}.
+args -> '(' eol ')' : #args{}.
+args -> '(' expr args_tail : ?args_add('$2', '$3').
+args -> '(' eol expr args_tail : ?args_add('$3', '$4').
+args -> '(' block_capture ')' : '$2'.
+
+args_tail -> ',' expr args_tail : ?args_add('$2', '$3').
+args_tail -> ',' eol expr args_tail : ?args_add('$3', '$4').
+args_tail -> eol ',' expr args_tail : ?args_add('$3', '$4').
+args_tail -> eol ',' eol expr args_tail : ?args_add('$4', '$5').
+
+args_tail -> ')' : #args{}.
+args_tail -> eol ')' : #args{}.
+args_tail -> ',' block_capture ')' : '$2'.
+args_tail -> eol ',' block_capture ')' : '$3'.
+
+block_capture -> '&' expr : #args{block='$2'}.
+block_capture -> '&' expr eol : #args{block='$2'}.
+block_capture -> eol '&' expr : #args{block='$3'}.
+block_capture -> eol '&' expr eol : #args{block='$3'}.
+
+%% Class instantiations
+class_inst -> module_name args :
+ #class_inst{
+ line = ?line('$1'),
+ class = ?identifier_name('$1'),
+ args = '$2'#args.args,
+ block = ?args_default_block(#nil{}, '$2')
+ }.
+
+class_inst -> module_name args block :
+ #class_inst{
+ line = ?line('$1'),
+ class = ?identifier_name('$1'),
+ args = '$2'#args.args,
+ block = '$3'
+ }.
+
+%% Local function calls
+call -> function_identifier args :
+ #local_call{
+ line = ?line('$1'),
+ name = ?identifier_name('$1'),
+ args = '$2'#args.args,
+ block = ?args_default_block(#nil{}, '$2')
+ }.
+
+%% Local function calls with blocks
+call -> function_identifier block :
+ #local_call{
+ line = ?line('$2'),
+ name = ?identifier_name('$1'),
+ block = '$2'
+ }.
+call -> function_identifier args block :
+ case '$2'#args.block of
+ #var{line=1, name='_'} -> % user didn't pass a &block
+ #local_call{
+ line = ?line('$1'),
+ name = ?identifier_name('$1'),
+ args = '$2'#args.args,
+ block = '$3'
+ };
+ _ ->
+ throw({error, {?line('$1'), "both block arg and actual block given"}})
+ end.
+
+%% Remote function calls
+call -> call_expr '.' function_identifier '(' ')' :
+ #remote_call{
+ line = ?line('$2'),
+ receiver = '$1',
+ name = ?identifier_name('$3')
+ }.
+call -> call_expr '.' function_identifier '(' exprs ')' :
+ #remote_call{
+ line = ?line('$2'),
+ receiver = '$1',
+ name = ?identifier_name('$3'),
+ args = '$5'
+ }.
+
+%% Remote function calls with blocks
+call -> call_expr '.' function_identifier block :
+ #remote_call{
+ line = ?line('$2'),
+ receiver = '$1',
+ name = ?identifier_name('$3'),
+ block = '$4'
+ }.
+call -> call_expr '.' function_identifier '(' ')' block :
+ #remote_call{
+ line = ?line('$2'),
+ receiver = '$1',
+ name = ?identifier_name('$3'),
+ block = '$6'
+ }.
+call -> call_expr '.' function_identifier '(' exprs ')' block :
+ #remote_call{
+ line = ?line('$2'),
+ receiver = '$1',
+ name = ?identifier_name('$3'),
+ args = '$5',
+ block = '$7'
+ }.
+
+%% Remote function calls with indexes
+call -> call_expr '.' function_identifier '[' expr ']' :
+ #binary_op{
+ line = ?line('$1'),
+ type = '[]',
+ left = #remote_call{
+ line = ?line('$2'),
+ receiver = '$1',
+ name = ?identifier_name('$3'),
+ args = []
+ },
+ right = '$5'
+ }.
+
+%% Function references
+funref -> call_expr '.' function_identifier :
+ #funref{
+ line = ?line('$1'),
+ receiver = '$1',
+ name = ?identifier_name('$3')
+ }.
+
+%% Blocks
+block -> '{' expr_list '}' :
+ #lambda{
+ line=?line('$1'),
+ body='$2'
+ }.
+block -> '{' '|' block_args '|' expr_list '}' :
+ #lambda{
+ line=?line('$1'),
+ args='$3',
+ body='$5'
+ }.
+block -> do expr_list 'end' :
+ #lambda{
+ line=?line('$1'),
+ body='$2'
+ }.
+block -> do '|' block_args '|' expr_list 'end' :
+ #lambda{
+ line=?line('$1'),
+ args='$3',
+ body='$5'
+ }.
+
+block_args -> max_expr : ['$1'].
+block_args -> max_expr ',' block_args : ['$1'|'$3'].
+
+%% Native Erlang function calls
+call -> erl '.' function_identifier '(' ')' :
+ #native_call{
+ line = ?line('$2'),
+ module = erlang,
+ function = ?identifier_name('$3')
+ }.
+call -> erl '.' function_identifier '(' exprs ')' :
+ #native_call{
+ line = ?line('$2'),
+ module = 'erlang',
+ function = ?identifier_name('$3'),
+ args = '$5'
+ }.
+call -> erl '.' function_identifier '.' function_identifier '(' ')' :
+ #native_call{
+ line = ?line('$2'),
+ module = ?identifier_name('$3'),
+ function = ?identifier_name('$5')
+ }.
+call -> erl '.' function_identifier '.' function_identifier '(' exprs ')' :
+ #native_call{
+ line = ?line('$2'),
+ module = ?identifier_name('$3'),
+ function = ?identifier_name('$5'),
+ args = '$7'
+ }.
+
+%% Boolean values
+boolean -> true : '$1'.
+boolean -> false : '$1'.
+boolean -> nil : '$1'.
+
+%% Numbers
+number -> float : '$1'.
+number -> integer : '$1'.
+
+%% Lists
+list -> '[' ']' : #empty{line=?line('$1')}.
+list -> '[' eol ']' : #empty{line=?line('$1')}.
+list -> '[' expr list_tail : #cons{line=?line('$1'), expr='$2', tail='$3'}.
+list -> '[' eol expr list_tail : #cons{line=?line('$1'), expr='$3', tail='$4'}.
+
+list_tail -> ',' expr list_tail : #cons{line=?line('$1'), expr='$2', tail='$3'}.
+list_tail -> ',' eol expr list_tail : #cons{line=?line('$1'), expr='$3', tail='$4'}.
+list_tail -> eol ',' expr list_tail : #cons{line=?line('$1'), expr='$3', tail='$4'}.
+list_tail -> eol ',' eol expr list_tail : #cons{line=?line('$1'), expr='$4', tail='$5'}.
+
+list_tail -> ']' : #empty{line=?line('$1')}.
+list_tail -> eol ']' : #empty{line=?line('$1')}.
+
+list_tail -> ',' splat ']' : '$2'.
+list_tail -> eol ',' splat ']' : '$3'.
+
+splat -> '*' expr : '$2'.
+splat -> '*' expr eol : '$2'.
+splat -> eol '*' expr : '$3'.
+splat -> eol '*' expr eol : '$3'.
+
+%% List comprehensions
+list_comprehension -> '[' expr for lc_exprs ']' :
+ #lc{
+ line=?line('$1'),
+ expr='$2',
+ generators='$4'
+ }.
+
+lc_exprs -> lc_expr : ['$1'].
+lc_exprs -> lc_expr ',' lc_exprs: ['$1'|'$3'].
+
+lc_expr -> expr : '$1'.
+lc_expr -> expr 'in' expr :
+ #generate{
+ line=?line('$2'),
+ pattern='$1',
+ source='$3'
+ }.
+
+%% Binaries
+binary -> '<[' ']>' : #binary{line=?line('$1'), elements=[]}.
+binary -> '<[' bin_elements ']>' : #binary{line=?line('$1'), elements='$2'}.
+
+bin_elements -> bin_element : ['$1'].
+bin_elements -> bin_element ',' bin_elements : ['$1'|'$3'].
+
+bin_element -> max_expr bit_size bit_type_list:
+ #bin_element{
+ line=?line('$1'),
+ expression='$1',
+ size='$2',
+ type_list='$3'
+ }.
+
+bit_size -> ':' max_expr : '$2'.
+bit_size -> '$empty' : default.
+
+bit_type_list -> '/' bit_type_elements : '$2'.
+bit_type_list -> '$empty' : default.
+
+bit_type_elements -> bit_type '-' bit_type_elements : ['$1'|'$3'].
+bit_type_elements -> bit_type : ['$1'].
+
+bit_type -> atom : element(3, '$1').
+bit_type -> atom ':' integer : {element(3, '$1'), element(3,'$3')}.
+
+%% Lambdas
+lambda -> fun '{' expr_list '}' :
+ #lambda{
+ line = ?line('$1'),
+ body = '$3'
+ }.
+lambda -> fun '(' ')' '{' expr_list '}' :
+ #lambda{
+ line = ?line('$1'),
+ body = '$5'
+ }.
+lambda -> fun '(' exprs ')' '{' expr_list '}' :
+ #lambda{
+ line = ?line('$1'),
+ args = '$3',
+ body = '$6'
+ }.
+lambda -> fun do expr_list 'end' :
+ #lambda{
+ line = ?line('$1'),
+ body = '$3'
+ }.
+lambda -> fun '(' ')' do expr_list 'end' :
+ #lambda{
+ line = ?line('$1'),
+ body = '$5'
+ }.
+lambda -> fun '(' exprs ')' do expr_list 'end' :
+ #lambda{
+ line = ?line('$1'),
+ args = '$3',
+ body = '$6'
+ }.
+
+%% Tuples
+tuple -> '(' ')' : #tuple{line=?line('$1'), elements=[]}.
+tuple -> '(' expr ',' ')' : #tuple{line=?line('$1'), elements=['$2']}.
+tuple -> '(' expr ',' exprs ')': #tuple{line=?line('$1'), elements=['$2'|'$4']}.
+
+%% Dicts
+dict -> '{' '}' : #dict{line=?line('$1'), elements=[]}.
+dict -> '{' dict_entries '}' : #dict{line=?line('$1'), elements='$2'}.
+
+dict_entries -> bool_expr '=>' expr : [{'$1','$3'}]. % FIXME: change add_expr to 1 below match
+dict_entries -> bool_expr '=>' expr ',' dict_entries : [{'$1','$3'}|'$5'].
+
+%% Instance variables
+ivar -> '@' identifier : #ivar{line=?line('$1'), name=?identifier_name('$2')}.
+
+%% Bound variables
+bound_var -> '^' identifier : #bound_var{line=?line('$1'), name=?identifier_name('$2')}.
+
+%% Index operation
+call -> call_expr '[' expr ']' :
+ #binary_op{
+ line=?line('$1'),
+ type='[]',
+ left='$1',
+ right='$3'
+ }.
+
+%% Clauses
+clauses -> clause clauses : ['$1'|'$2'].
+clauses -> clause : ['$1'].
+clauses -> else_clause : ['$1'#clause{patterns=[#var{line = ?line('$1'), name='_'}]}].
+
+clause -> when inline_exprs eol body :
+ #clause{
+ line=?line('$1'),
+ patterns='$2',
+ exprs='$4'
+ }.
+
+%% Case expressions
+case_expr -> 'case' expr eol clauses 'end':
+ #'case'{
+ line=?line('$1'),
+ expr='$2',
+ clauses='$4'
+ }.
+
+%% If expressions
+if_expr -> if_clause 'end' :
+ #'if'{line=?line('$1'), clauses=['$1']}.
+if_expr -> if_clause else_clause 'end' :
+ #'if'{line=?line('$1'), clauses=['$1','$2']}.
+if_expr -> if_clause elseif_clauses 'end' :
+ #'if'{line=?line('$1'), clauses=['$1'|'$2']}.
+if_expr -> if_clause elseif_clauses else_clause 'end' :
+ #'if'{line=?line('$1'), clauses=lists:flatten(['$1','$2','$3'])}.
+
+if_clause -> 'if' expr eol expr_list :
+ #clause{line=?line('$1'), patterns=['$2'], exprs='$4'}.
+if_clause -> 'unless' expr eol expr_list :
+ #clause{
+ line=?line('$1'),
+ patterns=[#unary_op{line=?line('$1'), type='not', expr='$2'}],
+ exprs='$4'
+ }.
+
+elseif_clauses -> elseif_clause elseif_clauses : ['$1'|'$2'].
+elseif_clauses -> elseif_clause : ['$1'].
+elseif_clause -> elseif expr eol expr_list :
+ #clause{line=?line('$1'), patterns=['$2'], exprs='$4'}.
+
+else_clause -> else expr_list :
+ #clause{line=?line('$1'), patterns=[#true{line=?line('$1')}], exprs='$2'}.
+
+%% Receive expressions
+receive_expr -> 'receive' eol clauses 'end':
+ #'receive'{line=?line('$1'), clauses='$3'}.
+receive_expr -> 'receive' eol clauses after_clause 'end':
+ #'receive'{line=?line('$1'), clauses='$3', after_clause='$4'}.
+receive_expr -> 'receive' eol after_clause 'end' :
+ #'receive'{line=?line('$1'), after_clause='$3'}.
+
+after_clause -> 'after' expr eol expr_list :
+ #'after'{line=?line('$1'), timeout='$2', exprs='$4'}.
+
+%% Throw expressions
+throw_expr -> throw '(' expr ')' :
+ #throw{
+ line = ?line('$1'),
+ message = '$3'
+ }.
+throw_expr -> throw '(' module_name ',' call_expr ')' :
+ #throw{
+ line = ?line('$1'),
+ type = '$3'#module_name.name,
+ message = '$5'
+ }.
+
+%% Try expressions
+try_expr -> 'try' expr_list catch_clauses 'end' :
+ #'try'{
+ line = ?line('$1'),
+ body = '$2',
+ clauses = '$3'
+ }.
+
+catch_clauses -> catch_clause catch_clauses : ['$1'|'$2'].
+catch_clauses -> catch_clause : ['$1'].
+
+catch_clause -> 'catch' expr eol expr_list :
+ #'catch'{
+ line = ?line('$1'),
+ pattern = '$2',
+ body = '$4'
+ }.
+
+Erlang code.
+
+-export([string/1]).
+-include("reia_nodes.hrl").
+-record(args, {args=[], block={var,1,'_'}}).
+-define(line(Node), element(2, Node)).
+-define(op(Node), element(1, Node)).
+-define(identifier_name(Id), element(3, Id)).
+-define(args_add(Arg, Args), Args#args{args=[Arg|Args#args.args]}).
+-define(args_default_block(Block, Args), case (Args)#args.block of {var,1,'_'} -> Block; _ -> (Args)#args.block end).
+
+%% Parse a given string with nicely formatted errors
+string(String) ->
+ try
+ case reia_scan:string(String) of
+ {ok, Tokens, _} ->
+ case reia_yecc_parse:parse(Tokens) of
+ {ok, Exprs} ->
+ {ok, Exprs};
+ {error, {_, _, [Message, []]}} ->
+ {error, {eof, lists:flatten([Message, "end of file"])}};
+ {error, {Line, _, [Message, Token]}} ->
+ {error, {Line, lists:flatten([Message, Token])}}
+ end;
+ {error, {Line, _, {Message, Token}}, _} ->
+ {error, {Line, lists:flatten(io_lib:format("~p ~p", [Message, Token]))}}
+ end
+ catch {error, {_Line, _Message}} = Error ->
+ Error
+ end.
+
+%% Ensure a given module body contains only function defs
+validate_module_body([]) -> ok;
+validate_module_body([#function{}|Exprs]) ->
+ validate_module_body(Exprs);
+validate_module_body([Expr|_]) ->
+ Line = element(2, Expr),
+ reia:throw('SyntaxError', Line, "Arbitrary expressions not allowed in module bodies").
+
+%% Ensure a given class body contains only method defs
+validate_class_body([]) -> ok;
+validate_class_body([#class_method{}|Exprs]) ->
+ validate_class_body(Exprs);
+validate_class_body([#function{}|Exprs]) ->
+ validate_class_body(Exprs);
+validate_class_body([Expr|_]) ->
+ Line = element(2, Expr),
+ reia:throw('SyntaxError', Line, "Arbitrary expressions not allowed in class bodies").
+
+%% Interpolate strings, parsing the contents of #{...} tags
+interpolate_string(#string{line=Line, characters=Chars}) ->
+ interpolate_string(Chars, Line, [], []).
+
+interpolate_string([], Line, CharAcc, ExprAcc) ->
+ Result = case CharAcc of
+ [] -> lists:reverse(ExprAcc);
+ _ -> lists:reverse([#string{line=Line, characters=lists:reverse(CharAcc)}|ExprAcc])
+ end,
+ case Result of
+ [] -> #string{line=Line, characters=""};
+ [#string{} = Res] -> Res;
+ _ -> #dstring{line=Line, elements=Result}
+ end;
+interpolate_string("#{" ++ String, Line, CharAcc, ExprAcc) ->
+ {String2, Expr} = extract_fragment([], String, Line),
+ ExprAcc2 = case CharAcc of
+ [] -> ExprAcc;
+ _ -> [#string{line=Line, characters=lists:reverse(CharAcc)}|ExprAcc]
+ end,
+ interpolate_string(String2, Line, [], [Expr|ExprAcc2]);
+interpolate_string([Char|Rest], Line, CharAcc, ExprAcc) ->
+ interpolate_string(Rest, Line, [Char|CharAcc], ExprAcc).
+
+extract_fragment(_Continuation, [], Line) ->
+ throw({error, {Line, "unexpected end of interpolated string"}});
+extract_fragment(_Continuation, [$"|_], Line) ->
+ throw({error, {Line, "invalid quote within interpolated string"}});
+extract_fragment(Continuation, [$}|String], Line) ->
+ {more, Continuation2} = reia_scan:tokens(Continuation, [$}], Line),
+ case Continuation2 of
+ {tokens, _, _, _, _, [{'}', _}|Tokens], _, _} ->
+ case reia_yecc_parse:parse(lists:reverse(Tokens)) of
+ {ok, [Expr]} ->
+ {String, Expr};
+ %% Need more tokens
+ {error, {_, _, [_, []]}} ->
+ extract_fragment(Continuation2, String, Line);
+ Error ->
+ throw(Error)
+ end;
+ {skip_tokens, _, _, _, _, {_, _, Error}, _, _} ->
+ throw({error, {Line, Error}})
+ end;
+extract_fragment(Continuation, [Char|String], Line) ->
+ {more, Continuation2} = reia_scan:tokens(Continuation, [Char], Line),
+ extract_fragment(Continuation2, String, Line).
View
6 src/neotoma/.gitignore
@@ -0,0 +1,6 @@
+ebin/*
+ebin_tests/*
+extra/*.beam
+*#*
+.DS_Store
+._*
View
14 src/neotoma/Makefile
@@ -1,14 +0,0 @@
-all: compile
-
-compile:
- @ ./rebar compile
-
-tests:
- @ ./rebar eunit
-
-clean:
- @ ./rebar clean
-
-bootstrap: compile
- @ erl -pz ebin -b start_sasl -noshell -s init stop -eval 'neotoma:bootstrap().'
- @ ./rebar compile
View
85 src/neotoma/Rakefile
@@ -0,0 +1,85 @@
+require 'rake/clean'
+
+task :default => %w(check_erl_version build test)
+
+# Build rules
+task :build => %w(neotoma)
+task :neotoma => %w(parser compile)
+task :parser => %w(src/neotoma_parse.erl)
+
+# include file
+file "priv/peg_includes.erl" => ["src/neotoma_peg.erl"] do
+ sh "cat src/neotoma_peg.erl | grep -v \"^%\" | grep -v \"^-\" > priv/peg_includes.erl"
+ end
+
+# Parser
+if File.exist?("ebin/neotoma_parse.beam")
+ file "src/neotoma_parse.erl" => ["src/neotoma_parse.peg", "priv/peg_includes.erl"] do
+ erl_eval 'neotoma:file("src/neotoma_parse.peg")', 'ebin/'
+ end
+else
+ sh "cp src/neotoma_parse.erl.safe src/neotoma_parse.erl"
+end
+
+task :compile => %w(ebin src/neotoma.app priv/peg_includes.erl) do
+ sh "cd src;erl -pa ../ebin -make"
+ end
+
+task :test_beams => %w(ebin_tests) do
+ sh "cd tests;erl -pa ../ebin -make"
+ end
+
+# create directories
+task :ebin do mkdir "ebin" end
+task :priv do mkdir "priv" end
+task :ebin_tests do mkdir "ebin_tests" end
+
+def mkdir(dir)
+ File.exist?(dir) || sh("mkdir #{dir}")
+end
+
+
+# Test suite
+task :test => %w(build test_beams) do
+ erl_eval 'test_suite:test()', 'ebin', 'ebin_tests', 'ebin_tests/examples'
+end
+
+# Cleaning
+CLEAN.include %w(ebin ebin_tests src/neotoma_parse.erl priv/peg_includes.erl)
+
+#---- generic erlang version tests ------
+
+# Returns the installed Erlang version
+def erlang_version
+ version = `erl -version 2>&1`.strip.match(/\d\.\d(\.\d)?$/)
+ unless version
+ STDERR.puts "Error retrieving Erlang version. Do you have it installed?"
+ exit 1
+ end
+
+ version[0]
+end
+
+# Evaluate the given Erlang statement
+def erl_eval(cmd, *pa)
+ pa_str = pa.empty? ? "" : "-pa #{pa.join(' ')}"
+ sh "erl -noshell #{pa_str} -eval '#{cmd}' -s erlang halt"
+end
+
+# Ensure the version of Erlang installed is recent enough
+task :check_erl_version do
+ print "Checking Erlang version... "
+ version = erlang_version
+
+ if version >= "5.6.3"
+ puts "#{version} (ok)"
+ else
+ puts "#{version} (too old)"
+ puts "Sorry, the version of Erlang you have installed is too old to run Reia"
+ puts "Reia requires a minimum Erlang version of R12B-3 (5.6.3)"
+ puts "Please see http://wiki.reia-lang.org/wiki/Building#Prerequisites"
+ exit 1
+ end
+end
+
+
View
2  src/neotoma/priv/peg_includes.erl
@@ -166,7 +166,7 @@ p_charclass(Class) ->
{match, [{0, Length}|_]} ->
{Head, Tail} = erlang:split_binary(Inp, Length),
{Head, Tail, p_advance_index(Head, Index)};
- _ -> {fail, {expected, {character_class, Class}, Index}}
+ _ -> {fail, {expected, {character_class, binary_to_list(Class)}, Index}}
end
end.
View
BIN  src/neotoma/rebar
Binary file not shown
View
1  src/neotoma/rebar.config
@@ -1 +0,0 @@
-{lib_dirs, [".."]}.
View
3  src/neotoma/src/neotoma.erl
@@ -31,7 +31,8 @@ file(InputGrammar, Options) ->
ModuleAttrs = generate_module_attrs(ModuleName),
EntryFuns = generate_entry_functions(Root),
TransformFun = create_transform(TransformModule, OutputDir, GenTransform),
- {ok, PegIncludes} = file:read_file(code:priv_dir(neotoma) ++ "/peg_includes.erl"),
+% {ok, PegIncludes} = file:read_file(code:priv_dir(neotoma) ++ "/peg_includes.erl"),
+ {ok, PegIncludes} = file:read_file(filename:dirname(filename:dirname(code:where_is_file("neotoma.beam"))) ++ "/priv/peg_includes.erl"),
file:write_file(OutputFilename, [ModuleAttrs, "\n", Code, "\n", EntryFuns, "\n", Rules, "\n", TransformFun, "\n", PegIncludes]).
validate_params(InputGrammar, _, _, OutputFile) when InputGrammar =:= OutputFile ->
View
727 src/neotoma/src/neotoma_parse.erl
@@ -3,395 +3,262 @@
-compile(nowarn_unused_vars).
-compile({nowarn_unused_function,[p/4, p/5, p_eof/0, p_optional/1, p_not/1, p_assert/1, p_seq/1, p_and/1, p_choose/1, p_zero_or_more/1, p_one_or_more/1, p_label/2, p_string/1, p_anything/0, p_charclass/1, line/1, column/1]}).
-escape_quotes(String) ->
- {ok, RE} = re:compile("\""),
- re:replace(String, RE, "\\\\\"", [global, {return, binary}]).
+
+
+% insert escapes into a string
+escape_string(String) -> escape_string(String, []).
+
+escape_string([], Output) ->
+ lists:reverse(Output);
+escape_string([H|T], Output) ->
+ escape_string(T,
+ case H of
+ $/ -> [$/,$\\|Output];
+ $\" -> [$\",$\\|Output]; % " comment inserted to help some editors with highlighting the generated parser
+ $\' -> [$\',$\\|Output]; % ' comment inserted to help some editors with highlighting the generated parser
+ $\b -> [$b,$\\|Output];
+ $\d -> [$d,$\\|Output];
+ $\e -> [$e,$\\|Output];
+ $\f -> [$f,$\\|Output];
+ $\n -> [$n,$\\|Output];
+ $\r -> [$r,$\\|Output];
+ $\s -> [$s,$\\|Output];
+ $\t -> [$t,$\\|Output];
+ $\v -> [$v,$\\|Output];
+ _ -> [H|Output]
+ end).
add_lhs(Symbol, Index) ->
- case ets:lookup(memo_table_name(), lhs) of
- [] ->
- ets:insert(memo_table_name(), {lhs, [{Symbol,Index}]});
- [{lhs, L}] when is_list(L) ->
- ets:insert(memo_table_name(), {lhs, [{Symbol,Index}|L]})
- end.
+ case ets:lookup(memo_table_name(), lhs) of
+ [] ->
+ ets:insert(memo_table_name(), {lhs, [{Symbol,Index}]});
+ [{lhs, L}] when is_list(L) ->
+ ets:insert(memo_table_name(), {lhs, [{Symbol,Index}|L]})
+ end.
add_nt(Symbol, Index) ->
- case ets:lookup(memo_table_name(), nts) of
- [] ->
- ets:insert(memo_table_name(), {nts, [{Symbol,Index}]});
- [{nts, L}] when is_list(L) ->
- case proplists:is_defined(Symbol, L) of
- true ->
- ok;
- _ ->
- ets:insert(memo_table_name(), {nts, [{Symbol,Index}|L]})
- end
- end.
+ case ets:lookup(memo_table_name(), nts) of
+ [] ->
+ ets:insert(memo_table_name(), {nts, [{Symbol,Index}]});
+ [{nts, L}] when is_list(L) ->
+ case proplists:is_defined(Symbol, L) of
+ true ->
+ ok;
+ _ ->
+ ets:insert(memo_table_name(), {nts, [{Symbol,Index}|L]})
+ end
+ end.
verify_rules() ->
- [{lhs, LHS}] = ets:lookup(memo_table_name(), lhs),
- [{nts, NTs}] = ets:lookup(memo_table_name(), nts),
- [Root|NonRoots] = lists:reverse(LHS),
- lists:foreach(fun({Sym,Idx}) ->
- case proplists:is_defined(Sym, NTs) of
- true ->
- ok;
- _ ->
- io:format("neotoma warning: rule '~s' is unused. ~p~n", [Sym,Idx])
- end
- end, NonRoots),
- lists:foreach(fun({S,I}) ->
- case proplists:is_defined(S, LHS) of
- true ->
- ok;
- _ ->
- io:format("neotoma error: nonterminal '~s' has no reduction. (found at ~p) No parser will be generated!~n", [S,I]),
- exit({neotoma, {no_reduction, list_to_atom(binary_to_list(S))}})
- end
- end, NTs),
+ [{lhs, LHS}] = ets:lookup(memo_table_name(), lhs),
+ [{nts, NTs}] = ets:lookup(memo_table_name(), nts),
+ [Root|NonRoots] = lists:reverse(LHS),
+ lists:foreach(fun({Sym,Idx}) ->
+ case proplists:is_defined(Sym, NTs) of
+ true ->
+ ok;
+ _ ->
+ io:format("neotoma warning: rule '~s' is unused. ~p~n", [Sym,Idx])
+ end
+ end, NonRoots),
+ lists:foreach(fun({S,I}) ->
+ case proplists:is_defined(S, LHS) of
+ true ->
+ ok;
+ _ ->
+ io:format("neotoma error: nonterminal '~s' has no reduction. (found at ~p) No parser will be generated!~n", [S,I]),
+ exit({neotoma, {no_reduction, list_to_atom(binary_to_list(S))}})
+ end
+ end, NTs),
Root.
+
+
file(Filename) -> {ok, Bin} = file:read_file(Filename), parse(Bin).
parse(List) when is_list(List) -> parse(list_to_binary(List));
parse(Input) when is_binary(Input) ->
- setup_memo(),
- Result = case 'rules'(Input,{{line,1},{column,1}}) of
- {AST, <<>>, _Index} -> AST;
- Any -> Any
- end,
- release_memo(), Result.
+ setup_memo(),
+ Result = case 'rules'(Input,{{line,1},{column,1}}) of
+ {AST, <<>>, _Index} -> AST;
+ Any -> Any
+ end,
+ release_memo(), Result.
'rules'(Input, Index) ->
- p(Input,
- Index,
- 'rules',
- fun(I,D) -> (p_seq([
- p_optional(fun 'space'/2),
- fun 'declaration_sequence'/2,
- p_optional(fun 'space'/2),
- p_optional(fun 'code_block'/2),
- p_optional(fun 'space'/2)
- ]))(I,D) end,
- fun(Node, Idx) ->
- RootRule = verify_rules(),
- Rules = iolist_to_binary(lists:map(fun(R) -> [R, "\n\n"] end, lists:nth(2, Node))),
- Code = case lists:nth(4, Node) of
- {code, Block} -> Block;
- _ -> []
- end,
- [{rules, Rules},
- {code, Code},
- {root, RootRule},
- {transform, ets:lookup(memo_table_name(),gen_transform)}]
- end).
+ p(Input, Index, 'rules', fun(I,D) -> (p_seq([p_optional(fun 'space'/2), fun 'declaration_sequence'/2, p_optional(fun 'space'/2), p_optional(fun 'code_block'/2), p_optional(fun 'space'/2)]))(I,D) end, fun(Node, Idx) ->
+ RootRule = verify_rules(),
+ Rules = iolist_to_binary(lists:map(fun(R) -> [R, "\n\n"] end, lists:nth(2, Node))),
+ Code = case lists:nth(4, Node) of
+ {code, Block} -> Block;
+ _ -> []
+ end,
+ [{rules, Rules},
+ {code, Code},
+ {root, RootRule},
+ {transform, ets:lookup(memo_table_name(),gen_transform)}]
+ end).
'declaration_sequence'(Input, Index) ->
- p(Input,
- Index,
- 'declaration_sequence',
- fun(I,D) -> (p_seq([
- p_label('head', fun 'declaration'/2),
- p_label('tail',
- p_zero_or_more(p_seq([
- fun 'space'/2,
- fun 'declaration'/2
- ])))
- ]))(I,D) end,
- fun(Node, Idx) ->
- FirstRule = proplists:get_value(head, Node),
- OtherRules = [lists:last(I) || I <- proplists:get_value(tail, Node, [])],
- [FirstRule|OtherRules]
- end).
+ p(Input, Index, 'declaration_sequence', fun(I,D) -> (p_seq([p_label('head', fun 'declaration'/2), p_label('tail', p_zero_or_more(p_seq([fun 'space'/2, fun 'declaration'/2])))]))(I,D) end, fun(Node, Idx) ->
+ FirstRule = proplists:get_value(head, Node),
+ OtherRules = [I || [_,I] <- proplists:get_value(tail, Node, [])],
+ [FirstRule|OtherRules]
+ end).
'declaration'(Input, Index) ->
- p(Input,
- Index,
- 'declaration',
- fun(I,D) -> (p_seq([
- fun 'nonterminal'/2,
- fun 'space'/2,
- p_string("<-"),
- fun 'space'/2,
- fun 'parsing_expression'/2,
- p_optional(fun 'space'/2),
- p_optional(fun 'code_block'/2),
- p_optional(fun 'space'/2),
- p_string(";")]
- ))(I,D) end,
- fun(Node, Idx) ->
- [{nonterminal,Symbol}|Tail] = Node,
- add_lhs(Symbol, Index),
- Transform = case lists:nth(6,Tail) of
- {code, CodeBlock} -> CodeBlock;
- _ ->
- ets:insert_new(memo_table_name(),{gen_transform, true}),
- ["transform('",Symbol,"', Node, Idx)"]
- end,
- ["'",Symbol,"'","(Input, Index) ->\n ",
- "p(Input, Index, '",Symbol,"', fun(I,D) -> (",
- lists:nth(4, Tail),
- ")(I,D) end, fun(Node, Idx) -> ",Transform," end)."]
- end).
+ p(Input, Index, 'declaration', fun(I,D) -> (p_seq([fun 'nonterminal'/2, fun 'space'/2, p_string(<<"<-">>), fun 'space'/2, fun 'parsing_expression'/2, p_optional(fun 'space'/2), p_optional(fun 'code_block'/2), p_optional(fun 'space'/2), p_string(<<";">>)]))(I,D) end, fun(Node, Idx) ->
+ [{nonterminal,Symbol}|Tail] = Node,
+ add_lhs(Symbol, Index),
+ Transform = case lists:nth(6,Tail) of
+ {code, CodeBlock} -> CodeBlock;
+ _ ->
+ ets:insert_new(memo_table_name(),{gen_transform, true}),
+ ["transform('",Symbol,"', Node, Idx)"]
+ end,
+ ["'",Symbol,"'","(Input, Index) ->\n ",
+ "p(Input, Index, '",Symbol,"', fun(I,D) -> (",
+ lists:nth(4, Tail),
+ ")(I,D) end, fun(Node, Idx) -> ",Transform," end)."]
+ end).
'parsing_expression'(Input, Index) ->
- p(Input,
- Index,
- 'parsing_expression',
- fun(I,D) -> (p_choose([
- fun 'choice'/2,
- fun 'sequence'/2,
- fun 'primary'/2
- ]))(I,D) end,
- fun(Node, Idx) -> Node end).
+ p(Input, Index, 'parsing_expression', fun(I,D) -> (p_choose([fun 'choice'/2, fun 'sequence'/2, fun 'primary'/2]))(I,D) end, fun(Node, Idx) -> Node end).
'choice'(Input, Index) ->
- p(Input,
- Index,
- 'choice',
- fun(I,D) -> (p_seq([
- p_label('head', fun 'alternative'/2),
- p_label('tail', p_one_or_more(p_seq([
- fun 'space'/2,
- p_string("/"),
- fun 'space'/2,
- fun 'alternative'/2
- ])))
- ]))(I,D) end,
- fun(Node, Idx) ->
- Tail = [lists:last(S) || S <- proplists:get_value(tail, Node)],
- Head = proplists:get_value(head, Node),
- Statements = [[", ", TS] || TS <- Tail],
- ["p_choose([", Head, Statements, "])"]
- end).
+ p(Input, Index, 'choice', fun(I,D) -> (p_seq([p_label('head', fun 'alternative'/2), p_label('tail', p_one_or_more(p_seq([fun 'space'/2, p_string(<<"\/">>), fun 'space'/2, fun 'alternative'/2])))]))(I,D) end, fun(Node, Idx) ->
+ Tail = [lists:last(S) || S <- proplists:get_value(tail, Node)],
+ Head = proplists:get_value(head, Node),
+ Statements = [[", ", TS] || TS <- Tail],
+ ["p_choose([", Head, Statements, "])"]
+ end).
'alternative'(Input, Index) ->
- p(Input,
- Index,
- 'alternative',
- fun(I,D) -> (p_choose([fun 'sequence'/2, fun 'labeled_primary'/2]))(I,D) end,
- fun(Node, Idx) -> Node end).
+ p(Input, Index, 'alternative', fun(I,D) -> (p_choose([fun 'sequence'/2, fun 'labeled_primary'/2]))(I,D) end, fun(Node, Idx) -> Node end).
'primary'(Input, Index) ->
- p(Input,
- Index,
- 'primary',
- fun(I,D) -> (p_choose([p_seq([
- fun 'prefix'/2,
- fun 'atomic'/2
- ]),
- p_seq([
- fun 'atomic'/2,
- fun 'suffix'/2
- ]),
- fun 'atomic'/2
- ]))(I,D) end,
- fun(Node, Idx) ->
- case Node of
- [Atomic, one_or_more] -> ["p_one_or_more(", Atomic, ")"];
- [Atomic, zero_or_more] -> ["p_zero_or_more(", Atomic, ")"];
- [Atomic, optional] -> ["p_optional(", Atomic, ")"];
- [assert, Atomic] -> ["p_assert(", Atomic, ")"];
- [not_, Atomic] -> ["p_not(", Atomic, ")"];
- _ -> Node
- end
- end).
+ p(Input, Index, 'primary', fun(I,D) -> (p_choose([p_seq([fun 'prefix'/2, fun 'atomic'/2]), p_seq([fun 'atomic'/2, fun 'suffix'/2]), fun 'atomic'/2]))(I,D) end, fun(Node, Idx) ->
+case Node of
+ [Atomic, one_or_more] -> ["p_one_or_more(", Atomic, ")"];
+ [Atomic, zero_or_more] -> ["p_zero_or_more(", Atomic, ")"];
+ [Atomic, optional] -> ["p_optional(", Atomic, ")"];
+ [assert, Atomic] -> ["p_assert(", Atomic, ")"];
+ [not_, Atomic] -> ["p_not(", Atomic, ")"];
+ _ -> Node
+end
+ end).
'sequence'(Input, Index) ->
- p(Input,
- Index,
- 'sequence',
- fun(I,D) -> (p_seq([
- p_label('head', fun 'labeled_primary'/2),
- p_label('tail', p_one_or_more(p_seq([
- fun 'space'/2,
- fun 'labeled_primary'/2
- ])))
- ]))(I,D) end,
- fun(Node, Idx) ->
- Tail = [lists:nth(2, S) || S <- proplists:get_value(tail, Node)],
- Head = proplists:get_value(head, Node),
- Statements = [[", ", TS] || TS <- Tail],
- ["p_seq([", Head, Statements, "])"]
- end).
+ p(Input, Index, 'sequence', fun(I,D) -> (p_seq([p_label('head', fun 'labeled_primary'/2), p_label('tail', p_one_or_more(p_seq([fun 'space'/2, fun 'labeled_primary'/2])))]))(I,D) end, fun(Node, Idx) ->
+ Tail = [lists:nth(2, S) || S <- proplists:get_value(tail, Node)],
+ Head = proplists:get_value(head, Node),
+ Statements = [[", ", TS] || TS <- Tail],
+ ["p_seq([", Head, Statements, "])"]
+ end).
'labeled_primary'(Input, Index) ->
- p(Input,
- Index,
- 'labeled_primary',
- fun(I,D) -> (p_seq([
- p_optional(fun 'label'/2),
- fun 'primary'/2
- ]))(I,D) end,
- fun(Node, Idx) ->
- case hd(Node) of
- [] -> lists:nth(2, Node);
- Label -> ["p_label('", Label, "', ", lists:nth(2, Node), ")"]
- end
- end).
+ p(Input, Index, 'labeled_primary', fun(I,D) -> (p_seq([p_optional(fun 'label'/2), fun 'primary'/2]))(I,D) end, fun(Node, Idx) ->
+ case hd(Node) of
+ [] -> lists:nth(2, Node);
+ Label -> ["p_label('", Label, "', ", lists:nth(2, Node), ")"]
+ end
+ end).
'label'(Input, Index) ->
- p(Input,
- Index,
- 'label',
- fun(I,D) -> (p_seq([
- fun 'alpha_char'/2,
- p_zero_or_more(fun 'alphanumeric_char'/2),
- p_string(":")
- ]))(I,D) end,
- fun(Node, Idx) ->
- lists:sublist(Node, length(Node)-1)
- end).
+ p(Input, Index, 'label', fun(I,D) -> (p_seq([fun 'alpha_char'/2, p_zero_or_more(fun 'alphanumeric_char'/2), p_string(<<":">>)]))(I,D) end, fun(Node, Idx) ->
+ lists:sublist(Node, length(Node)-1)
+ end).
'suffix'(Input, Index) ->
- p(Input,
- Index,
- 'suffix',
- fun(I,D) -> (p_choose([
- fun 'repetition_suffix'/2,
- fun 'optional_suffix'/2
- ]))(I,D) end,
- fun(Node, Idx) ->
- case Node of
- <<"*">> -> zero_or_more;
- <<"+">> -> one_or_more;
- <<"?">> -> optional
- end
- end).
+ p(Input, Index, 'suffix', fun(I,D) -> (p_choose([fun 'repetition_suffix'/2, fun 'optional_suffix'/2]))(I,D) end, fun(Node, Idx) ->
+ case Node of
+ <<"*">> -> zero_or_more;
+ <<"+">> -> one_or_more;
+ <<"?">> -> optional
+ end
+ end).
'optional_suffix'(Input, Index) ->
- p(Input, Index, 'optional_suffix', fun(I,D) -> (p_string("?"))(I,D) end, fun(Node, Idx) -> Node end).
+ p(Input, Index, 'optional_suffix', fun(I,D) -> (p_string(<<"?">>))(I,D) end, fun(Node, Idx) -> Node end).
'repetition_suffix'(Input, Index) ->
- p(Input, Index, 'repetition_suffix', fun(I,D) -> (p_choose([p_string("+"), p_string("*")]))(I,D) end, fun(Node, Idx) -> Node end).
+ p(Input, Index, 'repetition_suffix', fun(I,D) -> (p_choose([p_string(<<"+">>), p_string(<<"*">>)]))(I,D) end, fun(Node, Idx) -> Node end).
'prefix'(Input, Index) ->
- p(Input, Index, 'prefix', fun(I,D) -> (p_choose([p_string("&"), p_string("!")]))(I,D) end,
- fun(Node, Idx) ->
- case Node of
- <<"&">> -> assert;
- <<"!">> -> not_
- end
- end).
+ p(Input, Index, 'prefix', fun(I,D) -> (p_choose([p_string(<<"&">>), p_string(<<"!">>)]))(I,D) end, fun(Node, Idx) ->
+ case Node of
+ <<"&">> -> assert;
+ <<"!">> -> not_
+ end
+ end).
'atomic'(Input, Index) ->
- p(Input, Index, 'atomic', fun(I,D) -> (p_choose([
- fun 'terminal'/2,
- fun 'nonterminal'/2,
- fun 'parenthesized_expression'/2
- ]))(I,D) end,
- fun(Node, Idx) ->
- case Node of
- {nonterminal, Symbol} ->
- [<<"fun '">>, Symbol, <<"'/2">>];
- _ -> Node
- end
- end).
+ p(Input, Index, 'atomic', fun(I,D) -> (p_choose([fun 'terminal'/2, fun 'nonterminal'/2, fun 'parenthesized_expression'/2]))(I,D) end, fun(Node, Idx) ->
+case Node of
+ {nonterminal, Symbol} ->
+ [<<"fun '">>, Symbol, <<"'/2">>];
+ _ -> Node
+end
+ end).
'parenthesized_expression'(Input, Index) ->
- p(Input, Index, 'parenthesized_expression', fun(I,D) -> (p_seq([
- p_string("("),
- p_optional(fun 'space'/2),
- fun 'parsing_expression'/2,
- p_optional(fun 'space'/2),
- p_string(")")
- ]))(I,D) end,
- fun(Node, Idx) -> lists:nth(3, Node) end).
+ p(Input, Index, 'parenthesized_expression', fun(I,D) -> (p_seq([p_string(<<"(">>), p_optional(fun 'space'/2), fun 'parsing_expression'/2, p_optional(fun 'space'/2), p_string(<<")">>)]))(I,D) end, fun(Node, Idx) -> lists:nth(3, Node) end).
'nonterminal'(Input, Index) ->
- p(Input,
- Index,
- 'nonterminal',
- fun(I,D) -> (p_seq([
- fun 'alpha_char'/2,
- p_zero_or_more(fun 'alphanumeric_char'/2)
- ]))(I,D) end,
- fun(Node, Idx) ->
- Symbol = iolist_to_binary(Node),
- add_nt(Symbol, Idx),
- {nonterminal, Symbol}
- end).
+ p(Input, Index, 'nonterminal', fun(I,D) -> (p_seq([fun 'alpha_char'/2, p_zero_or_more(fun 'alphanumeric_char'/2)]))(I,D) end, fun(Node, Idx) ->
+ Symbol = iolist_to_binary(Node),
+ add_nt(Symbol, Idx),
+ {nonterminal, Symbol}
+ end).
'terminal'(Input, Index) ->
- p(Input,
- Index,
- 'terminal',
- fun(I,D) -> (p_choose([
- fun 'quoted_string'/2,
- fun 'character_class'/2,
- fun 'anything_symbol'/2
- ]))(I,D) end,
- fun(Node, Idx) -> Node end).
+ p(Input, Index, 'terminal', fun(I,D) -> (p_choose([fun 'quoted_string'/2, fun 'character_class'/2, fun 'anything_symbol'/2]))(I,D) end, fun(Node, Idx) -> Node end).
'quoted_string'(Input, Index) ->
- p(Input,
- Index,
- 'quoted_string',
- fun(I,D) -> (p_choose([
- fun 'single_quoted_string'/2,
- fun 'double_quoted_string'/2
- ]))(I,D) end,
- fun(Node, Idx) ->
- ["p_string(",
- io_lib:format("~p",[iolist_to_binary(proplists:get_value(string, Node))]),
- ")"]
- end).
+ p(Input, Index, 'quoted_string', fun(I,D) -> (p_choose([fun 'single_quoted_string'/2, fun 'double_quoted_string'/2]))(I,D) end, fun(Node, Idx) ->
+ lists:flatten(["p_string(<<\"",
+ escape_string(binary_to_list(iolist_to_binary(proplists:get_value(string, Node)))),
+ "\">>)"])
+ end).
'double_quoted_string'(Input, Index) ->
- p(Input,
- Index,
- 'double_quoted_string',
- fun(I,D) -> (p_seq([
- p_string("\""),
- p_label('string', p_zero_or_more(p_seq([
- p_not(p_string("\"")),
- p_choose([
- p_string("\\\\"),
- p_string("\\\""),
- p_anything()]
- )
- ]))),
- p_string("\"")
- ]))(I,D) end,
- fun(Node, Idx) -> Node end).
+ p(Input, Index, 'double_quoted_string', fun(I,D) -> (p_seq([p_string(<<"\"">>), p_label('string', p_zero_or_more(p_seq([p_not(p_string(<<"\"">>)), p_choose([p_string(<<"\\\\">>), p_string(<<"\\\"">>), p_anything()])]))), p_string(<<"\"">>)]))(I,D) end, fun(Node, Idx) -> Node end).
'single_quoted_string'(Input, Index) ->
- p(Input, Index, 'single_quoted_string', fun(I,D) -> (p_seq([p_string("'"), p_label('string', p_zero_or_more(p_seq([p_not(p_string("'")), p_choose([p_string("\\\\"), p_string("\\'"), p_anything()])]))), p_string("'")]))(I,D) end, fun(Node, Idx) -> Node end).
+ p(Input, Index, 'single_quoted_string', fun(I,D) -> (p_seq([p_string(<<"\'">>), p_label('string', p_zero_or_more(p_seq([p_not(p_string(<<"\'">>)), p_choose([p_string(<<"\\\\">>), p_string(<<"\\\'">>), p_anything()])]))), p_string(<<"\'">>)]))(I,D) end, fun(Node, Idx) -> Node end).
'character_class'(Input, Index) ->
- p(Input, Index, 'character_class', fun(I,D) -> (p_seq([p_string("["), p_label('characters', p_one_or_more(p_seq([p_not(p_string("]")), p_choose([p_seq([p_string("\\\\"), p_anything()]), p_seq([p_not(p_string("\\\\")), p_anything()])])]))), p_string("]")]))(I,D) end,
- fun(Node, Idx) ->
- ["p_charclass(<<\"[",
- escape_quotes(io_lib:format("~s", [iolist_to_binary(proplists:get_value(characters, Node))])),
- "]\">>)"]
- end).
+ p(Input, Index, 'character_class', fun(I,D) -> (p_seq([p_string(<<"[">>), p_label('characters', p_one_or_more(p_seq([p_not(p_string(<<"]">>)), p_choose([p_seq([p_string(<<"\\\\">>), p_anything()]), p_seq([p_not(p_string(<<"\\\\">>)), p_anything()])])]))), p_string(<<"]">>)]))(I,D) end, fun(Node, Idx) ->
+["p_charclass(<<\"[",
+ escape_string(binary_to_list(iolist_to_binary(proplists:get_value(characters, Node)))),
+ "]\">>)"]
+ end).
'anything_symbol'(Input, Index) ->
- p(Input, Index, 'anything_symbol', fun(I,D) -> (p_string("."))(I,D) end, fun(Node, Idx) -> <<"p_anything()">> end).
+ p(Input, Index, 'anything_symbol', fun(I,D) -> (p_string(<<".">>))(I,D) end, fun(Node, Idx) -> <<"p_anything()">> end).
'alpha_char'(Input, Index) ->
- p(Input, Index, 'alpha_char', fun(I,D) -> (p_charclass("[A-Za-z_]"))(I,D) end, fun(Node, Idx) -> Node end).
+ p(Input, Index, 'alpha_char', fun(I,D) -> (p_charclass(<<"[A-Za-z_]">>))(I,D) end, fun(Node, Idx) -> Node end).
'alphanumeric_char'(Input, Index) ->
- p(Input, Index, 'alphanumeric_char', fun(I,D) -> (p_choose([fun 'alpha_char'/2, p_charclass("[0-9]")]))(I,D) end, fun(Node, Idx) -> Node end).
+ p(Input, Index, 'alphanumeric_char', fun(I,D) -> (p_choose([fun 'alpha_char'/2, p_charclass(<<"[0-9]">>)]))(I,D) end, fun(Node, Idx) -> Node end).
'space'(Input, Index) ->
- p(Input, Index, 'space', fun(I,D) -> (p_one_or_more(p_choose([fun 'white'/2, fun 'comment_to_eol'/2])))(I,D) end, fun(Node, Idx) -> Node end).
+ p(Input, Index, 'space', fun(I,D) -> (p_one_or_more(p_choose([fun 'white'/2, fun 'comment_to_eol'/2])))(I,D) end, fun(Node, Idx) -> Node end).
'comment_to_eol'(Input, Index) ->
- p(Input, Index, 'comment_to_eol', fun(I,D) -> (p_seq([p_not(p_string("%{")), p_string("%"), p_zero_or_more(p_seq([p_not(p_string("\n")), p_anything()]))]))(I,D) end, fun(Node, Idx) -> Node end).
+ p(Input, Index, 'comment_to_eol', fun(I,D) -> (p_seq([p_not(p_string(<<"%{">>)), p_string(<<"%">>), p_zero_or_more(p_seq([p_not(p_string(<<"\n">>)), p_anything()]))]))(I,D) end, fun(Node, Idx) -> Node end).
'white'(Input, Index) ->
- p(Input, Index, 'white', fun(I,D) -> (p_charclass("[ \t\n\r]"))(I,D) end, fun(Node, Idx) -> Node end).
+ p(Input, Index, 'white', fun(I,D) -> (p_charclass(<<"[\s\t\n\r]">>))(I,D) end, fun(Node, Idx) -> Node end).
'code_block'(Input, Index) ->
- p(Input, Index, 'code_block', fun(I,D) -> (p_choose([p_seq([p_string("%{"), p_label('code', p_one_or_more(p_choose([p_string("\\%"), p_string("$%"), p_seq([p_not(p_string("%}")), p_anything()])]))), p_string("%}")]), p_seq([p_string("`"), p_label('code', p_one_or_more(p_choose([p_string("\\`"), p_string("$`"), p_seq([p_not(p_string("`")), p_anything()])]))), p_string("`")]), p_string("~")]))(I,D) end,
- fun(Node, Idx) ->
- case Node of
- <<"~">> -> {code, <<"Node">>};
- _ -> {code, proplists:get_value('code', Node)}
- end
- end).
+ p(Input, Index, 'code_block', fun(I,D) -> (p_choose([p_seq([p_string(<<"%{">>), p_label('code', p_one_or_more(p_choose([p_string(<<"\\%">>), p_string(<<"$%">>), p_seq([p_not(p_string(<<"%}">>)), p_anything()])]))), p_string(<<"%}">>)]), p_seq([p_string(<<"`">>), p_label('code', p_one_or_more(p_choose([p_string(<<"\\`">>), p_string(<<"$`">>), p_seq([p_not(p_string(<<"`">>)), p_anything()])]))), p_string(<<"`">>)]), p_string(<<"~">>)]))(I,D) end, fun(Node, Idx) ->
+ case Node of
+ <<"~">> -> {code, <<"Node">>};
+ _ -> {code, proplists:get_value('code', Node)}
+ end
+ end).
@@ -401,160 +268,160 @@ parse(Input) when is_binary(Input) ->
p(Inp, Index, Name, ParseFun) ->
- p(Inp, Index, Name, ParseFun, fun(N, _Idx) -> N end).
+ p(Inp, Index, Name, ParseFun, fun(N, _Idx) -> N end).
p(Inp, StartIndex, Name, ParseFun, TransformFun) ->
- % Grab the memo table from ets
- Memo = get_memo(StartIndex),
- % See if the current reduction is memoized
- case proplists:lookup(Name, Memo) of
- % If it is, return the result
- {Name, Result} -> Result;
- % If not, attempt to parse
- _ ->
- case ParseFun(Inp, StartIndex) of
- % If it fails, memoize the failure
- {fail,_} = Failure ->
- memoize(StartIndex, [{Name, Failure}|Memo]),
- Failure;
- % If it passes, transform and memoize the result.
- {Result, InpRem, NewIndex} ->
- Transformed = TransformFun(Result, StartIndex),
- memoize(StartIndex, [{Name, {Transformed, InpRem, NewIndex}}|Memo]),
- {Transformed, InpRem, NewIndex}
- end
- end.
+ % Grab the memo table from ets
+ Memo = get_memo(StartIndex),
+ % See if the current reduction is memoized
+ case proplists:lookup(Name, Memo) of
+ % If it is, return the result
+ {Name, Result} -> Result;
+ % If not, attempt to parse
+ _ ->
+ case ParseFun(Inp, StartIndex) of
+ % If it fails, memoize the failure
+ {fail,_} = Failure ->
+ memoize(StartIndex, [{Name, Failure}|Memo]),
+ Failure;
+ % If it passes, transform and memoize the result.
+ {Result, InpRem, NewIndex} ->
+ Transformed = TransformFun(Result, StartIndex),
+ memoize(StartIndex, [{Name, {Transformed, InpRem, NewIndex}}|Memo]),
+ {Transformed, InpRem, NewIndex}
+ end
+ end.
setup_memo() ->
- put(parse_memo_table, ets:new(?MODULE, [set])).
+ put(parse_memo_table, ets:new(?MODULE, [set])).
release_memo() ->
- ets:delete(memo_table_name()).
+ ets:delete(memo_table_name()).
memoize(Position, Struct) ->
- ets:insert(memo_table_name(), {Position, Struct}).
+ ets:insert(memo_table_name(), {Position, Struct}).
get_memo(Position) ->
- case ets:lookup(memo_table_name(), Position) of
- [] -> [];
- [{Position, PList}] -> PList
- end.
+ case ets:lookup(memo_table_name(), Position) of
+ [] -> [];
+ [{Position, PList}] -> PList
+ end.
memo_table_name() ->
get(parse_memo_table).
p_eof() ->
- fun(<<>>, Index) -> {eof, [], Index};
- (_, Index) -> {fail, {expected, eof, Index}} end.
+ fun(<<>>, Index) -> {eof, [], Index};
+ (_, Index) -> {fail, {expected, eof, Index}} end.
p_optional(P) ->
- fun(Input, Index) ->
- case P(Input, Index) of
- {fail,_} -> {[], Input, Index};
- {_, _, _} = Success -> Success
- end
- end.
+ fun(Input, Index) ->
+ case P(Input, Index) of
+ {fail,_} -> {[], Input, Index};
+ {_, _, _} = Success -> Success
+ end
+ end.
p_not(P) ->
- fun(Input, Index)->
- case P(Input,Index) of
- {fail,_} ->
- {[], Input, Index};
- {Result, _, _} -> {fail, {expected, {no_match, Result},Index}}
- end
- end.
+ fun(Input, Index)->
+ case P(Input,Index) of
+ {fail,_} ->
+ {[], Input, Index};
+ {Result, _, _} -> {fail, {expected, {no_match, Result},Index}}
+ end
+ end.
p_assert(P) ->
- fun(Input,Index) ->
- case P(Input,Index) of
- {fail,_} = Failure-> Failure;
- _ -> {[], Input, Index}
- end
- end.
+ fun(Input,Index) ->
+ case P(Input,Index) of
+ {fail,_} = Failure-> Failure;
+ _ -> {[], Input, Index}
+ end
+ end.
p_and(P) ->
- p_seq(P).
+ p_seq(P).
p_seq(P) ->
- fun(Input, Index) ->
- p_all(P, Input, Index, [])
- end.
+ fun(Input, Index) ->
+ p_all(P, Input, Index, [])
+ end.
p_all([], Inp, Index, Accum ) -> {lists:reverse( Accum ), Inp, Index};
p_all([P|Parsers], Inp, Index, Accum) ->
- case P(Inp, Index) of
- {fail, _} = Failure -> Failure;
- {Result, InpRem, NewIndex} -> p_all(Parsers, InpRem, NewIndex, [Result|Accum])
- end.
+ case P(Inp, Index) of
+ {fail, _} = Failure -> Failure;
+ {Result, InpRem, NewIndex} -> p_all(Parsers, InpRem, NewIndex, [Result|Accum])
+ end.
p_choose(Parsers) ->
- fun(Input, Index) ->
- p_attempt(Parsers, Input, Index, none)
- end.
+ fun(Input, Index) ->
+ p_attempt(Parsers, Input, Index, none)
+ end.
p_attempt([], _Input, _Index, Failure) -> Failure;
p_attempt([P|Parsers], Input, Index, FirstFailure)->
- case P(Input, Index) of
- {fail, _} = Failure ->
- case FirstFailure of
- none -> p_attempt(Parsers, Input, Index, Failure);
- _ -> p_attempt(Parsers, Input, Index, FirstFailure)
- end;
- Result -> Result
- end.
+ case P(Input, Index) of
+ {fail, _} = Failure ->
+ case FirstFailure of
+ none -> p_attempt(Parsers, Input, Index, Failure);
+ _ -> p_attempt(Parsers, Input, Index, FirstFailure)
+ end;
+ Result -> Result
+ end.
p_zero_or_more(P) ->
- fun(Input, Index) ->
- p_scan(P, Input, Index, [])
- end.
+ fun(Input, Index) ->
+ p_scan(P, Input, Index, [])
+ end.
p_one_or_more(P) ->
- fun(Input, Index)->
- Result = p_scan(P, Input, Index, []),
- case Result of
- {[_|_], _, _} ->
- Result;
- _ ->
- {fail, {expected, Failure, _}} = P(Input,Index),
- {fail, {expected, {at_least_one, Failure}, Index}}
- end
- end.
+ fun(Input, Index)->
+ Result = p_scan(P, Input, Index, []),
+ case Result of
+ {[_|_], _, _} ->
+ Result;
+ _ ->
+ {fail, {expected, Failure, _}} = P(Input,Index),
+ {fail, {expected, {at_least_one, Failure}, Index}}
+ end
+ end.
p_label(Tag, P) ->
- fun(Input, Index) ->
- case P(Input, Index) of
- {fail,_} = Failure ->
- Failure;
- {Result, InpRem, NewIndex} ->
- {{Tag, Result}, InpRem, NewIndex}
- end
- end.
+ fun(Input, Index) ->
+ case P(Input, Index) of
+ {fail,_} = Failure ->
+ Failure;
+ {Result, InpRem, NewIndex} ->
+ {{Tag, Result}, InpRem, NewIndex}
+ end
+ end.
p_scan(_, [], Index, Accum) -> {lists:reverse( Accum ), [], Index};
p_scan(P, Inp, Index, Accum) ->
- case P(Inp, Index) of
- {fail,_} -> {lists:reverse(Accum), Inp, Index};
- {Result, InpRem, NewIndex} -> p_scan(P, InpRem, NewIndex, [Result | Accum])
- end.
+ case P(Inp, Index) of
+ {fail,_} -> {lists:reverse(Accum), Inp, Index};
+ {Result, InpRem, NewIndex} -> p_scan(P, InpRem, NewIndex, [Result | Accum])
+ end.
p_string(S) when is_list(S) -> p_string(list_to_binary(S));
p_string(S) ->
Length = erlang:byte_size(S),
fun(Input, Index) ->
- try
- <<S:Length/binary, Rest/binary>> = Input,
- {S, Rest, p_advance_index(S, Index)}
- catch
- error:{badmatch,_} -> {fail, {expected, {string, S}, Index}}
- end
+ try
+ <<S:Length/binary, Rest/binary>> = Input,
+ {S, Rest, p_advance_index(S, Index)}
+ catch
+ error:{badmatch,_} -> {fail, {expected, {string, S}, Index}}
+ end
end.
p_anything() ->
- fun(<<>>, Index) -> {fail, {expected, any_character, Index}};
- (Input, Index) when is_binary(Input) ->
- <<C/utf8, Rest/binary>> = Input,
- {<<C/utf8>>, Rest, p_advance_index(<<C/utf8>>, Index)}
- end.
+ fun(<<>>, Index) -> {fail, {expected, any_character, Index}};
+ (Input, Index) when is_binary(Input) ->
+ <<C/utf8, Rest/binary>> = Input,
+ {<<C/utf8>>, Rest, p_advance_index(<<C/utf8>>, Index)}
+ end.
p_charclass(Class) ->
{ok, RE} = re:compile(Class, [unicode, dotall]),
@@ -563,7 +430,7 @@ p_charclass(Class) ->
{match, [{0, Length}|_]} ->
{Head, Tail} = erlang:split_binary(Inp, Length),
{Head, Tail, p_advance_index(Head, Index)};
- _ -> {fail, {expected, {character_class, Class}, Index}}
+ _ -> {fail, {expected, {character_class, binary_to_list(Class)}, Index}}
end
end.
@@ -574,10 +441,10 @@ column({_,{column,C}}) -> C;
column(_) -> undefined.
p_advance_index(MatchedInput, Index) when is_list(MatchedInput) orelse is_binary(MatchedInput)-> % strings
- lists:foldl(fun p_advance_index/2, Index, unicode:characters_to_list(MatchedInput));
+ lists:foldl(fun p_advance_index/2, Index, unicode:characters_to_list(MatchedInput));
p_advance_index(MatchedInput, Index) when is_integer(MatchedInput) -> % single characters
- {{line, Line}, {column, Col}} = Index,
- case MatchedInput of
- $\n -> {{line, Line+1}, {column, 1}};
- _ -> {{line, Line}, {column, Col+1}}
- end.
+ {{line, Line}, {column, Col}} = Index,
+ case MatchedInput of
+ $\n -> {{line, Line+1}, {column, 1}};
+ _ -> {{line, Line}, {column, Col+1}}
+ end.
View
452 src/neotoma/src/neotoma_parse.erl.safe
@@ -0,0 +1,452 @@
+-module(neotoma_parse).
+-export([parse/1,file/1]).
+-compile(nowarn_unused_vars).
+-compile({nowarn_unused_function,[p/4, p/5, p_eof/0, p_optional/1, p_not/1, p_assert/1, p_seq/1, p_and/1, p_choose/1, p_zero_or_more/1, p_one_or_more/1, p_label/2, p_string/1, p_anything/0, p_charclass/1, line/1, column/1]}).
+
+
+
+% insert escapes into a string
+escape_string(String) -> escape_string(String, []).
+
+escape_string([], Output) ->
+ lists:reverse(Output);
+escape_string([H|T], Output) ->
+ escape_string(T,
+ case H of
+% $/ -> [$/,$\\|Output];
+ $\" -> [$\",$\\|Output]; % " comment inserted to help some editors with highlighting the generated parser
+% $\' -> [$\',$\\|Output]; % ' comment inserted to help some editors with highlighting the generated parser
+% $\b -> [$b,$\\|Output];
+% $\d -> [$d,$\\|Output];
+% $\e -> [$e,$\\|Output];
+% $\f -> [$f,$\\|Output];
+% $\n -> [$n,$\\|Output];
+% $\r -> [$r,$\\|Output];
+% $\s -> [$s,$\\|Output];
+% $\t -> [$t,$\\|Output];
+ $\v -> [$v,$\\|Output];
+ _ -> [H|Output]
+ end).
+
+add_lhs(Symbol, Index) ->
+ case ets:lookup(memo_table_name(), lhs) of
+ [] ->
+ ets:insert(memo_table_name(), {lhs, [{Symbol,Index}]});
+ [{lhs, L}] when is_list(L) ->
+ ets:insert(memo_table_name(), {lhs, [{Symbol,Index}|L]})
+ end.
+
+add_nt(Symbol, Index) ->
+ case ets:lookup(memo_table_name(), nts) of
+ [] ->
+ ets:insert(memo_table_name(), {nts, [{Symbol,Index}]});
+ [{nts, L}] when is_list(L) ->
+ case proplists:is_defined(Symbol, L) of
+ true ->
+ ok;
+ _ ->
+ ets:insert(memo_table_name(), {nts, [{Symbol,Index}|L]})
+ end
+ end.
+
+verify_rules() ->
+ [{lhs, LHS}] = ets:lookup(memo_table_name(), lhs),
+ [{nts, NTs}] = ets:lookup(memo_table_name(), nts),
+ [Root|NonRoots] = lists:reverse(LHS),
+ lists:foreach(fun({Sym,Idx}) ->
+ case proplists:is_defined(Sym, NTs) of
+ true ->
+ ok;
+ _ ->
+ io:format("neotoma warning: rule '~s' is unused. ~p~n", [Sym,Idx])
+ end
+ end, NonRoots),
+ lists:foreach(fun({S,I}) ->
+ case proplists:is_defined(S, LHS) of
+ true ->
+ ok;
+ _ ->
+ io:format("neotoma error: nonterminal '~s' has no reduction. (found at ~p) No parser will be generated!~n", [S,I]),
+ exit({neotoma, {no_reduction, list_to_atom(binary_to_list(S))}})
+ end
+ end, NTs),
+ Root.
+
+
+
+file(Filename) -> {ok, Bin} = file:read_file(Filename), parse(Bin).
+
+parse(List) when is_list(List) -> parse(list_to_binary(List));
+parse(Input) when is_binary(Input) ->
+ setup_memo(),
+ Result = case 'rules'(Input,{{line,1},{column,1}}) of
+ {AST, <<>>, _Index} -> AST;
+ Any -> Any
+ end,
+ release_memo(), Result.
+
+'rules'(Input, Index) ->
+ p(Input, Index, 'rules', fun(I,D) -> (p_seq([p_optional(fun 'space'/2), fun 'declaration_sequence'/2, p_optional(fun 'space'/2), p_optional(fun 'code_block'/2), p_optional(fun 'space'/2)]))(I,D) end, fun(Node, Idx) ->
+ RootRule = verify_rules(),
+ Rules = iolist_to_binary(lists:map(fun(R) -> [R, "\n\n"] end, lists:nth(2, Node))),
+ Code = case lists:nth(4, Node) of
+ {code, Block} -> Block;
+ _ -> []
+ end,
+ [{rules, Rules},
+ {code, Code},
+ {root, RootRule},
+ {transform, ets:lookup(memo_table_name(),gen_transform)}]
+ end).
+
+'declaration_sequence'(Input, Index) ->
+ p(Input, Index, 'declaration_sequence', fun(I,D) -> (p_seq([p_label('head', fun 'declaration'/2), p_label('tail', p_zero_or_more(p_seq([fun 'space'/2, fun 'declaration'/2])))]))(I,D) end, fun(Node, Idx) ->
+ FirstRule = proplists:get_value(head, Node),
+ OtherRules = [I || [_,I] <- proplists:get_value(tail, Node, [])],
+ [FirstRule|OtherRules]
+ end).
+
+'declaration'(Input, Index) ->
+ p(Input, Index, 'declaration', fun(I,D) -> (p_seq([fun 'nonterminal'/2, fun 'space'/2, p_string(<<"<-">>), fun 'space'/2, fun 'parsing_expression'/2, p_optional(fun 'space'/2), p_optional(fun 'code_block'/2), p_optional(fun 'space'/2), p_string(<<";">>)]))(I,D) end, fun(Node, Idx) ->
+ [{nonterminal,Symbol}|Tail] = Node,
+ add_lhs(Symbol, Index),
+ Transform = case lists:nth(6,Tail) of
+ {code, CodeBlock} -> CodeBlock;
+ _ ->
+ ets:insert_new(memo_table_name(),{gen_transform, true}),
+ ["transform('",Symbol,"', Node, Idx)"]
+ end,
+ ["'",Symbol,"'","(Input, Index) ->\n ",
+ "p(Input, Index, '",Symbol,"', fun(I,D) -> (",
+ lists:nth(4, Tail),
+ ")(I,D) end, fun(Node, Idx) -> ",Transform," end)."]
+ end).
+
+'parsing_expression'(Input, Index) ->
+ p(Input, Index, 'parsing_expression', fun(I,D) -> (p_choose([fun 'choice'/2, fun 'sequence'/2, fun 'primary'/2]))(I,D) end, fun(Node, Idx) -> Node end).
+
+'choice'(Input, Index) ->
+ p(Input, Index, 'choice', fun(I,D) -> (p_seq([p_label('head', fun 'alternative'/2), p_label('tail', p_one_or_more(p_seq([fun 'space'/2, p_string(<<"/">>), fun 'space'/2, fun 'alternative'/2])))]))(I,D) end, fun(Node, Idx) ->
+ Tail = [lists:last(S) || S <- proplists:get_value(tail, Node)],
+ Head = proplists:get_value(head, Node),
+ Statements = [[", ", TS] || TS <- Tail],
+ ["p_choose([", Head, Statements, "])"]
+ end).
+
+'alternative'(Input, Index) ->
+ p(Input, Index, 'alternative', fun(I,D) -> (p_choose([fun 'sequence'/2, fun 'labeled_primary'/2]))(I,D) end, fun(Node, Idx) -> Node end).
+
+'primary'(Input, Index) ->
+ p(Input, Index, 'primary', fun(I,D) -> (p_choose([p_seq([fun 'prefix'/2, fun 'atomic'/2]), p_seq([fun 'atomic'/2, fun 'suffix'/2]), fun 'atomic'/2]))(I,D) end, fun(Node, Idx) ->
+case Node of
+ [Atomic, one_or_more] -> ["p_one_or_more(", Atomic, ")"];
+ [Atomic, zero_or_more] -> ["p_zero_or_more(", Atomic, ")"];
+ [Atomic, optional] -> ["p_optional(", Atomic, ")"];
+ [assert, Atomic] -> ["p_assert(", Atomic, ")"];
+ [not_, Atomic] -> ["p_not(", Atomic, ")"];
+ _ -> Node
+end
+ end).
+
+'sequence'(Input, Index) ->
+ p(Input, Index, 'sequence', fun(I,D) -> (p_seq([p_label('head', fun 'labeled_primary'/2), p_label('tail', p_one_or_more(p_seq([fun 'space'/2, fun 'labeled_primary'/2])))]))(I,D) end, fun(Node, Idx) ->
+ Tail = [lists:nth(2, S) || S <- proplists:get_value(tail, Node)],
+ Head = proplists:get_value(head, Node),
+ Statements = [[", ", TS] || TS <- Tail],
+ ["p_seq([", Head, Statements, "])"]
+ end).
+
+'labeled_primary'(Input, Index) ->
+ p(Input, Index, 'labeled_primary', fun(I,D) -> (p_seq([p_optional(fun 'label'/2), fun 'primary'/2]))(I,D) end, fun(Node, Idx) ->
+ case hd(Node) of
+ [] -> lists:nth(2, Node);
+ Label -> ["p_label('", Label, "', ", lists:nth(2, Node), ")"]
+ end
+ end).
+
+'label'(Input, Index) ->
+ p(Input, Index, 'label', fun(I,D) -> (p_seq([fun 'alpha_char'/2, p_zero_or_more(fun 'alphanumeric_char'/2), p_string(<<":">>)]))(I,D) end, fun(Node, Idx) ->
+ lists:sublist(Node, length(Node)-1)
+ end).
+
+'suffix'(Input, Index) ->
+ p(Input, Index, 'suffix', fun(I,D) -> (p_choose([fun 'repetition_suffix'/2, fun 'optional_suffix'/2]))(I,D) end, fun(Node, Idx) ->
+ case Node of
+ <<"*">> -> zero_or_more;
+ <<"+">> -> one_or_more;
+ <<"?">> -> optional
+ end
+ end).
+
+'optional_suffix'(Input, Index) ->
+ p(Input, Index, 'optional_suffix', fun(I,D) -> (p_string(<<"?">>))(I,D) end, fun(Node, Idx) -> Node end).
+
+'repetition_suffix'(Input, Index) ->
+ p(Input, Index, 'repetition_suffix', fun(I,D) -> (p_choose([p_string(<<"+">>), p_string(<<"*">>)]))(I,D) end, fun(Node, Idx) -> Node end).
+
+'prefix'(Input, Index) ->
+ p(Input, Index, 'prefix', fun(I,D) -> (p_choose([p_string(<<"&">>), p_string(<<"!">>)]))(I,D) end, fun(Node, Idx) ->
+ case Node of
+ <<"&">> -> assert;
+ <<"!">> -> not_
+ end
+ end).
+
+'atomic'(Input, Index) ->
+ p(Input, Index, 'atomic', fun(I,D) -> (p_choose([fun 'terminal'/2, fun 'nonterminal'/2, fun 'parenthesized_expression'/2]))(I,D) end, fun(Node, Idx) ->
+case Node of
+ {nonterminal, Symbol} ->
+ [<<"fun '">>, Symbol, <<"'/2">>];
+ _ -> Node
+end
+ end).
+
+'parenthesized_expression'(Input, Index) ->
+ p(Input, Index, 'parenthesized_expression', fun(I,D) -> (p_seq([p_string(<<"(">>), p_optional(fun 'space'/2), fun 'parsing_expression'/2, p_optional(fun 'space'/2), p_string(<<")">>)]))(I,D) end, fun(Node, Idx) -> lists:nth(3, Node) end).
+
+'nonterminal'(Input, Index) ->
+ p(Input, Index, 'nonterminal', fun(I,D) -> (p_seq([fun 'alpha_char'/2, p_zero_or_more(fun 'alphanumeric_char'/2)]))(I,D) end, fun(Node, Idx) ->
+ Symbol = iolist_to_binary(Node),
+ add_nt(Symbol, Idx),
+ {nonterminal, Symbol}
+ end).
+
+'terminal'(Input, Index) ->
+ p(Input, Index, 'terminal', fun(I,D) -> (p_choose([fun 'quoted_string'/2, fun 'character_class'/2, fun 'anything_symbol'/2]))(I,D) end, fun(Node, Idx) -> Node end).
+
+'quoted_string'(Input, Index) ->
+ p(Input, Index, 'quoted_string', fun(I,D) -> (p_choose([fun 'single_quoted_string'/2, fun 'double_quoted_string'/2]))(I,D) end, fun(Node, Idx) ->
+ lists:flatten(["p_string(<<\"",
+ escape_string(binary_to_list(iolist_to_binary(proplists:get_value(string, Node)))),
+ "\">>)"])
+ end).
+
+'double_quoted_string'(Input, Index) ->
+ p(Input, Index, 'double_quoted_string', fun(I,D) -> (p_seq([p_string(<<"\"">>), p_label('string', p_zero_or_more(p_seq([p_not(p_string(<<"\"">>)), p_choose([p_string(<<"\\\\\\\\">>), p_string(<<"\\\\\"">>), p_anything()])]))), p_string(<<"\"">>)]))(I,D) end, fun(Node, Idx) -> Node end).
+
+'single_quoted_string'(Input, Index) ->
+ p(Input, Index, 'single_quoted_string', fun(I,D) -> (p_seq([p_string(<<"'">>), p_label('string', p_zero_or_more(p_seq([p_not(p_string(<<"'">>)), p_choose([p_string(<<"\\\\\\\\">>), p_string(<<"\\\\'">>), p_anything()])]))), p_string(<<"'">>)]))(I,D) end, fun(Node, Idx) -> Node end).
+
+'character_class'(Input, Index) ->
+ p(Input, Index, 'character_class', fun(I,D) -> (p_seq([p_string(<<"[">>), p_label('characters', p_one_or_more(p_seq([p_not(p_string(<<"]">>)), p_choose([p_seq([p_string(<<"\\\\\\\\">>), p_anything()]), p_seq([p_not(p_string(<<"\\\\\\\\">>)), p_anything()])])]))), p_string(<<"]">>)]))(I,D) end, fun(Node, Idx) ->
+["p_charclass(<<\"[",
+ escape_string(binary_to_list(iolist_to_binary(proplists:get_value(characters, Node)))),
+ "]\">>)"]
+ end).
+
+'anything_symbol'(Input, Index) ->
+ p(Input, Index, 'anything_symbol', fun(I,D) -> (p_string(<<".">>))(I,D) end, fun(Node, Idx) -> <<"p_anything()">> end).
+
+'alpha_char'(Input, Index) ->
+ p(Input, Index, 'alpha_char', fun(I,D) -> (p_charclass(<<"[A-Za-z_]">>))(I,D) end, fun(Node, Idx) -> Node end).
+
+'alphanumeric_char'(Input, Index) ->
+ p(Input, Index, 'alphanumeric_char', fun(I,D) -> (p_choose([fun 'alpha_char'/2, p_charclass(<<"[0-9]">>)]))(I,D) end, fun(Node, Idx) -> Node end).
+
+'space'(Input, Index) ->
+ p(Input, Index, 'space', fun(I,D) -> (p_one_or_more(p_choose([fun 'white'/2, fun 'comment_to_eol'/2])))(I,D) end, fun(Node, Idx) -> Node end).
+
+'comment_to_eol'(Input, Index) ->
+ p(Input, Index, 'comment_to_eol', fun(I,D) -> (p_seq([p_not(p_string(<<"%{">>)), p_string(<<"%">>), p_zero_or_more(p_seq([p_not(p_string(<<"\n">>)), p_anything()]))]))(I,D) end, fun(Node, Idx) -> Node end).
+
+'white'(Input, Index) ->
+ p(Input, Index, 'white', fun(I,D) -> (p_charclass(<<"[ \t\n\r]">>))(I,D) end, fun(Node, Idx) -> Node end).
+
+'code_block'(Input, Index) ->
+ p(Input, Index, 'code_block', fun(I,D) -> (p_choose([p_seq([p_string(<<"%{">>), p_label('code', p_one_or_more(p_choose([p_string(<<"\\\\%">>), p_string(<<"$%">>), p_seq([p_not(p_string(<<"%}">>)), p_anything()])]))), p_string(<<"%}">>)]), p_seq([p_string(<<"`">>), p_label('code', p_one_or_more(p_choose([p_string(<<"\\\\`">>), p_string(<<"$`">>), p_seq([p_not(p_string(<<"`">>)), p_anything()])]))), p_string(<<"`">>)]), p_string(<<"~">>)]))(I,D) end, fun(Node, Idx) ->
+ case Node of
+ <<"~">> -> {code, <<"Node">>};
+ _ -> {code, proplists:get_value('code', Node)}
+ end
+ end).
+
+
+
+
+
+
+
+
+p(Inp, Index, Name, ParseFun) ->
+ p(Inp, Index, Name, ParseFun, fun(N, _Idx) -> N end).
+
+p(Inp, StartIndex, Name, ParseFun, TransformFun) ->
+ % Grab the memo table from ets
+ Memo = get_memo(StartIndex),
+ % See if the current reduction is memoized
+ case proplists:lookup(Name, Memo) of
+ % If it is, return the result
+ {Name, Result} -> Result;
+ % If not, attempt to parse
+ _ ->
+ case ParseFun(Inp, StartIndex) of
+ % If it fails, memoize the failure
+ {fail,_} = Failure ->
+ memoize(StartIndex, [{Name, Failure}|Memo]),
+ Failure;
+ % If it passes, transform and memoize the result.
+ {Result, InpRem, NewIndex} ->
+ Transformed = TransformFun(Result, StartIndex),
+ memoize(StartIndex, [{Name, {Transformed, InpRem, NewIndex}}|Memo]),
+ {Transformed, InpRem, NewIndex}
+ end
+ end.
+
+setup_memo() ->
+ put(parse_memo_table, ets:new(?MODULE, [set])).
+
+release_memo() ->
+ ets:delete(memo_table_name()).
+
+memoize(Position, Struct) ->
+ ets:insert(memo_table_name(), {Position, Struct}).
+
+get_memo(Position) ->
+ case ets:lookup(memo_table_name(), Position) of
+ [] -> [];
+ [{Position, PList}] -> PList
+ end.
+
+memo_table_name() ->
+ get(parse_memo_table).
+
+p_eof() ->
+ fun(<<>>, Index) -> {eof, [], Index};
+ (_, Index) -> {fail, {expected, eof, Index}} end.
+
+p_optional(P) ->
+ fun(Input, Index) ->
+ case P(Input, Index) of
+ {fail,_} -> {[], Input, Index};
+ {_, _, _} = Success -> Success
+ end
+ end.
+
+p_not(P) ->
+ fun(Input, Index)->
+ case P(Input,Index) of
+ {fail,_} ->
+ {[], Input, Index};
+ {Result, _, _} -> {fail, {expected, {no_match, Result},Index}}
+ end
+ end.
+
+p_assert(P) ->
+ fun(Input,Index) ->
+ case P(Input,Index) of
+ {fail,_} = Failure-> Failure;
+ _ -> {[], Input, Index}
+ end