Commit 2c4aa504 authored by Grzegorz Bizon's avatar Grzegorz Bizon

Implement pipeline expressions parser

parent 91a42a1a
......@@ -7,6 +7,10 @@ module Gitlab
raise NotImplementedError
end
def self.build(token)
raise NotImplementedError
end
def self.scan(scanner)
if scanner.scan(self::PATTERN)
Expression::Token.new(scanner.matched, self)
......
......@@ -5,7 +5,8 @@ module Gitlab
class Lexer
LEXEMES = [
Expression::Variable,
Expression::String
Expression::String,
Expression::Equals
]
MAX_CYCLES = 5
......
......@@ -3,6 +3,8 @@ module Gitlab
module Pipeline
module Expression
class Statement
ParserError = Class.new(StandardError)
GRAMMAR = [
%w[variable equals string],
%w[variable equals variable],
......@@ -12,14 +14,41 @@ module Gitlab
%w[variable]
]
def initialize(pipeline, statement)
def initialize(statement, pipeline)
@pipeline = pipeline
@statement = statement
@lexer = Expression::Lexer.new(statement)
end
def variables
end
def tokens
@lexer.tokenize
end
def lexemes
@lexemes ||= tokens.map(&:to_lexeme)
end
##
# Our syntax is very simple, so we don't need yet to implement a
# recurisive parser, we can use the most simple approach to create
# a reverse descent parse tree "by hand".
#
def parse_tree
raise ParserError if lexemes.empty?
unless GRAMMAR.find { |syntax| syntax == lexemes }
raise ParserError, 'Unknown pipeline expression!'
end
if lexemes.many?
Expression::Equals.new(tokens.first.build, tokens.last.build)
else
tokens.first.build
end
end
def evaluate
end
end
......
......@@ -14,6 +14,7 @@ module Gitlab
end
def self.build(string)
new(string.match(PATTERN)[:string])
end
end
end
......
......@@ -10,7 +10,12 @@ module Gitlab
@type = type
end
def build
@type.build(@value)
end
def to_lexeme
type.name.demodulize.downcase
end
end
end
......
......@@ -5,12 +5,16 @@ module Gitlab
class Variable < Expression::Lexeme
PATTERN = /\$(?<name>\w+)/.freeze
def initialize(value)
@value = value
def initialize(name)
@name = name
end
def evaluate(**variables)
end
def self.build(string)
new(string.match(PATTERN)[:name])
end
end
end
end
......
......@@ -36,6 +36,15 @@ describe Gitlab::Ci::Pipeline::Expression::Lexer do
expect(tokens.third.value).to eq '"value"'
end
it 'tokenizes tokens and operators' do
tokens = described_class.new('$VARIABLE == "text"').tokenize
expect(tokens.size).to eq 3
expect(tokens.first.value).to eq '$VARIABLE'
expect(tokens.second.value).to eq '=='
expect(tokens.third.value).to eq '"text"'
end
it 'limits statement to 5 tokens' do
lexer = described_class.new("$V1 $V2 $V3 $V4 $V5 $V6")
......
require 'spec_helper'
describe Gitlab::Ci::Pipeline::Expression::Statement do
let(:pipeline) { build(:ci_pipeline) }
let(:text) { '$VAR "text"' }
subject do
described_class.new(text, pipeline)
end
describe '#tokens' do
it 'returns raw tokens' do
expect(subject.tokens.size).to eq 2
end
end
describe '#lexemes' do
it 'returns an array of syntax lexemes' do
expect(subject.lexemes).to eq %w[variable string]
end
end
describe '#parse_tree' do
context 'when expression grammar is incorrect' do
it 'raises an error' do
expect { subject.parse_tree }
.to raise_error described_class::ParserError
end
end
context 'when expression grammar is correct' do
let(:text) { '$VAR == "value"' }
it 'returns a reverse descent parse tree when using operator' do
expect(subject.parse_tree)
.to be_a Gitlab::Ci::Pipeline::Expression::Equals
end
end
end
end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment