Skip to content

Instantly share code, notes, and snippets.

@whitequark
Created April 20, 2015 18:44
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save whitequark/87b5a9302a28f968f375 to your computer and use it in GitHub Desktop.
Save whitequark/87b5a9302a28f968f375 to your computer and use it in GitHub Desktop.
%%machine lex; # %
class LispLexer
%% write data nofinal;
# %
def initialize(source)
@source = source
@source_pts = source.unpack('U*')
@cs = self.class.lex_en_start
@ts = nil
@te = nil
end
def lex
_lex_trans_keys = self.class.send :_lex_trans_keys
_lex_key_spans = self.class.send :_lex_key_spans
_lex_index_offsets = self.class.send :_lex_index_offsets
_lex_indicies = self.class.send :_lex_indicies
_lex_trans_targs = self.class.send :_lex_trans_targs
_lex_trans_actions = self.class.send :_lex_trans_actions
_lex_to_state_actions = self.class.send :_lex_to_state_actions
_lex_from_state_actions = self.class.send :_lex_from_state_actions
_lex_eof_trans = self.class.send :_lex_eof_trans
p, pe, eof = 0, @source.length, @source.length
@tokens = []
%% write exec;
# %
@tokens << [false, :eof]
@tokens
end
def token(kind, value=nil)
@tokens << [kind, value]
end
%%{
# %
access @;
getkey (@source_pts[p] || 0);
start := |*
space+ => {};
'(' => { token(:lparen) };
')' => { token(:rparen) };
'#t' => { token(:boolean, true) };
'#f' => { token(:boolean, false) };
[1-9][0-9]* => { token(:integer, @source[@ts...@te].to_i) };
'"' ('\\' any | [^"] )* '"' # '
=> { token(:string, @source[@ts+1...@te-1]) };
'"' ('\\' any | [^"] )* # "
=> { raise "unterminated string" };
(any - ([()] | space))+ => { token(:symbol, @source[@ts...@te]) };
*|;
}%%
# %
end
class LispParser
token lparen rparen boolean integer string symbol
rule
program: cells
atom: boolean { result = [:boolean, val[0]] }
| integer { result = [:integer, val[0]] }
| string { result = [:string, val[0]] }
| symbol { result = [:symbol, val[0]] }
list: lparen cells rparen { result = [:list, val[1]] }
cells: cell cells { result = [val[0]] + val[1] }
| /* nothing */ { result = [] }
cell: atom
| list
end
---- inner
def initialize(source)
@tokens = LispLexer.new(source).lex
@pos = 0
end
def next_token
token = @tokens[@pos]
@pos += 1
token
end
def on_error(error_token_id, error_value, value_stack)
raise "unexpected `#{token_to_str(error_token_id)}`"
end
task :generate => ["lisp_lexer.rb", "lisp_parser.rb"]
rule '.rb' => '.rl' do |t|
sh "ragel", "-F1", "-R", t.source, "-o", t.name
end
rule '.rb' => '.y' do |t|
opts = [ t.source, "-o", t.name ]
sh "racc", *opts
end
task :test => :generate do
require_relative 'lisp_lexer'
require_relative 'lisp_parser'
p LispParser.new(%{("x\\"y" 1 20 #t #f (foo bar))}).do_parse
end
task :default => :test
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment