start conditionals
This commit is contained in:
parent
11a218449d
commit
acf4046225
@ -59,9 +59,62 @@ module Ast
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
class TypedName < NameExpression
|
||||||
|
attr_reader :type
|
||||||
|
def initialize type , name
|
||||||
|
super(name)
|
||||||
|
@type = type.to_sym
|
||||||
|
end
|
||||||
|
def attributes
|
||||||
|
[:type, :name]
|
||||||
|
end
|
||||||
|
def inspect
|
||||||
|
"#{self.class.name}.new(#{type.inspect},#{name.inspect})"
|
||||||
|
end
|
||||||
|
def to_s
|
||||||
|
inspect
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class VariableDefinition < TypedName
|
||||||
|
attr_reader :right
|
||||||
|
|
||||||
|
def initialize type , name , right
|
||||||
|
super(type , name)
|
||||||
|
@right = right
|
||||||
|
end
|
||||||
|
def attributes
|
||||||
|
super + [:right]
|
||||||
|
end
|
||||||
|
def inspect
|
||||||
|
self.class.name + ".new(" + type.inspect + "," + name.inspect + "," + right.inspect + ")"
|
||||||
|
end
|
||||||
|
def to_s
|
||||||
|
inspect
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
class VariableExpression < NameExpression
|
class VariableExpression < NameExpression
|
||||||
end
|
end
|
||||||
|
|
||||||
|
class AssignmentExpression < NameExpression
|
||||||
|
attr_reader :right
|
||||||
|
|
||||||
|
def initialize name, right
|
||||||
|
super(name)
|
||||||
|
@right = right
|
||||||
|
end
|
||||||
|
def attributes
|
||||||
|
super + [:right]
|
||||||
|
end
|
||||||
|
def inspect
|
||||||
|
self.class.name + ".new(" + name.inspect + "," + right.inspect + ")"
|
||||||
|
end
|
||||||
|
def to_s
|
||||||
|
"#{left} = #{right}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
class ModuleName < NameExpression
|
class ModuleName < NameExpression
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
module Ast
|
module Ast
|
||||||
|
|
||||||
class OperatorExpression < Expression
|
class OperatorExpression < Expression
|
||||||
attr_reader :operator, :left, :right
|
attr_reader :operator, :left, :right
|
||||||
|
|
||||||
@ -10,28 +10,11 @@ module Ast
|
|||||||
[:operator, :left, :right]
|
[:operator, :left, :right]
|
||||||
end
|
end
|
||||||
def inspect
|
def inspect
|
||||||
self.class.name + ".new(" + operator.inspect + ", " + left.inspect + "," + right.inspect + ")"
|
self.class.name + ".new(" + operator.inspect + ", " + left.inspect + "," + right.inspect + ")"
|
||||||
end
|
end
|
||||||
def to_s
|
def to_s
|
||||||
"#{left} #{operator} #{right}"
|
"#{left} #{operator} #{right}"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
class AssignmentExpression < Expression
|
|
||||||
attr_reader :left, :right
|
|
||||||
|
|
||||||
def initialize left, right
|
|
||||||
@left, @right = left, right
|
|
||||||
end
|
|
||||||
def attributes
|
|
||||||
[:left, :right]
|
|
||||||
end
|
|
||||||
def inspect
|
|
||||||
self.class.name + ".new(" + left.inspect + "," + right.inspect + ")"
|
|
||||||
end
|
|
||||||
def to_s
|
|
||||||
"#{left} = #{right}"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
end
|
||||||
|
@ -13,11 +13,6 @@ grammar BasicTypes
|
|||||||
rule print /[[:print:]]/ end # Like [:graph:], but includes the space character
|
rule print /[[:print:]]/ end # Like [:graph:], but includes the space character
|
||||||
rule xdigit /[[:xdigit:]]/ end # Digit allowed in a hexadecimal number (i.e., 0-9a-fA-F)
|
rule xdigit /[[:xdigit:]]/ end # Digit allowed in a hexadecimal number (i.e., 0-9a-fA-F)
|
||||||
|
|
||||||
# ruby is newline sensitive, so there is more whitespace footwork
|
|
||||||
# rule of thumb is that anything eats space behind it, but only blank, no newlines
|
|
||||||
# comments are also deliminatord, but also don't include the newline
|
|
||||||
rule deliminator blank* comment? end
|
|
||||||
|
|
||||||
rule linebreak (!blank space) end #define in regex terms for utf
|
rule linebreak (!blank space) end #define in regex terms for utf
|
||||||
|
|
||||||
rule comment #don't include the newline (which ends the comment)
|
rule comment #don't include the newline (which ends the comment)
|
||||||
@ -25,11 +20,11 @@ grammar BasicTypes
|
|||||||
end
|
end
|
||||||
|
|
||||||
rule name_expression
|
rule name_expression
|
||||||
(name:([a-z_] [a-zA-Z0-9_]*) deliminator) { Ast::NameExpression.new(capture(:name).to_str)}
|
(name:([a-z_] [a-zA-Z0-9_]*) space*) { Ast::NameExpression.new(capture(:name).to_str)}
|
||||||
end
|
end
|
||||||
|
|
||||||
rule module_name_expression
|
rule module_name_expression
|
||||||
(name:([A-Z] [a-zA-Z0-9_]*) deliminator) { Ast::ModuleName.new(capture(:name).to_str)}
|
(name:([A-Z] [a-zA-Z0-9_]*) space*) { Ast::ModuleName.new(capture(:name).to_str)}
|
||||||
end
|
end
|
||||||
|
|
||||||
rule digits
|
rule digits
|
||||||
@ -37,7 +32,7 @@ grammar BasicTypes
|
|||||||
end
|
end
|
||||||
|
|
||||||
rule integer_expression
|
rule integer_expression
|
||||||
(digits deliminator) { Ast::IntegerExpression.new(to_str.to_i) }
|
(digits space*) { Ast::IntegerExpression.new(to_str.to_i) }
|
||||||
end
|
end
|
||||||
|
|
||||||
rule string_expression
|
rule string_expression
|
||||||
@ -56,51 +51,51 @@ grammar BasicTypes
|
|||||||
# operator symbols are separate in Opreators
|
# operator symbols are separate in Opreators
|
||||||
|
|
||||||
rule left_parenthesis
|
rule left_parenthesis
|
||||||
'(' space?
|
'(' space*
|
||||||
end
|
end
|
||||||
|
|
||||||
rule right_parenthesis
|
rule right_parenthesis
|
||||||
')' space?
|
')' space*
|
||||||
end
|
end
|
||||||
|
|
||||||
rule left_brace
|
rule left_brace
|
||||||
'{' space?
|
'{' space*
|
||||||
end
|
end
|
||||||
|
|
||||||
rule right_brace
|
rule right_brace
|
||||||
'}' space?
|
'}' space*
|
||||||
end
|
end
|
||||||
|
|
||||||
rule left_bracket
|
rule left_bracket
|
||||||
'[' space?
|
'[' space*
|
||||||
end
|
end
|
||||||
|
|
||||||
rule right_bracket
|
rule right_bracket
|
||||||
']' space?
|
']' space*
|
||||||
end
|
end
|
||||||
|
|
||||||
rule association
|
rule association
|
||||||
"=>" space?
|
"=>" space*
|
||||||
end
|
end
|
||||||
|
|
||||||
rule comma
|
rule comma
|
||||||
',' space?
|
',' space*
|
||||||
end
|
end
|
||||||
|
|
||||||
rule colon
|
rule colon
|
||||||
':' space?
|
':' space*
|
||||||
end
|
end
|
||||||
|
|
||||||
rule semicolon
|
rule semicolon
|
||||||
';' space?
|
';' space*
|
||||||
end
|
end
|
||||||
|
|
||||||
rule question_mark
|
rule question_mark
|
||||||
'?' space?
|
'?' space*
|
||||||
end
|
end
|
||||||
|
|
||||||
rule excamation_mark
|
rule excamation_mark
|
||||||
'!' space?
|
'!' space*
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
@ -28,11 +28,6 @@ grammar Expression
|
|||||||
call_site | basic_expression
|
call_site | basic_expression
|
||||||
end
|
end
|
||||||
|
|
||||||
rule expression
|
|
||||||
# (simple_return | while_do | conditional | operator_expression | call_site ) >> newline
|
|
||||||
(call_site ) newline
|
|
||||||
end
|
|
||||||
|
|
||||||
rule function_definition
|
rule function_definition
|
||||||
keyword_def name:function_name parameter_list newline expressions_end newline
|
keyword_def name:function_name parameter_list newline expressions_end newline
|
||||||
end
|
end
|
||||||
|
37
lib/parser/function.citrus
Normal file
37
lib/parser/function.citrus
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
grammar Function
|
||||||
|
include Statement
|
||||||
|
|
||||||
|
|
||||||
|
rule more_typed_args
|
||||||
|
(comma typed_arg )* {
|
||||||
|
captures(:typed_arg).collect{|u| u.value }
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
rule typed_argument_list
|
||||||
|
(left_parenthesis typed_arg? more_typed_args right_parenthesis){
|
||||||
|
args = [ ]
|
||||||
|
args << capture(:typed_arg).value if capture(:typed_arg)
|
||||||
|
args += capture(:more_typed_args).value if capture(:more_typed_args)
|
||||||
|
args
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
rule call_site
|
||||||
|
(basic_expression "." name_expression argument_list space?) {
|
||||||
|
Ast::CallSiteExpression.new(capture(:name_expression).to_str ,
|
||||||
|
capture(:argument_list).value ,
|
||||||
|
capture(:basic_expression).value )
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
rule function_definition
|
||||||
|
keyword_def name:function_name parameter_list newline expressions_end newline
|
||||||
|
end
|
||||||
|
|
||||||
|
rule parameter_list
|
||||||
|
left_parenthesis parameter_list:( name:parameter? (comma name:parameter)* ) right_parenthesis
|
||||||
|
end
|
||||||
|
|
||||||
|
root call_site
|
||||||
|
end
|
@ -1,37 +1,37 @@
|
|||||||
grammar Keywords
|
grammar Keywords
|
||||||
include BasicTypes
|
include BasicTypes
|
||||||
|
|
||||||
rule keyword_begin 'begin' deliminator end
|
rule keyword_begin 'begin' space* end
|
||||||
rule keyword_class 'class' deliminator end
|
rule keyword_class 'class' space* end
|
||||||
rule keyword_def 'def' deliminator end
|
rule keyword_def 'def' space* end
|
||||||
rule keyword_do 'do' deliminator end
|
rule keyword_do 'do' space* end
|
||||||
rule keyword_else 'else' deliminator end
|
rule keyword_else 'else' space* end
|
||||||
rule keyword_end 'end' deliminator end
|
rule keyword_end 'end' space* end
|
||||||
rule keyword_if 'if' deliminator end
|
rule keyword_if 'if' end
|
||||||
rule keyword_rescue 'rescue' deliminator end
|
rule keyword_rescue 'rescue' space* end
|
||||||
rule keyword_return 'return' deliminator end
|
rule keyword_return 'return' space* end
|
||||||
rule keyword_module 'module' deliminator end
|
rule keyword_module 'module' space* end
|
||||||
rule keyword_unless 'unless' deliminator end
|
rule keyword_unless 'unless' space* end
|
||||||
rule keyword_until 'until' deliminator end
|
rule keyword_until 'until' space* end
|
||||||
rule keyword_while 'while' deliminator end
|
rule keyword_while 'while' space* end
|
||||||
|
|
||||||
rule keyword_nil
|
rule keyword_nil
|
||||||
('nil' deliminator ){ Ast::NilExpression.new }
|
('nil' space* ){ Ast::NilExpression.new }
|
||||||
end
|
end
|
||||||
|
|
||||||
rule keyword_false
|
rule keyword_false
|
||||||
('false' deliminator) { Ast::FalseExpression.new }
|
('false' space*) { Ast::FalseExpression.new }
|
||||||
end
|
end
|
||||||
|
|
||||||
rule keyword_true
|
rule keyword_true
|
||||||
('true' deliminator) { Ast::TrueExpression.new }
|
('true' space*) { Ast::TrueExpression.new }
|
||||||
end
|
end
|
||||||
|
|
||||||
# this rule is just to make sure identifiers can't be keywords. Kind of duplication here, but we need the
|
# this rule is just to make sure identifiers can't be keywords. Kind of duplication here, but we need the
|
||||||
# space in above rules, so just make sure to add any here too.
|
# space in above rules, so just make sure to add any here too.
|
||||||
rule keyword ('begin' | 'def' | 'do' | 'else' | 'end' |
|
rule keyword ('begin' | 'def' | 'do' | 'else' | 'end' |
|
||||||
'false' | 'if' | 'rescue' | 'true' | 'nil' |
|
'false' | 'if' | 'rescue' | 'true' | 'nil' |
|
||||||
'unless' | 'until' | 'while') deliminator
|
'unless' | 'until' | 'while') space*
|
||||||
end
|
end
|
||||||
|
|
||||||
rule keyword_expression
|
rule keyword_expression
|
||||||
|
@ -1,39 +1,13 @@
|
|||||||
#require_relative "basic_types"
|
|
||||||
#require_relative "compound_types"
|
|
||||||
#require_relative "tokens"
|
|
||||||
#require_relative "keywords"
|
|
||||||
#require_relative "control"
|
|
||||||
#require_relative "expression"
|
|
||||||
#require_relative "call_site"
|
|
||||||
#require_relative "function_definition"
|
|
||||||
#require_relative "module_definition"
|
|
||||||
#require_relative "operators"
|
|
||||||
|
|
||||||
require 'ast/expression'
|
require 'ast/expression'
|
||||||
|
|
||||||
module Parser
|
module Parser
|
||||||
|
|
||||||
# obviously a work in progress !!
|
# obviously a work in progress !!
|
||||||
# We "compose" the parser from bits, divide and hopefully conquer
|
|
||||||
|
|
||||||
Citrus.require "parser/basic"
|
Citrus.require "parser/basic"
|
||||||
Citrus.require "parser/keywords"
|
Citrus.require "parser/keywords"
|
||||||
Citrus.require "parser/expression"
|
Citrus.require "parser/expression"
|
||||||
|
Citrus.require "parser/statement"
|
||||||
|
|
||||||
# class Salama < output::Parser
|
|
||||||
# include BasicTypes
|
|
||||||
# include CompoundTypes
|
|
||||||
# include Tokens
|
|
||||||
# include Keywords
|
|
||||||
# include Control
|
|
||||||
# include Expression
|
|
||||||
# include CallSite
|
|
||||||
# include FunctionDefinition
|
|
||||||
# include Operators
|
|
||||||
# include ModuleDef
|
|
||||||
|
|
||||||
# rule(:root_body) {(module_definition | class_definition | function_definition | expression |
|
|
||||||
# operator_expression | call_site | basic_type | hash_constant | array_constant )}
|
|
||||||
# rule(:root) { root_body.repeat.as(:expression_list) }
|
|
||||||
# end
|
|
||||||
end
|
end
|
||||||
|
58
lib/parser/statement.citrus
Normal file
58
lib/parser/statement.citrus
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
grammar Statement
|
||||||
|
include Expression
|
||||||
|
|
||||||
|
rule type
|
||||||
|
(typ:("int" | "ref") space*) { capture(:typ).to_s }
|
||||||
|
end
|
||||||
|
|
||||||
|
rule typed_arg
|
||||||
|
(type name_expression) {
|
||||||
|
Ast::TypedName.new(capture(:type).value , capture(:name_expression).value.name)
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
rule variable_definition
|
||||||
|
(typed_arg ("=" space* value_expression)?) {
|
||||||
|
type = capture(:typed_arg).value
|
||||||
|
value = capture(:value_expression) ? capture(:value_expression).value : nil
|
||||||
|
var = Ast::VariableDefinition.new(type.type , type.name , value)
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
rule assignment
|
||||||
|
(name_expression "=" space* value_expression){
|
||||||
|
Ast::AssignmentExpression.new( capture(:name_expression).value.name , capture(:value_expression).value)
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
rule conditional
|
||||||
|
(keyword_if left_parenthesis value_expression right_parenthesis
|
||||||
|
body
|
||||||
|
keyword_end) {
|
||||||
|
Ast::IfExpression.new( capture(:value_expression).value , capture(:body).value , nil)
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
rule while
|
||||||
|
keyword_while left_parenthesis value_expression right_parenthesis keyword_do
|
||||||
|
body
|
||||||
|
keyword_end
|
||||||
|
end
|
||||||
|
|
||||||
|
rule return
|
||||||
|
keyword_return value_expression newline
|
||||||
|
end
|
||||||
|
|
||||||
|
rule body
|
||||||
|
(statement+){
|
||||||
|
captures(:statement).collect{|u| u.value }
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
rule statement
|
||||||
|
conditional | while | return | variable_definition | assignment
|
||||||
|
end
|
||||||
|
|
||||||
|
root statement
|
||||||
|
|
||||||
|
end
|
@ -1,10 +1,10 @@
|
|||||||
|
|
||||||
#require_relative "test_arguments"
|
#require_relative "test_arguments"
|
||||||
require_relative "test_basic"
|
require_relative "test_basic"
|
||||||
#require_relative "test_call_site"
|
require_relative "test_call_site"
|
||||||
#require_relative "test_class"
|
#require_relative "test_class"
|
||||||
#require_relative "test_compound"
|
#require_relative "test_compound"
|
||||||
#require_relative "test_conditional"
|
require_relative "test_conditional"
|
||||||
#require_relative "test_expressions"
|
#require_relative "test_expressions"
|
||||||
#require_relative "test_function_definition"
|
#require_relative "test_function_definition"
|
||||||
#require_relative "test_module"
|
#require_relative "test_module"
|
||||||
|
@ -1,45 +1,56 @@
|
|||||||
require_relative "../parser_helper"
|
require_relative "../parser_helper"
|
||||||
|
|
||||||
class TestConditional < MiniTest::Test
|
class TestConditional < MiniTest::Test
|
||||||
# include the magic (setup and parse -> test method translation), see there
|
|
||||||
include ParserHelper
|
|
||||||
|
|
||||||
def test_conditional_brackets
|
def setup
|
||||||
check("(0)")
|
@parser = Statement
|
||||||
end
|
|
||||||
def test_conditional_no_brackets
|
|
||||||
check("0")
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def check cond
|
def check
|
||||||
input = <<HERE
|
parse = @parser.parse(@input)
|
||||||
|
assert_equal @input , parse
|
||||||
42
|
assert_equal @output , parse.value
|
||||||
else
|
|
||||||
667
|
|
||||||
end
|
|
||||||
HERE
|
|
||||||
@input = "if #{cond} " + input.chop!
|
|
||||||
@parse_output = {:if=>"if", :conditional=>{:integer=>"0"}, :if_true=>{:expressions=>[{:integer=>"42"}], :else=>"else"}, :if_false=>{:expressions=>[{:integer=>"667"}], :end=>"end"}}
|
|
||||||
@output = Ast::IfExpression.new(Ast::IntegerExpression.new(0), [Ast::IntegerExpression.new(42)],[Ast::IntegerExpression.new(667)] )
|
|
||||||
@root = :conditional
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_conditional_with_calls
|
def test_assignment
|
||||||
|
@input = "myvar = 42"
|
||||||
|
@output = Ast::AssignmentExpression.new(:myvar,Ast::IntegerExpression.new(42))
|
||||||
|
check
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_variable_declaration
|
||||||
|
@input = "int myvar"
|
||||||
|
@output = Ast::VariableDefinition.new(:int,:myvar,nil)
|
||||||
|
check
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_variable_declaration_value
|
||||||
|
@input = "int myvar = 42"
|
||||||
|
@output = Ast::VariableDefinition.new(:int,:myvar,Ast::IntegerExpression.new(42))
|
||||||
|
check
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_if
|
||||||
@input = <<HERE
|
@input = <<HERE
|
||||||
if(3 > var)
|
if( 1 )
|
||||||
Object.initialize(3)
|
int num = 42
|
||||||
else
|
|
||||||
var.new(33)
|
|
||||||
end
|
end
|
||||||
HERE
|
HERE
|
||||||
@input.chop!
|
@output = Ast::IfExpression.new(Ast::IntegerExpression.new(1), [Ast::VariableDefinition.new(:int,:num,Ast::IntegerExpression.new(42))],nil )
|
||||||
@parse_output = {:if=>"if", :conditional=>{:l=>{:integer=>"3"}, :o=>"> ", :r=>{:name=>"var"}}, :if_true=>{:expressions=>[{:receiver=>{:module_name=>"Object"}, :call_site=>{:name=>"initialize"}, :argument_list=>[{:argument=>{:integer=>"3"}}]}], :else=>"else"}, :if_false=>{:expressions=>[{:receiver=>{:name=>"var"}, :call_site=>{:name=>"new"}, :argument_list=>[{:argument=>{:integer=>"33"}}]}], :end=>"end"}}
|
check
|
||||||
@output = Ast::IfExpression.new(Ast::OperatorExpression.new(">", Ast::IntegerExpression.new(3),Ast::NameExpression.new("var")), [Ast::CallSiteExpression.new(:initialize, [Ast::IntegerExpression.new(3)] ,Ast::ModuleName.new("Object"))],[Ast::CallSiteExpression.new(:new, [Ast::IntegerExpression.new(33)] ,Ast::NameExpression.new("var"))] )
|
|
||||||
@root = :conditional
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_conditional_nil
|
def ttest_conditional_with_calls
|
||||||
|
@input = <<HERE
|
||||||
|
if(var)
|
||||||
|
|
||||||
|
end
|
||||||
|
HERE
|
||||||
|
@output = Ast::IfExpression.new(Ast::OperatorExpression.new(">", Ast::IntegerExpression.new(3),Ast::NameExpression.new("var")), [Ast::CallSiteExpression.new(:initialize, [Ast::IntegerExpression.new(3)] ,Ast::ModuleName.new("Object"))],[Ast::CallSiteExpression.new(:new, [Ast::IntegerExpression.new(33)] ,Ast::NameExpression.new("var"))] )
|
||||||
|
check
|
||||||
|
end
|
||||||
|
|
||||||
|
def ttest_conditional_nil
|
||||||
@input = <<HERE
|
@input = <<HERE
|
||||||
if(3 == nil)
|
if(3 == nil)
|
||||||
3
|
3
|
||||||
|
Loading…
x
Reference in New Issue
Block a user