Last active
August 29, 2015 14:04
-
-
Save teliosdev/a14597cd2067b0b3f9c4 to your computer and use it in GitHub Desktop.
Antelope Lexer Example
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require "strscan" | |
class Lexer | |
def initialize(input) | |
@input = input | |
@scanner = StringScanner.new(input) | |
@line = 1 | |
end | |
def lex | |
@tokens = [] | |
until @scanner.eos? | |
scan_token or scan_whitespace or error! | |
end | |
@tokens | |
end | |
def scan_token | |
scan_variable or scan_number or scan_operator | |
end | |
def scan_whitespace | |
if @scanner.scan(/\s+/) | |
@line += @scanner[0].count("\n") | |
end | |
end | |
def scan_variable | |
if @scanner.scan(/[A-Za-z_][A-Za-z0-9_-]*/) | |
emit :ident, @scanner[0] | |
end | |
end | |
def scan_number | |
if @scanner.scan(/[1-9][0-9]*(\.[0-9]+)?/) | |
emit :num, @scanner[0] | |
end | |
end | |
def scan_operator | |
case | |
when @scanner.scan(/\+/) | |
emit :plus | |
when @scanner.scan(/\-/) | |
emit :minus | |
when @scanner.scan(/\//) | |
emit :divide | |
when @scanner.scan(/\*/) | |
emit :multiply | |
when @scanner.scan(/\=/) | |
emit :equals | |
else | |
nil | |
end | |
end | |
private | |
def error! | |
raise SyntaxError, "Unexpected `#{@scanner.check(/./)}' on line #{@line} (column #{column})" | |
end | |
def emit(type, value = nil, line = @line, col = column) | |
@tokens << Token.new(type, value, line, col) | |
end | |
def column | |
last_newline = @scanner.string.rindex("\n") || 0 | |
@scanner.pos - last_newline | |
end | |
end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require_relative "lexer" | |
require_relative "token" | |
describe Lexer do | |
subject { Lexer.new(input) } | |
let(:input) { "test = 3 + 2" } | |
it "lexes a basic string" do | |
expect(subject.lex).to eq [ | |
token(:ident, "test", 1, 4), | |
token(:equals, nil, 1, 6), | |
token(:num, "3", 1, 8), | |
token(:plus, nil, 1, 10), | |
token(:num, "2", 1, 12) | |
] | |
end | |
def token(*args) | |
Token.new(*args) | |
end | |
end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Represents a lexical token. Our lexical tokens have a line number, | |
# a column number, a type, and a value. | |
class Token | |
attr_reader :line | |
attr_reader :column | |
attr_reader :type | |
attr_reader :value | |
def initialize(type, value, line, column) | |
@type = type | |
@value = value | |
@line = line | |
@column = column | |
freeze | |
end | |
def to_a | |
[line, column, type, value].freeze | |
end | |
def ==(other) | |
if other.is_a?(Token) | |
return to_a == other.to_a | |
else | |
return to_a == other | |
end | |
end | |
end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require_relative "token" | |
describe Token do | |
subject { Token.new(:ident, "test", 1, 0) } | |
it { should be_frozen } | |
it { should eq Token.new(:ident, "test", 1, 0) } | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment