Sha256: 365c1f624ebf2da7735b8415037d2794a3f5f702ed80ec1bd69400ff05373fb3

Contents?: true

Size: 1.32 KB

Versions: 2

Compression:

Stored size: 1.32 KB

Contents

require "lasp/lexer"

module Lasp
  class Parser
    ESCAPE_CHARACTERS = {
      '\n'   => "\n",
      '\t'   => "\t",
      '\\\\' => "\\",
      '\"'   => "\"",
    }

    def self.parse(program)
      new.parse(program)
    end

    def parse(program)
      build_ast(tokenize(program))
    end

    def tokenize(program)
      Lexer.tokenize(program)
    end

    private

    def build_ast(tokens)
      return if tokens.empty?
      token = tokens.shift

      case token
      when "(" then form(tokens)
      when "'" then quote(tokens)
      else atom(token)
      end
    end

    def form(tokens)
      form = []
      while tokens.first != ")"
        form << build_ast(tokens)
      end
      tokens.shift
      form
    end

    def quote(tokens)
      [:quote] << build_ast(tokens)
    end

    def atom(token)
      case token
      when "true"          then true
      when "false"         then false
      when "nil"           then nil
      when /\A-?\d+\z/     then Integer(token)
      when /\A-?\d+.\d+\z/ then Float(token)
      when /"(.*)"/        then String(unescape($1))
      when /:([^\s]+)/     then String($1) # Symbol style strings are actually just strings
      else token.to_sym
      end
    end

    def unescape(string)
      string.gsub(/\\(.)/) { |match| ESCAPE_CHARACTERS.fetch(match, $1) }
    end
  end
end

Version data entries

2 entries across 2 versions & 1 rubygems

Version Path
lasp-0.10.1 lib/lasp/parser.rb
lasp-0.10.0 lib/lasp/parser.rb