Compare commits

..

2 Commits

Author SHA1 Message Date
Torsten Ruger 6302038914 make 1.9 save 2015-09-15 20:04:32 +03:00
Torsten Ruger 20796dce66 fix gemfile 2015-09-15 19:46:05 +03:00
153 changed files with 2936 additions and 1905 deletions

View File

@ -2,14 +2,14 @@ source "http://rubygems.org"
gem "rake"
gem "soml-parser" , :path => "."
gem "salama-reader" , :path => "."
gem "citrus" , :github => "salama/citrus" , :branch => "gemspec-readme"
gem "ast" #, :path => "../ast"
# use this for debugging, the printout is better (cut/paste)
gem "ast" , :github => "dancinglightning/ast"
# Add dependencies to develop your gem here.
# Include everything needed to run rake, tests, features, etc.
group :development do
gem "codeclimate-test-reporter", require: nil
gem "minitest"
gem "rubygems-tasks"
end

View File

@ -1,28 +1,27 @@
GIT
remote: git://github.com/dancinglightning/ast.git
revision: 3814fb102af82a30bf334005ebe17332744f9dad
remote: git://github.com/salama/citrus.git
revision: 34276150a67a89f84cab091fe0be7bb6a390e768
branch: gemspec-readme
specs:
ast (2.1.0)
citrus (3.0.3)
PATH
remote: .
specs:
soml-parser (0.5.0)
ast (~> 2.1.0)
parslet (~> 1.7.1)
salama-reader (0.2.0)
citrus (~> 3.0.3)
GEM
remote: http://rubygems.org/
specs:
blankslate (3.1.3)
ast (2.1.0)
codeclimate-test-reporter (0.4.6)
simplecov (>= 0.7.1, < 1.0.0)
docile (1.1.5)
minitest (5.6.1)
multi_json (1.11.2)
parslet (1.7.1)
blankslate (>= 2.0, <= 4.0)
rake (10.4.2)
rubygems-tasks (0.2.4)
simplecov (0.9.2)
docile (~> 1.1.0)
multi_json (~> 1.0)
@ -33,11 +32,13 @@ PLATFORMS
ruby
DEPENDENCIES
ast!
ast
citrus!
codeclimate-test-reporter
minitest
rake
soml-parser!
rubygems-tasks
salama-reader!
BUNDLED WITH
1.13.5
1.10.5

119
README.md
View File

@ -1,38 +1,16 @@
[![Build Status](https://travis-ci.org/salama/soml-parser.svg?branch=master)](https://travis-ci.org/salama/soml-parser)
[![Gem Version](https://badge.fury.io/rb/soml-parser.svg)](http://badge.fury.io/rb/soml-parser)
[![Test Coverage](https://codeclimate.com/github/salama/soml-parser/badges/coverage.svg)](https://codeclimate.com/github/salama/soml-parser)
[![Build Status](https://travis-ci.org/salama/salama-reader.svg?branch=master)](https://travis-ci.org/salama/salama-reader)
[![Gem Version](https://badge.fury.io/rb/salama-reader.svg)](http://badge.fury.io/rb/salama-reader)
[![Code Climate](https://codeclimate.com/github/salama/salama-reader/badges/gpa.svg)](https://codeclimate.com/github/salama/salama-reader)
[![Test Coverage](https://codeclimate.com/github/salama/salama-reader/badges/coverage.svg)](https://codeclimate.com/github/salama/salama-reader)
## Soml Parser
## Salama Reader
The parser part of soml is a standalone gem to allow independent development.
It parses Soml using Parslet and no other dependencies.
The parser part of salama is now a standalone gem. It parses ruby using Citrus and no other dependencies.
This is interesting if you want to generate executable code, like salama, but also for other things, like code analysis.
Also it is very educational, as it is very readable code, and not too much of it.
## Soml: Salama Object Machine Language
Soml is Still forming after realizing the need for an oo system language.
The need comes from these three things:
- a language is needed to translate to. Meaning a software layer is needed, but to understand how
that layer works, a syntax is needed. Thus is born a language.
- Upward compatible memory and calling conventions are needed
- Multiple return addresses are needed
In Soml a function call is not necessarily a part of linear code. A call may return to several
addresses, making the call more like an if statement.
### Syntax
Syntax and semantics of Soml are described on the [salama site](http://salama-vm.org/soml/soml.html)
- statically typed so all variable declarations, functions and arguments are typed.
- objects but without data hiding
- static blocks (a bit ala crystal)
- call syntax as already discussed, ie message based
- no semicolns and stuff, but not ruby either
### Parser
The main parser per se is in parser/salama , but it just pulls in all the parts.
@ -44,32 +22,83 @@ Most names are quite self explanatory, but here is a list:
- call_site is a function call. May be qualified, but currently must still have braches
- compound types are hash and array definitions. Hashes still need curlies
- control is if statement which still must have an else
- statement is a helper for all code allowed in a function
- expression is a helper for all code allowed in a function
- function definition must have braces too
- keywords is just a list of them
- operator statement are binary operators (see also below). There's a surprising amount
- operator expression are binary operators (see also below). There's a surprising amount
- return statement are straightforward
- while still needs a do, though i think in ruby a newline is sufficient
**Transform** defines how the rules map to Ast objects.
### Ast
The ast layer now uses the ast gem. That approach is to use a single class to represent all
types of node and use a type symbol (instead of different classes)
The Abtract Syntax Tree (ast) layer puts the parsed code into objects, so they are nice and easy to work with.
This works well, and is much less work.
The Classes don't really define any functionality, that is done in Salama, or can be done in any code using this. Salama just adds a compile function to each class, but a visitor pattern would do just as well.
The following step of compiling use the same kind of visitor approach as before
The functionality that is in there is mainly to do with testing. Equality is defined, but also **inspect** in such a way that it's output (which you get from a failing test) can be pasted straight into the test case as the expected result.
### Parslet
Parslet is really great in that it:
- does not generate code but instead gives a clean dsl to define a grammar
### Citrus
Citrus is really great in that it:
- does not generate code but instead gives a syntax to define a grammar
- uses ruby modules so one can split the grammars up
- has support for binary operators with precedence and binding
- has a separate transform stage to generate an ast layer
Especially the last point is great. Since it is separate it does not clutter up the actual grammar.
And it can generate a layer that has no links to the actual parser anymore, thus saving/automating
a complete transformation process.
### Todo
A random list of things left for the future
- extract commonality of function call/definition,array, hash and multi assignment comma lists
- break and next
- blocks
- more loops, i'm not even sure what ruby supports
- ifs without else, also elsif
- negative tests
### Operators
See the operators.rb for details. Below is a list from the web of how it should be.
Operator list from http://stackoverflow.com/questions/21060234/ruby-operator-precedence-table
N A M Operator(s) Description
- - - ----------- -----------
1 R Y ! ~ + boolean NOT, bitwise complement, unary plus
(unary plus may be redefined from Ruby 1.9 with +@)
2 R Y ** exponentiation
1 R Y - unary minus (redefine with -@)
2 L Y * / % multiplication, division, modulo (remainder)
2 L Y + - addition (or concatenation), subtraction
2 L Y << >> bitwise shift-left (or append), bitwise shift-right
2 L Y & bitwise AND
2 L Y | ^ bitwise OR, bitwise XOR (exclusive OR)
2 L Y < <= >= > ordering
2 N Y == === != =~ !~ <=> equality, pattern matching, comparison
(!= and !~ may not be redefined prior to Ruby 1.9)
2 L N && boolean AND
2 L N || boolean OR
2 N N .. ... range creation (inclusive and exclusive)
and boolean flip-flops
3 R N ? : ternary if-then-else (conditional)
2 L N rescue exception-handling modifier
2 R N = assignment
2 R N **= *= /= %= += -= assignment
2 R N <<= >>= assignment
2 R N &&= &= ||= |= ^= assignment
1 N N defined? test variable definition and type
1 R N not boolean NOT (low precedence)
2 L N and or boolean AND, boolean OR (low precedence)
2 N N if unless while until conditional and loop modifiers

View File

@ -11,7 +11,9 @@ rescue Bundler::BundlerError => e
end
require 'rake'
require "bundler/gem_tasks"
require 'rubygems/tasks'
Gem::Tasks.new
require 'rake/testtask'
Rake::TestTask.new(:test) do |test|
@ -27,7 +29,7 @@ Rake::RDocTask.new do |rdoc|
version = File.exist?('VERSION') ? File.read('VERSION') : ""
rdoc.rdoc_dir = 'rdoc'
rdoc.title = "soml-parser #{version}"
rdoc.title = "salama-reader #{version}"
rdoc.rdoc_files.include('README*')
rdoc.rdoc_files.include('lib/**/*.rb')
end

View File

@ -0,0 +1,135 @@
# collection of the simple ones, int and strings and such
module Ast
class IntegerExpression < Expression
attr_reader :value
def initialize val
@value = val
end
def attributes
[:value]
end
def to_s
value.to_s
end
end
class TrueExpression < Expression
def to_s
""
end
def attributes
[]
end
end
class FalseExpression < Expression
def to_s
""
end
def attributes
[]
end
end
class NilExpression < Expression
def to_s
""
end
def attributes
[]
end
end
class NameExpression < Expression
attr_reader :name
def initialize name
@name = name.to_sym
end
def attributes
[:name]
end
def inspect
"#{self.class.name}.new(#{name.inspect})"
end
def to_s
name.to_s
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
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
end
class StringExpression < Expression
attr_reader :string
def initialize str
@string = str
end
def attributes
[:string]
end
def inspect
self.class.name + '.new("' + string + '")'
end
def to_s
'"' + string.to_s + '"'
end
end
end

View File

@ -0,0 +1,21 @@
module Ast
class BlockExpression < Expression
attr_reader :call_exp, :args , :body_exp
def initialize call_exp, args , body_exp
@call_exp = call_exp.to_sym
@args = args
@body_exp = body_exp
end
def attributes
[:call_exp , :args , :body_exp]
end
def to_s
call_exp.inspect + ", ["+
args.collect{|m| m.inspect }.join( ",") + "] ," + body_exp.inspect
end
end
end

View File

@ -0,0 +1,35 @@
module Ast
class FieldExpression < Expression
attr_reader :receiver , :name
def initialize receiver , name
@receiver = receiver
@name = name.to_sym
end
def attributes
[:receiver , :name]
end
def to_s
receiver.inspect + "," + name.inspect
end
end
class CallSiteExpression < Expression
attr_reader :field , :args
def initialize field, args
@field = field
@args = args
end
def attributes
[:field , :args]
end
def to_s
field.inspect + ", ["+
args.collect{|m| m.inspect }.join( ",") + "] "
end
end
end

View File

@ -0,0 +1,34 @@
module Ast
class ArrayExpression < Expression
attr_reader :values
def attributes
[:values]
end
def initialize vals
@values = vals
end
def to_s
values.to_s
end
end
class AssociationExpression < Expression
attr_reader :key , :value
def initialize key , value
@key , @value = key , value
end
def attributes
[:key , :value]
end
def to_s
key.inspect + " , " + value.inspect
end
end
class HashExpression < ArrayExpression
end
end

82
lib/ast/expression.rb Normal file
View File

@ -0,0 +1,82 @@
# abstract syntax tree (ast)
# This Layer is semi automagically created by parslet using the transform
# It in turn is responsible for the transformation to the next layer, vm code
# This happens in the compile function which must return a Vm::Code derivative
# PS: compare is only for tests and should be factored out to there
Array.class_eval do
def to_basic
collect do |item|
item.to_basic
end
end
end
Symbol.class_eval do
def to_basic
to_s
end
end
String.class_eval do
def to_basic
to_s
end
end
module Ast
class Expression
def attributes
raise "abstract called for #{self}"
end
def to_basic()
data = { "class" => self.class.name }
attributes.each do |name|
val = instance_variable_get("@#{name}".to_sym)
res = val.to_basic
data[name] = res
end
data
end
def self.from_basic(hash)
clazz = hash.delete("class")
keys = hash.keys
klass = clazz.split("::").inject(Object) {|o,c| o.const_get(c)}
keys.delete("class")
values = keys.collect{|k| read_basic(hash[k]) }
klass.new(*values)
end
def self.read_basic val
return from_basic(val) if val.is_a?(Hash)
return val.collect{|i| from_basic(i)} if(val.is_a? Array )
val
end
def inspect
self.class.name + ".new(#{to_s})"
end
def == other
return false unless other.class == self.class
attributes.each do |a|
left = send(a)
right = other.send(a)
return false unless left.class == right.class
return false unless left == right
end
return true
end
end
end
require_relative "basic_expressions"
require_relative "call_site_expression"
require_relative "compound_expressions"
require_relative "if_expression"
require_relative "function_expression"
require_relative "module_expression"
require_relative "operator_expressions"
require_relative "return_expression"
require_relative "while_expression"
require_relative "expression_list"

View File

@ -0,0 +1,17 @@
module Ast
class ExpressionList < Expression
attr_reader :expressions
def initialize expressions
@expressions = expressions
end
def attributes
[:expressions]
end
def to_s
"["+ expressions.collect(&:inspect).join( ",") +"]"
end
end
end

View File

@ -0,0 +1,19 @@
module Ast
class FunctionExpression < Expression
attr_reader :name, :params, :body , :receiver
def initialize name, params, body , receiver = nil
@name = name.to_sym
@params = params
@body = body
@receiver = receiver
end
def attributes
[:name, :params, :body , :receiver]
end
def to_s
name.inspect + ", ["+
params.collect{|m| m.inspect }.join( ",") +"] , [" +
body.collect{|m| m.inspect }.join( ",") +"] ,"+ receiver.inspect
end
end
end

15
lib/ast/if_expression.rb Normal file
View File

@ -0,0 +1,15 @@
module Ast
class IfExpression < Expression
attr_reader :cond, :if_true, :if_false
def initialize cond, if_true, if_false
@cond, @if_true, @if_false = cond, if_true, if_false
end
def attributes
[:cond, :if_true, :if_false]
end
def to_s
cond.inspect + ", " + if_true.inspect + "," + if_false.inspect
end
end
end

View File

@ -0,0 +1,37 @@
module Ast
class ModuleExpression < Expression
attr_reader :name ,:expressions
def initialize name , expressions
@name = name.to_sym
@expressions = expressions
end
def attributes
[:name , :expressions]
end
def to_s
name.inspect + " ," + expressions.inspect
end
end
class ClassExpression < ModuleExpression
def initialize name , derived , expressions
super(name , expressions)
@derived_from = derived
end
def attributes
[:name , :derived_from , :expressions]
end
def derived_from
@derived_from ? @derived_from : :Object
end
def to_s
name.inspect + " ," + derived_from.inspect + ", " + expressions.inspect
end
end
end

View File

@ -0,0 +1,19 @@
module Ast
class OperatorExpression < Expression
attr_reader :operator, :left, :right
def initialize operator, left, right
@operator, @left, @right = operator, left, right
end
def attributes
[:operator, :left, :right]
end
def to_s
operator.inspect + ", " + left.inspect + "," + right.inspect
end
end
end

View File

@ -0,0 +1,15 @@
module Ast
class ReturnExpression < Expression
attr_reader :expression
def initialize expression
@expression = expression
end
def attributes
[:expression]
end
def to_s
expression.inspect
end
end
end

View File

@ -0,0 +1,17 @@
module Ast
class WhileExpression < Expression
attr_reader :condition, :body
def initialize condition, body
@condition , @body = condition , body
end
def attributes
[:condition, :body]
end
def to_s
condition.inspect + ", " + body.inspect
end
end
end

View File

@ -1,64 +0,0 @@
module Parser
# Basic types are numbers and strings
module BasicTypes
include Parslet
# unicode generalized categories , according to regex ruby page
rule(:lower) { match "[[:lower:]]" } # Lowercase alphabetical character
rule(:upper) { match "[[:upper:]]" } # Uppercase alphabetical
rule(:alnum) { match "[[:alnum:]]" } # Alphabetic and numeric character
rule(:alpha) { match "[[:alpha:]]" } # Alphabetic character
rule(:blank) { match "[[:blank:]]" } # Space or tab
rule(:space) { match("[[:space:]]").repeat } # Whitespace character ([:blank:], newline, carriage return, etc.)
rule(:digit) { match "[[:digit:]]" } # Digit
rule(:graph) { match "[[:graph:]]" } # Non-blank character (excludes spaces, control characters, and similar)
rule(:print) { match "[[:print:]]" } # Like [:graph:], but includes the space character
rule(:xdigit) { match "[[:xdigit:]]"} # Digit allowed in a hexadecimal number (i.e., 0-9a-fA-F)
# rule of thumb is that anything eats space behind it, but only space, no newlines
rule(:space?) { space.maybe }
rule(:linebreak){ str("\n") >> space? >> linebreak.repeat }
rule(:quote) { str('"') }
rule(:nonquote) { str('"').absent? >> any }
rule(:comment){ match('#') >> (linebreak.absent? >> any).repeat >> linebreak }
rule(:newline) { (linebreak | comment) >> space? }
rule(:eol) { newline | any.absent? }
rule(:double_quote){ str('"') }
rule(:minus) { str('-') }
rule(:plus) { str('+') }
rule(:sign) { plus | minus }
rule(:dot) { str('.') }
rule(:digit) { match('[0-9]') }
rule(:exponent) { (str('e')| str('E')) }
rule(:type) { (str("int") | class_name).as(:type) >> space }
# identifier must start with lower case
# TODO rule forbit names like true_statements, because it starts with a keyword. a little looser please!
rule(:name) { (keyword|type).absent? >> (match['a-z_'] >> match['a-zA-Z0-9_'].repeat).as(:name) >> space? }
# fields have type
# and class/module names must start with capital
rule(:class_name) { keyword.absent? >> (match['A-Z'] >> match['a-zA-Z0-9_'].repeat).as(:class_name) >> space? }
rule(:escape) { str('\\') >> any.as(:esc) }
rule(:string) { quote >> (
escape |
nonquote.as(:char)
).repeat(1).as(:string) >> quote >> space? }
rule(:integer) { sign.maybe >> digit.repeat(1).as(:integer) >> space? }
rule(:float) { integer >> dot >> integer >>
(exponent >> sign.maybe >> digit.repeat(1,3)).maybe >> space?}
rule(:field_access) { name.as(:receiver) >> str(".") >> name.as(:field) }
rule(:basic_type){ integer | name | string | float | class_name |
((keyword_true | keyword_false | keyword_nil) >> space? ) }
end
end

244
lib/parser/bosl.citrus Normal file
View File

@ -0,0 +1,244 @@
grammar Bosl
# unicode generalized categories , according to regex ruby page
rule lower /[[:lower:]]/ end # Lowercase alphabetical character
rule upper /[[:upper:]]/ end # Uppercase alphabetical
rule alnum /[[:alnum:]]/ end # Alphabetic and numeric character
rule alpha /[[:alpha:]]/ end # Alphabetic character
rule blank /[[:blank:]]/ end # Space or tab
rule space /[[:space:]]/ end # Whitespace character ([:blank:], newline, carriage return, etc.)
rule digit /[[:digit:]]/ end # Digit
rule graph /[[:graph:]]/ end # Non-blank character (excludes spaces, control characters, and similar)
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 linebreak (!blank space) end #define in regex terms for utf
rule comment #don't include the newline (which ends the comment)
"#" /.*/
end
rule name_expression
(name:([a-z_] [a-zA-Z0-9_]*) space*) { capture(:name).to_str}
end
rule aspect_name_expression
(name:([A-Z] [a-zA-Z0-9_]*) space*) { s(:aspect , capture(:name).to_str)}
end
rule digits
[0-9] [0-9]*
end
rule integer_expression
(digits space*) { s(:int , to_str.to_i) }
end
rule string_expression
#"'" (/.*/ !"'") "'"
('"' str:(!'"' .)* '"') {s(:string , capture(:str).to_str) }
end
rule basic_expression
name_expression | integer_expression |
aspect_name_expression | string_expression
end
rule keyword_begin 'begin' space* end
rule keyword_class 'class' space* end
rule keyword_def 'def' space* end
rule keyword_do 'do' space* end
rule keyword_else 'else' space* end
rule keyword_end 'end' space* end
rule keyword_if 'if' end
rule keyword_rescue 'rescue' space* end
rule keyword_return 'return' space* end
rule keyword_aspect 'aspect' space* end
rule keyword_unless 'unless' space* end
rule keyword_while 'while' space* end
rule keyword_nil
('nil' space* ){ s(:nil) }
end
rule keyword_false
('false' space*) { s(:false) }
end
rule keyword_true
('true' space*) { s(:true) }
end
# 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.
rule keyword ('begin' | 'def' | 'do' | 'else' | 'end' |
'false' | 'if' | 'rescue' | 'true' | 'nil' |
'unless' | 'until' | 'while') space*
end
rule keyword_expression
keyword_true | keyword_false | keyword_nil | basic_expression
end
# Tokens are single or double character combinations with "meaning"
# braces, comman, point, questionmark , quotes, that kind of thing
# operator symbols are separate in Opreators
rule left_parenthesis
'(' space*
end
rule right_parenthesis
')' space*
end
rule left_brace
'{' space*
end
rule right_brace
'}' space*
end
rule left_bracket
'[' space*
end
rule right_bracket
']' space*
end
rule association
"=>" space*
end
rule comma
',' space*
end
rule colon
':' space*
end
rule semicolon
';' space*
end
rule question_mark
'?' space*
end
rule excamation_mark
'!' space*
end
rule more_args
(comma basic_expression )* {
captures(:basic_expression).collect{|u| u.value }
}
end
rule argument_list
(left_parenthesis basic_expression? more_args right_parenthesis){
args = [ ]
args << capture(:basic_expression).value if capture(:basic_expression)
args += capture(:more_args).value if capture(:more_args)
args
}
end
rule call_site
(field_expression argument_list space?) {
s(:call , capture(:field_expression).value , capture(:argument_list).value )
}
end
rule field_expression
(basic_expression "." name_expression space?) {
s(:field , capture(:basic_expression).value ,
capture(:name_expression).value )
}
end
rule value_expression
call_site | basic_expression
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
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 type
(typ:("int" | "ref") space*) { capture(:typ).to_s }
end
rule typed_arg
(type name_expression) {
s(:type , capture(:type).value , capture(:name_expression).value)
}
end
rule variable_definition
(typed_arg ("=" space* value_expression)?) {
value = capture(:value_expression) ? capture(:value_expression).value : nil
s(:variable , capture(:typed_arg).value , value )
}
end
rule assignment
(name_expression "=" space* value_expression){
s(:assign , capture(:name_expression).value.to_sym , capture(:value_expression).value)
}
end
rule conditional
(keyword_if left_parenthesis value_expression right_parenthesis
body
keyword_end) {
s(:if , s(:cond , capture(:value_expression).value ) , s(:then , capture(:body).value) )
}
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 | call_site
end
end

View File

@ -1,23 +0,0 @@
module Parser
module CallSite
include Parslet
rule(:r_value) { operator_value | call_site | field_access | basic_type }
rule(:assign) { str('=') >> space?}
rule(:assignment){ (field_access|name).as(:l_value) >> assign.as(:assign) >> r_value.as(:r_value) }
rule(:field_def) { type >> name >> (assign >> r_value.as(:value) ).maybe}
rule(:argument_list) {
left_parenthesis >>
( (basic_type.as(:argument) >>
(comma >> r_value.as(:argument)).repeat(0)).repeat(0,1)).as(:argument_list) >>
right_parenthesis
}
rule(:call_site) { (basic_type.as(:receiver) >> str(".")).maybe >> #possibly qualified
name.as(:call_site) >> argument_list >> comment.maybe}
end
end

View File

@ -1,16 +0,0 @@
module Parser
module ModuleDef
include Parslet
rule(:class_field) { keyword_field >> field_def }
rule(:class_body) {
function_definition | class_field
}
rule(:class_definition) do
keyword_class >> class_name >> (str("<") >> space >> class_name).maybe.as(:derived_name) >>
( (keyword_end.absent? >> class_body).repeat()).as(:class_statements) >> keyword_end
end
end
end

View File

@ -1,19 +1,19 @@
module Parser
# Compound types are Arrays and Hashes
module CompoundTypes
include Parslet
include output
rule(:array_constant) do
left_bracket >>
( (r_value.as(:array_element) >> space? >>
(comma >> space? >> r_value.as(:array_element)).repeat(0)).repeat(0,1)).as(:array_constant) >>
( ((operator_expression|value_expression).as(:array_element) >> space? >>
(comma >> space? >> (operator_expression|value_expression).as(:array_element)).repeat(0)).repeat(0,1)).as(:array_constant) >>
space? >> right_bracket
end
rule(:hash_pair) { basic_type.as(:hash_key) >> association >> r_value.as(:hash_value) }
rule(:hash_constant) { left_brace >> ((hash_pair.as(:hash_pair) >>
(comma >> space? >> hash_pair.as(:hash_pair)).repeat(0)).repeat(0,1)).as(:hash_constant)>>
rule(:hash_pair) { basic_type.as(:hash_key) >> association >> (operator_expression|value_expression).as(:hash_value) }
rule(:hash_constant) { left_brace >> ((hash_pair.as(:hash_pair) >>
(comma >> space? >> hash_pair.as(:hash_pair)).repeat(0)).repeat(0,1)).as(:hash_constant)>>
space? >> right_brace }
end
end
end

20
lib/parser/control.rb Normal file
View File

@ -0,0 +1,20 @@
module Parser
module Control
include output
rule(:conditional) do
keyword_if >>
(( (value_expression|operator_expression).as(:conditional) ) |
left_parenthesis >> (operator_expression|value_expression).as(:conditional) >> right_parenthesis) >>
newline >> expressions_else.as(:if_true) >> newline >> expressions_end.as(:if_false)
end
rule(:while_do) do
keyword_while >> left_parenthesis >> (operator_expression|value_expression).as(:while_cond) >>
right_parenthesis >> keyword_do >> newline >>
expressions_end.as(:body)
end
rule(:simple_return) do
keyword_return >> (operator_expression|value_expression).as(:return_expression)
end
end
end

View File

@ -1,16 +0,0 @@
module Parser
module FunctionDefinition
include Parslet
rule(:function_definition) {
type >> ((class_name|name).as(:receiver) >> str(".")).maybe >> #possibly qualified
name.as(:function_name) >> left_parenthesis >>
parameter_list.maybe >> right_parenthesis >> statements_end >> space?
}
rule(:parameter_list) {
((field_def.as(:parameter) >> (comma >> field_def.as(:parameter)).repeat(0)).repeat(0,1)).as(:parameter_list)
}
end
end

View File

@ -1,21 +0,0 @@
module Parser
module Keywords
include Parslet
rule(:keyword_begin) { str('begin').as(:begin) >> space?}
rule(:keyword_class) { str('class') >> space? }
rule(:keyword_else) { str('else').as(:else) >> space? }
rule(:keyword_end) { str('end').as(:end) >> space? }
rule(:keyword_false) { str('false').as(:false) }
rule(:keyword_field) { str('field').as(:field) >> space? }
rule(:keyword_return) { str('return').as(:return) >> space?}
rule(:keyword_true) { str('true').as(:true) }
rule(:keyword_nil) { str('nil').as(:nil) }
# 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.
rule(:keyword){ str('if_') | str('else') | str('end') | str('while_') |
str('false') | str('true')| str('nil') | str("class") |
str('return')| str('int')| str('field')}
end
end

View File

@ -0,0 +1,15 @@
module Parser
module ModuleDef
include output
rule(:module_definition) do
keyword_module >> module_name >> eol >>
( (keyword_end.absent? >> root_body).repeat()).as(:module_expressions) >> keyword_end >> newline
end
rule(:class_definition) do
keyword_class >> module_name >> (smaller >> module_name).maybe.as(:derived_name) >> eol >>
( (keyword_end.absent? >> root_body).repeat()).as(:class_expressions) >> keyword_end >> newline
end
end
end

View File

@ -1,15 +1,52 @@
module Parser
module Operators
include Parslet
rule(:operator_sym) {
str('**') | str('*') | str('/') | str('/') | str('%') |
str('+') | str('-') | str('<<')| str('>>') |
str('|') | str('&') |
str('>=') | str('<=') | str('>') | str('<') |
str('==') | str('!=') }
rule(:operator_value) { (field_access|basic_type).as(:left) >>
operator_sym.as(:operator) >> space? >> (field_access|basic_type).as(:right) }
include output
rule(:exponent) { str('**') >> space?}
rule(:multiply) { match['*/%'] >> space? }
rule(:plus) { match['+-'] >> space? }
rule(:shift) { str(">>") | str("<<") >> space?}
rule(:bit_and) { str('&') >> space?}
rule(:bit_or) { str('|') >> space?}
rule(:greater_equal) { str('>=') >> space?}
rule(:less_or_equal) { str('<=') >> space?}
rule(:larger) { str('>') >> space?}
rule(:smaller) { str('<') >> space?}
rule(:identity) { str('===') >> space?}
rule(:equal) { str('==') >> space?}
rule(:not_equal) { str('!=') >> space?}
rule(:boolean_and) { str('&&') | str("and") >> space?}
rule(:boolean_or) { str('||') | str("or") >> space?}
rule(:assign) { str('=') >> space?}
rule(:op_assign) { str('+=')|str('-=')|str('*=')|str('/=')|str('%=') >> space?}
rule(:eclipse) { str('..') |str("...") >> space?}
rule(:assign) { str('=') >> space?}
#infix doing the heavy lifting here,
# is defined as an expressions and array of [atoms,priority,binding] triples
rule(:operator_expression) do infix_expression(value_expression,
[exponent, 120, :left] ,
[multiply, 120, :left] ,
[plus, 110, :left],
[shift, 100, :left],
[bit_and, 90, :left],
[bit_or, 90, :right],
[greater_equal, 80, :left],
[less_or_equal, 80, :left],
[larger, 80, :left],
[smaller, 80, :left],
[identity, 70, :right],
[equal, 70, :right],
[not_equal, 70, :right],
[boolean_and, 60, :left],
[boolean_or, 50, :right],
[eclipse, 40, :right],
[keyword_rescue, 30, :right],
[assign, 20, :right],
[op_assign, 20, :right],
[keyword_until, 10, :right],
[keyword_while, 10, :right],
[keyword_unless, 10, :right],
[keyword_if, 10, :right])
end
end
end

11
lib/parser/salama.rb Normal file
View File

@ -0,0 +1,11 @@
require "ast"
module Parser
# obviously a work in progress !!
Citrus::Match.send :include , AST::Sexp
Citrus.require "parser/bosl"
end

View File

@ -1,35 +0,0 @@
require_relative "basic_types"
require_relative "compound_types"
require_relative "tokens"
require_relative "keywords"
require_relative "statement"
require_relative "call_site"
require_relative "function_definition"
require_relative "class_definition"
require_relative "operators"
module Parser
# obviously a work in progress !!
# We "compose" the parser from bits, divide and hopefully conquer
# a note about .maybe : .maybe is almost every respect the same as .repeat(0,1)
# so either 0, or 1, in other words maybe. Nice feature, but there are strings attached:
# a maybe removes the 0 a sequence (array) to a single (hash). Thus 2 transformations are needed
# More work than the prettiness is worth, so only use .maybe on something that does not need capturing
class Salama < Parslet::Parser
include BasicTypes
include CompoundTypes
include Tokens
include Keywords
include Statement
include CallSite
include FunctionDefinition
include Operators
include ModuleDef
rule(:root_body) {( class_definition | function_definition )}
rule(:root) { root_body.repeat.as(:statement_list) }
end
end

View File

@ -1,46 +0,0 @@
module Parser
module Statement
include Parslet
rule( :keyword_while) do
str("while_") >> alpha.repeat.as(:condition) >> space
end
rule(:while_statement) do
keyword_while >> left_parenthesis >> r_value.as(:conditional) >>
right_parenthesis >> statements_end.as(:body)
end
rule( :keyword_if) do
str("if_") >> alpha.repeat.as(:condition) >> space
end
rule(:if_statement) do
keyword_if >>
left_parenthesis >> r_value.as(:conditional) >> right_parenthesis >>
statements_else.as(:true_statements) >> statements_end.as(:false_statements)
end
rule(:small_conditional) do
keyword_if >>
left_parenthesis >> r_value.as(:conditional) >> right_parenthesis >>
statements_end.as(:true_statements)
end
rule(:return_statement) do
keyword_return >> r_value.as(:return_statement)
end
rule(:statement) { (return_statement | while_statement | small_conditional | if_statement |
assignment | class_field | field_def | call_site |
hash_constant | array_constant) }
def delimited_statements( delimit )
( (delimit.absent? >> statement).repeat(1)).as(:statements) >> delimit
end
rule(:statements_do) { delimited_statements(keyword_do) }
rule(:statements_else) { delimited_statements(keyword_else) }
rule(:statements_end) { delimited_statements(keyword_end) }
end
end

View File

@ -1,126 +1,99 @@
require 'parslet'
require 'ast/expression'
#include is private in 1.9, who'd have known without travis
Parslet::Context.send :include , AST::Sexp
Parslet::Context.class_eval do
def type_sym t
if( t.is_a? AST::Node )
t = t.children.first
else
t = t.to_sym
t = :Integer if t == :int
end
t
end
end
module Parser
class Transform < Parslet::Transform
rule(:string => sequence(:chars)) { s(:string , chars.join) }
class Transform < output::Transform
rule(:string => sequence(:chars)) { Ast::StringExpression.new chars.join }
rule(:esc => simple(:esc)) { '\\' + esc }
rule(char: simple(:char)) { char }
rule(:true => simple(:true)) { s(:true) }
rule(:false => simple(:false)) { s(:false) }
rule(:nil => simple(:nil)) { s(:nil) }
rule(:integer => simple(:value)) { s(:int ,value.to_i) }
rule(:name => simple(:name)) { s(:name , name.to_sym) }
# local variables
rule(:type => simple(:type), :name => simple(:name)) {
s(:field_def , type_sym(type) , s(:name , name.to_sym)) }
rule(:type => simple(:type), :name => simple(:name) , :value => simple(:value)) {
s(:field_def , type_sym(type) , s(:name , name.to_sym) , value ) }
# class field
rule(:field => simple(:field) , :type => simple(:type), :name => simple(:name)) {
s(:class_field , type_sym(type) , name.to_sym) }
rule(:field => simple(:field) , :type => simple(:type), :name => simple(:name) , :value => simple(:value)) {
s(:class_field , type_sym(type) , name.to_sym , value ) }
rule(:true => simple(:true)) { Ast::TrueExpression.new() }
rule(:false => simple(:false)) { Ast::FalseExpression.new() }
rule(:nil => simple(:nil)) { Ast::NilExpression.new() }
rule(:integer => simple(:value)) { Ast::IntegerExpression.new(value.to_i) }
rule(:name => simple(:name)) { Ast::NameExpression.new(name.to_s) }
rule(:instance_variable => simple(:instance_variable)) { Ast::VariableExpression.new(instance_variable.name) }
rule(:module_name => simple(:module_name)) { Ast::ModuleName.new(module_name.to_s) }
rule(:l_value => simple(:l_value) , :assign => simple(:assign) , :r_value => simple(:r_value)) {
s(:assignment , l_value , r_value)
}
rule( :left => simple(:left) , :operator => simple(:operator) ,
:right => simple(:right)) { s(:operator_value , operator.to_sym , left , right )}
rule(:class_name => simple(:class_name)) { s(:class_name,class_name.to_s.to_sym) }
rule(:array_constant => sequence(:array_constant) ) { s(:array , *array_constant) }
rule(:array_constant => sequence(:array_constant) ) { Ast::ArrayExpression.new(array_constant) }
rule(:array_element => simple(:array_element)) { array_element }
rule(:hash_constant => sequence(:hash_constant) ) { s(:hash , *hash_constant) }
rule(:hash_key => simple(:hash_key) , :hash_value => simple(:hash_value)) { s(:assoc , hash_key , hash_value) }
rule(:hash_constant => sequence(:hash_constant) ) { Ast::HashExpression.new(hash_constant) }
rule(:hash_key => simple(:hash_key) , :hash_value => simple(:hash_value)) { Ast::AssociationExpression.new(hash_key,hash_value) }
rule(:hash_pair => simple(:hash_pair) ) { hash_pair }
rule(:argument => simple(:argument)) { argument }
rule(:argument_list => sequence(:argument_list)) { argument_list }
#Two rules for calls, simple and qualified. Keeps the rules simpler
rule( :call_site => simple(:call_site),
rule( :call_site => simple(:call_site),
:argument_list => sequence(:argument_list)) do
s(:call , call_site, s(:arguments , *argument_list) )
Ast::CallSiteExpression.new(call_site.name, argument_list )
end
rule( :receiver => simple(:receiver) , :call_site => simple(:call_site),
rule( :receiver => simple(:receiver) , :call_site => simple(:call_site),
:argument_list => sequence(:argument_list)) do
s(:call , call_site, s(:arguments , *argument_list) , s(:receiver , receiver))
end
rule( :receiver => simple(:receiver) , :field => simple(:field) ) do
s(:field_access , s(:receiver , receiver) , s(:field , field) )
Ast::CallSiteExpression.new(call_site.name, argument_list , receiver)
end
rule(:condition => simple(:condition) ,
:conditional => simple(:conditional),
:body => {:statements => sequence(:body) , :end => simple(:e) }) do
s(:while_statement,condition.to_s.to_sym, s(:conditional , conditional), s(:statements , *body))
end
rule(:if => simple(:if), :conditional => simple(:conditional),
:if_true => {:expressions => sequence(:if_true) , :else => simple(:else) },
:if_false => {:expressions => sequence(:if_false) , :end => simple(:e) }) do
Ast::IfExpression.new(conditional, if_true, if_false)
end
rule(:condition => simple(:condition), :conditional => simple(:conditional),
:true_statements => {:statements => sequence(:true_statements) , :else => simple(:else) },
:false_statements => {:statements => sequence(:false_statements) , :end => simple(:e) }) do
s(:if_statement, condition.to_s.to_sym,
s(:condition, conditional),
s(:true_statements, *true_statements),
s(:false_statements , *false_statements))
end
rule(:while => simple(:while),
:while_cond => simple(:while_cond) , :do => simple(:do) ,
:body => {:expressions => sequence(:body) , :end => simple(:e) }) do
Ast::WhileExpression.new(while_cond, body)
end
rule(:condition => simple(:condition), :conditional => simple(:conditional),
:true_statements => {:statements => sequence(:true_statements) , :end => simple(:e) }) do
s(:if_statement, condition.to_s.to_sym, s(:condition, conditional),
s(:true_statements, *true_statements), s(:false_statements , nil) )
end
rule(:return => simple(:return) , :return_statement => simple(:return_statement))do
s(:return , return_statement)
rule(:return => simple(:return) , :return_expression => simple(:return_expression))do
Ast::ReturnExpression.new(return_expression)
end
rule(:parameter => simple(:parameter)) { s(:parameter , parameter.children[0] , parameter.children[1].children[0]) }
rule(:parameter => simple(:parameter)) { parameter }
rule(:parameter_list => sequence(:parameter_list)) { parameter_list }
# Also two rules for function definitions, unqualified and qualified
rule(:type => simple(:type) ,
:function_name => simple(:function_name),
rule(:function_name => simple(:function_name),
:parameter_list => sequence(:parameter_list),
:statements => sequence(:statements) , :end => simple(:e)) do
s(:function, type_sym(type) , function_name, s(:parameters , *parameter_list ),
s(:statements , *statements))
:expressions => sequence(:expressions) , :end => simple(:e)) do
Ast::FunctionExpression.new(function_name.name, parameter_list, expressions)
end
rule(:type => simple(:type) ,
:receiver=> simple(:receiver),
:function_name => simple(:function_name),
:parameter_list => sequence(:parameter_list),
:statements => sequence(:statements) , :end => simple(:e)) do
s(:function, type_sym(type) , function_name, s(:parameters , *parameter_list ),
s(:statements , *statements) , s(:receiver , *receiver))
rule(:function_name => simple(:function_name),
:expressions => sequence(:expressions) , :end => simple(:e)) do
Ast::FunctionExpression.new(function_name.name, [], expressions)
end
rule( :class_name => simple(:class_name) , :derived_name => simple(:derived_name) , :class_statements => sequence(:class_statements) , :end=>"end") do
s(:class , class_name.to_s.to_sym ,
s(:derives, derived_name ? derived_name.to_a.first.to_sym : nil) ,
s(:statements, *class_statements) )
rule(:receiver=> simple(:receiver),
:function_name => simple(:function_name),
:parameter_list => sequence(:parameter_list),
:expressions => sequence(:expressions) , :end => simple(:e)) do
Ast::FunctionExpression.new(function_name.name, parameter_list, expressions , receiver)
end
rule(l: simple(:l), o: simple(:o) , r: simple(:r)) do
op = o.to_s.strip
if op == "="
Ast::AssignmentExpression.new( l ,r)
else
Ast::OperatorExpression.new( op , l ,r)
end
end
rule(:statement_list => sequence(:statement_list)) {
s(:statements , *statement_list)
#modules and classes are understandibly quite similar Class < Module
rule( :module_name => simple(:module_name) , :module_expressions => sequence(:module_expressions) , :end=>"end") do
Ast::ModuleExpression.new(module_name , module_expressions)
end
rule( :module_name => simple(:module_name) , :derived_name => simple(:derived_name) , :class_expressions => sequence(:class_expressions) , :end=>"end") do
Ast::ClassExpression.new(module_name , derived_name ? derived_name.name : nil , class_expressions)
end
rule(:expression_list => sequence(:expression_list)) {
Ast::ExpressionList.new(expression_list)
}
#shortcut to get the ast tree for a given string
# optional second arguement specifies a rule that will be parsed (mainly for testing)
# optional second arguement specifies a rule that will be parsed (mainly for testing)
def self.ast string , rule = :root
syntax = Parser.new.send(rule).parse(string)
tree = Transform.new.apply(syntax)

3
lib/salama-reader.rb Normal file
View File

@ -0,0 +1,3 @@
require 'citrus'
require 'parser/salama'
#require 'parser/transform'

49
lib/skip/basic_types.rb Normal file
View File

@ -0,0 +1,49 @@
module Parser
# Basic types are numbers and strings
module BasicTypes
include output
# space really is just space. ruby is newline sensitive, so there is more whitespace footwork
# rule of thumb is that anything eats space behind it, but only space, no newlines
rule(:space) { (str('\t') | str(' ')).repeat(1) }
rule(:space?) { space.maybe }
rule(:linebreak){ str("\n") >> space? >> linebreak.repeat }
rule(:quote) { str('"') }
rule(:nonquote) { str('"').absent? >> any }
rule(:comment){ match('#') >> (linebreak.absent? >> any).repeat >> linebreak }
rule(:newline) { linebreak | comment }
rule(:eol) { newline | any.absent? }
rule(:double_quote){ str('"') }
rule(:minus) { str('-') }
rule(:plus) { str('+') }
rule(:sign) { plus | minus }
rule(:dot) { str('.') }
rule(:digit) { match('[0-9]') }
rule(:exponent) { (str('e')| str('E')) }
# identifier must start with lower case
# TODO rule forbit names like if_true, because it starts with a keyword. a little looser please!
rule(:name) { keyword.absent? >> (match['a-z_'] >> match['a-zA-Z0-9_'].repeat).as(:name) >> space? }
# instance variables must have the @
rule(:instance_variable) { (str('@') >> name).as(:instance_variable) }
# and class/module names must start with capital
# (admittatly the rule matches constants too, but one step at a time)
rule(:module_name) { keyword.absent? >> (match['A-Z'] >> match['a-zA-Z0-9_'].repeat).as(:module_name) >> space? }
rule(:escape) { str('\\') >> any.as(:esc) }
rule(:string) { quote >> (
escape |
nonquote.as(:char)
).repeat(1).as(:string) >> quote }
rule(:integer) { sign.maybe >> digit.repeat(1).as(:integer) >> space? }
rule(:float) { integer >> dot >> integer >>
(exponent >> sign.maybe >> digit.repeat(1,3)).maybe >> space?}
rule(:basic_type){ integer | name | string | float | instance_variable | module_name |
keyword_true | keyword_false | keyword_nil }
end
end

17
lib/skip/call_site.rb Normal file
View File

@ -0,0 +1,17 @@
module Parser
module CallSite
include output
rule(:argument_list) {
left_parenthesis >>
( ((operator_expression|value_expression).as(:argument) >> space? >>
(comma >> space? >> (operator_expression|value_expression).as(:argument)).repeat(0)).repeat(0,1)).as(:argument_list) >>
space? >> right_parenthesis
}
rule(:call_site) { ((module_name|instance_variable|basic_type).as(:receiver) >> str(".")).maybe >> #possibly qualified
name.as(:call_site) >> argument_list >> comment.maybe}
end
end

18
lib/skip/expression.rb Normal file
View File

@ -0,0 +1,18 @@
module Parser
module Expression
include output
rule(:value_expression) { call_site | basic_type }
rule(:expression) { (simple_return | while_do | conditional | operator_expression | call_site ) >> newline }
def delimited_expressions( delimit )
( (delimit.absent? >> expression).repeat(1)).as(:expressions) >> delimit
end
rule(:expressions_do) { delimited_expressions(keyword_do) }
rule(:expressions_else) { delimited_expressions(keyword_else) }
rule(:expressions_end) { delimited_expressions(keyword_end) }
end
end

View File

@ -0,0 +1,17 @@
module Parser
module FunctionDefinition
include output
rule(:function_definition) {
keyword_def >> ((module_name|instance_variable|name).as(:receiver) >> str(".")).maybe >> #possibly qualified
name.as(:function_name) >> parameter_list.maybe >> newline >> expressions_end >> newline
}
rule(:parameter_list) {
left_parenthesis >>
((name.as(:parameter) >> (comma >> name.as(:parameter)).repeat(0)).repeat(0,1)).as(:parameter_list) >>
right_parenthesis
}
end
end

28
lib/skip/keywords.rb Normal file
View File

@ -0,0 +1,28 @@
module Parser
module Keywords
include output
rule(:keyword_begin) { str('begin').as(:begin) >> space?}
rule(:keyword_class) { str('class') >> space? }
rule(:keyword_def) { str('def') >> space? }
rule(:keyword_do) { str('do').as(:do) >> space?}
rule(:keyword_else) { str('else').as(:else) >> space? }
rule(:keyword_end) { str('end').as(:end) >> space? }
rule(:keyword_false) { str('false').as(:false) >> space?}
rule(:keyword_if) { str('if').as(:if) >> space? }
rule(:keyword_rescue) { str('rescue').as(:rescue) >> space?}
rule(:keyword_return) { str('return').as(:return) >> space?}
rule(:keyword_true) { str('true').as(:true) >> space?}
rule(:keyword_module) { str('module') >> space? }
rule(:keyword_nil) { str('nil').as(:nil) >> space?}
rule(:keyword_unless) { str('unless').as(:unless) >> space?}
rule(:keyword_until) { str('until').as(:until) >> space?}
rule(:keyword_while) { str('while').as(:while) >> space?}
# 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.
rule(:keyword){ str('begin') | str('def') | str('do') | str('else') | str('end') |
str('false')| str('if')| str('rescue')| str('true')| str('nil') |
str('unless')| str('until')| str('while')}
end
end

View File

@ -3,7 +3,7 @@ module Parser
# braces, comman, point, questionmark , quotes, that kind of thing
# operator symbols are separate in Opreators
module Tokens
include Parslet
include output
rule(:left_parenthesis) { str('(') >> space? }
rule(:right_parenthesis) { str(')') >> space? }
rule(:left_brace) { str('{') >> space? }

View File

@ -1,5 +0,0 @@
require 'parslet'
require 'parser/soml'
require "ast"
require 'parser/transform'
require "soml/code/code"

View File

@ -1,13 +0,0 @@
module Soml
class Assignment < Statement
attr_accessor :name , :value
def initialize(n = nil , v = nil )
@name , @value = n , v
end
end
class FieldDef < Statement
attr_accessor :name , :type , :value
end
end

View File

@ -1,39 +0,0 @@
module Soml
class IntegerExpression < Expression
attr_accessor :value
def initialize(value)
@value = value
end
end
class FloatExpression < Expression
attr_accessor :value
def initialize(value)
@value = value
end
end
class TrueExpression < Expression
end
class FalseExpression < Expression
end
class NilExpression < Expression
end
class StringExpression < Expression
attr_accessor :value
def initialize(value)
@value = value
end
end
class NameExpression < Expression
attr_accessor :value
alias :name :value
def initialize(value)
@value = value
end
end
class ClassExpression < Expression
attr_accessor :value
def initialize(value)
@value = value
end
end
end

View File

@ -1,5 +0,0 @@
module Soml
class CallSite < Expression
attr_accessor :name , :receiver , :arguments
end
end

View File

@ -1,9 +0,0 @@
module Soml
class ClassStatement < Statement
attr_accessor :name , :derives , :statements
end
class ClassField < Statement
attr_accessor :name , :type
end
end

View File

@ -1,27 +0,0 @@
# Base class for Expresssion and Statement
module Soml
class Code
end
class Statement < Code
end
class Expression < Code
end
end
require_relative "while_statement"
require_relative "if_statement"
require_relative "return_statement"
require_relative "statements"
require_relative "operator_expression"
require_relative "field_access"
require_relative "call_site"
require_relative "basic_values"
require_relative "assignment"
require_relative "class_statement"
require_relative "function_statement"
require_relative "to_code"

View File

@ -1,5 +0,0 @@
module Soml
class FieldAccess < Expression
attr_accessor :receiver , :field
end
end

View File

@ -1,5 +0,0 @@
module Soml
class FunctionStatement < Statement
attr_accessor :return_type , :name , :parameters, :statements , :receiver
end
end

View File

@ -1,5 +0,0 @@
module Soml
class IfStatement < Statement
attr_accessor :branch_type , :condition , :if_true , :if_false
end
end

View File

@ -1,5 +0,0 @@
module Soml
class OperatorExpression < Expression
attr_accessor :operator , :left_expression , :right_expression
end
end

View File

@ -1,5 +0,0 @@
module Soml
class ReturnStatement < Statement
attr_accessor :return_value
end
end

View File

@ -1,5 +0,0 @@
module Soml
class Statements < Statement
attr_accessor :statements
end
end

View File

@ -1,163 +0,0 @@
module Soml
def self.ast_to_code statement
compiler = ToCode.new
compiler.process statement
end
class ToCode < AST::Processor
def handler_missing node
raise "No handler on_#{node.type}(node)"
end
def on_class statement
name , derives , statements = *statement
w = ClassStatement.new()
w.name = name
w.derives = derives.children.first
w.statements = process(statements)
w
end
def on_function statement
return_type , name , parameters, statements , receiver = *statement
w = FunctionStatement.new()
w.return_type = return_type
w.name = name.children.first
w.parameters = parameters.to_a.collect do |p|
raise "error, argument must be a identifier, not #{p}" unless p.type == :parameter
p.children
end
w.statements = process(statements)
w.receiver = receiver
w
end
def on_field_def statement
type , name , value = *statement
w = FieldDef.new()
w.type = type
w.name = process(name)
w.value = process(value) if value
w
end
def on_class_field statement
type , name = *statement
w = ClassField.new()
w.type = type
w.name = name
w
end
def on_while_statement statement
branch_type , condition , statements = *statement
w = WhileStatement.new()
w.branch_type = branch_type
w.condition = process(condition)
w.statements = process(statements)
w
end
def on_if_statement statement
branch_type , condition , if_true , if_false = *statement
w = IfStatement.new()
w.branch_type = branch_type
w.condition = process(condition)
w.if_true = process(if_true)
w.if_false = process(if_false)
w
end
def process_first code
raise "Too many children #{code.inspect}" if code.children.length != 1
process code.children.first
end
alias :on_conditional :process_first
alias :on_condition :process_first
alias :on_field :process_first
def on_statements statement
w = Statements.new()
return w unless statement.children
return w unless statement.children.first
w.statements = process_all(statement.children)
w
end
alias :on_true_statements :on_statements
alias :on_false_statements :on_statements
def on_return statement
w = ReturnStatement.new()
w.return_value = process(statement.children.first)
w
end
def on_operator_value statement
operator , left_e , right_e = *statement
w = OperatorExpression.new()
w.operator = operator
w.left_expression = process(left_e)
w.right_expression = process(right_e)
w
end
def on_field_access statement
receiver_ast , field_ast = *statement
w = FieldAccess.new()
w.receiver = process(receiver_ast)
w.field = process(field_ast)
w
end
def on_receiver expression
process expression.children.first
end
def on_call statement
name_s , arguments , receiver = *statement
w = CallSite.new()
w.name = name_s.children.first
w.arguments = process_all(arguments)
w.receiver = process(receiver)
w
end
def on_int expression
IntegerExpression.new(expression.children.first)
end
def on_true expression
TrueExpression.new
end
def on_false expression
FalseExpression.new
end
def on_nil expression
NilExpression.new
end
def on_name statement
NameExpression.new(statement.children.first)
end
def on_string expression
StringExpression.new(expression.children.first)
end
def on_class_name expression
ClassExpression.new(expression.children.first)
end
def on_assignment statement
name , value = *statement
w = Assignment.new()
w.name = process name
w.value = process(value)
w
end
end
end

View File

@ -1,5 +0,0 @@
module Soml
class WhileStatement < Statement
attr_accessor :branch_type , :condition , :statements
end
end

View File

@ -1,19 +1,18 @@
# -*- encoding: utf-8 -*-
Gem::Specification.new do |s|
s.name = 'soml-parser'
s.version = '0.5.0'
s.name = 'salama-reader'
s.version = '0.2.0'
s.authors = ['Torsten Ruger']
s.email = 'torsten@villataika.fi'
s.extra_rdoc_files = ['README.md']
s.files = %w(README.md LICENSE) + Dir.glob("lib/**/*")
s.homepage = 'https://github.com/salama/soml-parser'
s.homepage = 'https://github.com/salama/salama-reader'
s.license = 'GNU v3'
s.require_paths = ['lib']
s.summary = 'Ruby parser for the salama object system language'
s.summary = 'Ruby parser for the salama machine'
s.add_dependency 'parslet', '~> 1.7.1'
s.add_dependency 'ast', '~> 2.1.0'
s.add_dependency 'citrus', '~> 3.0.3'
end

View File

@ -8,10 +8,8 @@ to run just a single, replace all with what you want to test. Minitest accept a
ruby test/test_class.rb -n test_class_ops_parse
Notice tough the "_parse" at the end, while you will find no such function.
The Magic (explained below) generates three functions per case.
Your options are "_parse" , "_transform" , or if it's really bad, "_ast"
(this should really work when the previous two work)
Notice tough the "_parse" at the end, while you will find no such function. The Magic (explained below) renerates three
functions per case. Your options are "_parse" , "_transform" , or if it's really bad, "_ast" (this should really work when the previous two work)
### Directories
@ -28,7 +26,7 @@ Apart from just plain more tests, two additional directories are planned. One is
Parsing is a two step process with parslet:
- parse takes an input and outputs hashes/arrays with basic types
- transform takes the output of parse and generates an ast (as specified by the transformation)
- tramsform takes the output of parse and generates an ast (as specified by the transformation)
A test tests both phases separately and again together.
Each test must thus specify (as instance variables):
@ -45,10 +43,10 @@ test method. See test_basic for easy example.
Example:
def test_number
@string_input = '42 '
@input = '42 '
@test_output = {:integer => '42'}
@transform_output = Parser::IntegerExpression.new(42)
@parser = @parser.integer
@output = Parser::IntegerExpression.new(42)
@root = :integer
end
The first three lines define the data as described above.

View File

@ -1,9 +0,0 @@
self.a = 5
-- -- --
s(:assignment,
s(:field_access,
s(:receiver,
s(:name, :self)),
s(:field,
s(:name, :a))),
s(:int, 5))

View File

@ -1,5 +0,0 @@
name = 10
-- -- --
s(:assignment,
s(:name, :name),
s(:int, 10))

View File

@ -1,3 +0,0 @@
FooBar
-- -- --
s(:class_name, :FooBar)

View File

@ -1,3 +0,0 @@
foo
-- -- --
s(:name, :foo)

View File

@ -1,3 +0,0 @@
foo_bar
-- -- --
s(:name, :foo_bar)

View File

@ -1,3 +0,0 @@
_bar
-- -- --
s(:name, :_bar)

View File

@ -1,3 +0,0 @@
42
-- -- --
s(:int, 42)

View File

@ -1,2 +0,0 @@
"hello"-- -- --
s(:string, "hello")

View File

@ -1,2 +0,0 @@
"hello \nyou"-- -- --
s(:string, "hello \\nyou")

View File

@ -1,7 +0,0 @@
puts(3 , a )
-- -- --
s(:call,
s(:name, :puts),
s(:arguments,
s(:int, 3),
s(:name, :a)))

View File

@ -1,7 +0,0 @@
baz(42, foo)
-- -- --
s(:call,
s(:name, :baz),
s(:arguments,
s(:int, 42),
s(:name, :foo)))

View File

@ -1,6 +0,0 @@
puts( "hello")
-- -- --
s(:call,
s(:name, :puts),
s(:arguments,
s(:string, "hello")))

View File

@ -1,7 +0,0 @@
42.put()
-- -- --
s(:call,
s(:name, :put),
s(:arguments),
s(:receiver,
s(:int, 42)))

View File

@ -1,6 +0,0 @@
puts( 5)
-- -- --
s(:call,
s(:name, :puts),
s(:arguments,
s(:int, 5)))

View File

@ -1,6 +0,0 @@
foo(42)
-- -- --
s(:call,
s(:name, :foo),
s(:arguments,
s(:int, 42)))

View File

@ -1,8 +0,0 @@
Object.foo(42)
-- -- --
s(:call,
s(:name, :foo),
s(:arguments,
s(:int, 42)),
s(:receiver,
s(:class_name, :Object)))

View File

@ -1,8 +0,0 @@
my_my.foo(42)
-- -- --
s(:call,
s(:name, :foo),
s(:arguments,
s(:int, 42)),
s(:receiver,
s(:name, :my_my)))

View File

@ -1,8 +0,0 @@
self.foo(42)
-- -- --
s(:call,
s(:name, :foo),
s(:arguments,
s(:int, 42)),
s(:receiver,
s(:name, :self)))

View File

@ -1,7 +0,0 @@
"hello".puts()
-- -- --
s(:call,
s(:name, :puts),
s(:arguments),
s(:receiver,
s(:string, "hello")))

View File

@ -1,161 +0,0 @@
class String
String self.new_string(int len )
len = len << 2
return super.new_object( len)
end
int length()
return self.length
end
int plus(String str)
my_length = self.length
str_len = str.length()
my_length = str_len + my_length
new_string = self.new_string(my_length )
i = 0
while_plus( i - my_length)
char = get(i)
new_string.set(i , char)
i = i + 1
end
i = 0
while_plus( i - str_len)
char = str.get(i)
len = i + my_length
new_string.set( len , char)
i = i + 1
end
return new_string
end
end
-- -- --
s(:statements,
s(:class, :String,
s(:derives, nil),
s(:statements,
s(:function, :String,
s(:name, :new_string),
s(:parameters,
s(:parameter, :Integer, :len)),
s(:statements,
s(:assignment,
s(:name, :len),
s(:operator_value, :<<,
s(:name, :len),
s(:int, 2))),
s(:return,
s(:call,
s(:name, :new_object),
s(:arguments,
s(:name, :len)),
s(:receiver,
s(:name, :super))))),
s(:receiver, :self)),
s(:function, :Integer,
s(:name, :length),
s(:parameters),
s(:statements,
s(:return,
s(:field_access,
s(:receiver,
s(:name, :self)),
s(:field,
s(:name, :length)))))),
s(:function, :Integer,
s(:name, :plus),
s(:parameters,
s(:parameter, :String, :str)),
s(:statements,
s(:assignment,
s(:name, :my_length),
s(:field_access,
s(:receiver,
s(:name, :self)),
s(:field,
s(:name, :length)))),
s(:assignment,
s(:name, :str_len),
s(:call,
s(:name, :length),
s(:arguments),
s(:receiver,
s(:name, :str)))),
s(:assignment,
s(:name, :my_length),
s(:operator_value, :+,
s(:name, :str_len),
s(:name, :my_length))),
s(:assignment,
s(:name, :new_string),
s(:call,
s(:name, :new_string),
s(:arguments,
s(:name, :my_length)),
s(:receiver,
s(:name, :self)))),
s(:assignment,
s(:name, :i),
s(:int, 0)),
s(:while_statement, :plus,
s(:conditional,
s(:operator_value, :-,
s(:name, :i),
s(:name, :my_length))),
s(:statements,
s(:assignment,
s(:name, :char),
s(:call,
s(:name, :get),
s(:arguments,
s(:name, :i)))),
s(:call,
s(:name, :set),
s(:arguments,
s(:name, :i),
s(:name, :char)),
s(:receiver,
s(:name, :new_string))),
s(:assignment,
s(:name, :i),
s(:operator_value, :+,
s(:name, :i),
s(:int, 1))))),
s(:assignment,
s(:name, :i),
s(:int, 0)),
s(:while_statement, :plus,
s(:conditional,
s(:operator_value, :-,
s(:name, :i),
s(:name, :str_len))),
s(:statements,
s(:assignment,
s(:name, :char),
s(:call,
s(:name, :get),
s(:arguments,
s(:name, :i)),
s(:receiver,
s(:name, :str)))),
s(:assignment,
s(:name, :len),
s(:operator_value, :+,
s(:name, :i),
s(:name, :my_length))),
s(:call,
s(:name, :set),
s(:arguments,
s(:name, :len),
s(:name, :char)),
s(:receiver,
s(:name, :new_string))),
s(:assignment,
s(:name, :i),
s(:operator_value, :+,
s(:name, :i),
s(:int, 1))))),
s(:return,
s(:name, :new_string)))))))

View File

@ -1,23 +0,0 @@
class Foo < Object
field int fff = 3
int func()
return self.fff
end
end
-- -- --
s(:statements,
s(:class, :Foo,
s(:derives, :Object),
s(:statements,
s(:class_field, :Integer, :fff,
s(:int, 3)),
s(:function, :Integer,
s(:name, :func),
s(:parameters),
s(:statements,
s(:return,
s(:field_access,
s(:receiver,
s(:name, :self)),
s(:field,
s(:name, :fff)))))))))

View File

@ -1,30 +0,0 @@
class Pifi
int self.ofthen(int n , Object m)
n = n + m.index
return n
end
end
-- -- --
s(:statements,
s(:class, :Pifi,
s(:derives, nil),
s(:statements,
s(:function, :Integer,
s(:name, :ofthen),
s(:parameters,
s(:parameter, :Integer, :n),
s(:parameter, :Object, :m)),
s(:statements,
s(:assignment,
s(:name, :n),
s(:operator_value, :+,
s(:name, :n),
s(:field_access,
s(:receiver,
s(:name, :m)),
s(:field,
s(:name, :index))))),
s(:return,
s(:name, :n))),
s(:receiver, :self)))))

View File

@ -1,30 +0,0 @@
class Ifi
int ofthen(int n)
if_zero(0)
isit = 42
else
maybenot = 667
end
end
end
-- -- --
s(:statements,
s(:class, :Ifi,
s(:derives, nil),
s(:statements,
s(:function, :Integer,
s(:name, :ofthen),
s(:parameters,
s(:parameter, :Integer, :n)),
s(:statements,
s(:if_statement, :zero,
s(:condition,
s(:int, 0)),
s(:true_statements,
s(:assignment,
s(:name, :isit),
s(:int, 42))),
s(:false_statements,
s(:assignment,
s(:name, :maybenot),
s(:int, 667)))))))))

View File

@ -1,16 +0,0 @@
class Foo < Object
int test()
return 43
end
end
-- -- --
s(:statements,
s(:class, :Foo,
s(:derives, :Object),
s(:statements,
s(:function, :Integer,
s(:name, :test),
s(:parameters),
s(:statements,
s(:return,
s(:int, 43)))))))

View File

@ -1,23 +0,0 @@
class Opers
int foo(int x)
int abba = 5
return abba + 5
end
end
-- -- --
s(:statements,
s(:class, :Opers,
s(:derives, nil),
s(:statements,
s(:function, :Integer,
s(:name, :foo),
s(:parameters,
s(:parameter, :Integer, :x)),
s(:statements,
s(:field_def, :Integer,
s(:name, :abba),
s(:int, 5)),
s(:return,
s(:operator_value, :+,
s(:name, :abba),
s(:int, 5))))))))

View File

@ -1,9 +0,0 @@
class Foo
field int x
end
-- -- --
s(:statements,
s(:class, :Foo,
s(:derives, nil),
s(:statements,
s(:class_field, :Integer, :x))))

View File

@ -1,7 +0,0 @@
foo.bar
-- -- --
s(:field_access,
s(:receiver,
s(:name, :foo)),
s(:field,
s(:name, :bar)))

View File

@ -1,13 +0,0 @@
int self.length( Object x )
return 5
end
-- -- --
s(:statements,
s(:function, :Integer,
s(:name, :length),
s(:parameters,
s(:parameter, :Object, :x)),
s(:statements,
s(:return,
s(:int, 5))),
s(:receiver, :self)))

View File

@ -1,48 +0,0 @@
int fibonaccit(int n)
a = 0
b = 1
while_positive( n )
tmp = a
a = b
b = tmp + b
puts(b)
n = n - 1
end
end
-- -- --
s(:statements,
s(:function, :Integer,
s(:name, :fibonaccit),
s(:parameters,
s(:parameter, :Integer, :n)),
s(:statements,
s(:assignment,
s(:name, :a),
s(:int, 0)),
s(:assignment,
s(:name, :b),
s(:int, 1)),
s(:while_statement, :positive,
s(:conditional,
s(:name, :n)),
s(:statements,
s(:assignment,
s(:name, :tmp),
s(:name, :a)),
s(:assignment,
s(:name, :a),
s(:name, :b)),
s(:assignment,
s(:name, :b),
s(:operator_value, :+,
s(:name, :tmp),
s(:name, :b))),
s(:call,
s(:name, :puts),
s(:arguments,
s(:name, :b))),
s(:assignment,
s(:name, :n),
s(:operator_value, :-,
s(:name, :n),
s(:int, 1))))))))

View File

@ -1,16 +0,0 @@
int foo(int x)
int a = 5
return a
end
-- -- --
s(:statements,
s(:function, :Integer,
s(:name, :foo),
s(:parameters,
s(:parameter, :Integer, :x)),
s(:statements,
s(:field_def, :Integer,
s(:name, :a),
s(:int, 5)),
s(:return,
s(:name, :a)))))

View File

@ -1,25 +0,0 @@
int ofthen(int n)
if_plus(0)
isit = 42
else
maybenot = 667
end
end
-- -- --
s(:statements,
s(:function, :Integer,
s(:name, :ofthen),
s(:parameters,
s(:parameter, :Integer, :n)),
s(:statements,
s(:if_statement, :plus,
s(:condition,
s(:int, 0)),
s(:true_statements,
s(:assignment,
s(:name, :isit),
s(:int, 42))),
s(:false_statements,
s(:assignment,
s(:name, :maybenot),
s(:int, 667)))))))

View File

@ -1,11 +0,0 @@
int foo()
return 5
end
-- -- --
s(:statements,
s(:function, :Integer,
s(:name, :foo),
s(:parameters),
s(:statements,
s(:return,
s(:int, 5)))))

View File

@ -1,19 +0,0 @@
int foo(int x)
int abba = 5
abba = abba + 5
end
-- -- --
s(:statements,
s(:function, :Integer,
s(:name, :foo),
s(:parameters,
s(:parameter, :Integer, :x)),
s(:statements,
s(:field_def, :Integer,
s(:name, :abba),
s(:int, 5)),
s(:assignment,
s(:name, :abba),
s(:operator_value, :+,
s(:name, :abba),
s(:int, 5))))))

View File

@ -1,20 +0,0 @@
int retvar(Object n)
int i = n.layout
return i
end
-- -- --
s(:statements,
s(:function, :Integer,
s(:name, :retvar),
s(:parameters,
s(:parameter, :Object, :n)),
s(:statements,
s(:field_def, :Integer,
s(:name, :i),
s(:field_access,
s(:receiver,
s(:name, :n)),
s(:field,
s(:name, :layout)))),
s(:return,
s(:name, :i)))))

View File

@ -1,25 +0,0 @@
int retvar(int n)
if_positive( n - 5)
return 10
else
return 20
end
end
-- -- --
s(:statements,
s(:function, :Integer,
s(:name, :retvar),
s(:parameters,
s(:parameter, :Integer, :n)),
s(:statements,
s(:if_statement, :positive,
s(:condition,
s(:operator_value, :-,
s(:name, :n),
s(:int, 5))),
s(:true_statements,
s(:return,
s(:int, 10))),
s(:false_statements,
s(:return,
s(:int, 20)))))))

View File

@ -1,26 +0,0 @@
int retvar(int n )
while_positive( n - 5)
n = n + 1
return n
end
end
-- -- --
s(:statements,
s(:function, :Integer,
s(:name, :retvar),
s(:parameters,
s(:parameter, :Integer, :n)),
s(:statements,
s(:while_statement, :positive,
s(:conditional,
s(:operator_value, :-,
s(:name, :n),
s(:int, 5))),
s(:statements,
s(:assignment,
s(:name, :n),
s(:operator_value, :+,
s(:name, :n),
s(:int, 1))),
s(:return,
s(:name, :n)))))))

View File

@ -1,13 +0,0 @@
int foo( int n ,String m)
return n
end
-- -- --
s(:statements,
s(:function, :Integer,
s(:name, :foo),
s(:parameters,
s(:parameter, :Integer, :n),
s(:parameter, :String, :m)),
s(:statements,
s(:return,
s(:name, :n)))))

View File

@ -1,29 +0,0 @@
Biggie fibonaccit(int n)
a = 0
while_ok(n)
some = 43
other = some * 4
end
end
-- -- --
s(:statements,
s(:function, :Biggie,
s(:name, :fibonaccit),
s(:parameters,
s(:parameter, :Integer, :n)),
s(:statements,
s(:assignment,
s(:name, :a),
s(:int, 0)),
s(:while_statement, :ok,
s(:conditional,
s(:name, :n)),
s(:statements,
s(:assignment,
s(:name, :some),
s(:int, 43)),
s(:assignment,
s(:name, :other),
s(:operator_value, :*,
s(:name, :some),
s(:int, 4))))))))

View File

@ -1,13 +0,0 @@
int foo(Class x)
a = 1
end
-- -- --
s(:statements,
s(:function, :Integer,
s(:name, :foo),
s(:parameters,
s(:parameter, :Class, :x)),
s(:statements,
s(:assignment,
s(:name, :a),
s(:int, 1)))))

View File

@ -1,17 +0,0 @@
if_true(0)
fourty = 10
else
twenty = 5
end
-- -- --
s(:if_statement, :true,
s(:condition,
s(:int, 0)),
s(:true_statements,
s(:assignment,
s(:name, :fourty),
s(:int, 10))),
s(:false_statements,
s(:assignment,
s(:name, :twenty),
s(:int, 5))))

View File

@ -1,25 +0,0 @@
if_yes(3 > var)
Object.initialize(3)
else
var.new(33)
end
-- -- --
s(:if_statement, :yes,
s(:condition,
s(:operator_value, :>,
s(:int, 3),
s(:name, :var))),
s(:true_statements,
s(:call,
s(:name, :initialize),
s(:arguments,
s(:int, 3)),
s(:receiver,
s(:class_name, :Object)))),
s(:false_statements,
s(:call,
s(:name, :new),
s(:arguments,
s(:int, 33)),
s(:receiver,
s(:name, :var)))))

View File

@ -1,9 +0,0 @@
foo.bar - gumbar
-- -- --
s(:operator_value, :-,
s(:field_access,
s(:receiver,
s(:name, :foo)),
s(:field,
s(:name, :bar))),
s(:name, :gumbar))

View File

@ -1,9 +0,0 @@
5 % foo.bar
-- -- --
s(:operator_value, :%,
s(:int, 5),
s(:field_access,
s(:receiver,
s(:name, :foo)),
s(:field,
s(:name, :bar))))

View File

@ -1,5 +0,0 @@
3 > var
-- -- --
s(:operator_value, :>,
s(:int, 3),
s(:name, :var))

View File

@ -1,5 +0,0 @@
5 + 7
-- -- --
s(:operator_value, :+,
s(:int, 5),
s(:int, 7))

View File

@ -1,5 +0,0 @@
a - b
-- -- --
s(:operator_value, :-,
s(:name, :a),
s(:name, :b))

View File

@ -1,5 +0,0 @@
a - 5
-- -- --
s(:operator_value, :-,
s(:name, :a),
s(:int, 5))

View File

@ -1,5 +0,0 @@
a - "st"
-- -- --
s(:operator_value, :-,
s(:name, :a),
s(:string, "st"))

View File

@ -1,5 +0,0 @@
a == true
-- -- --
s(:operator_value, :==,
s(:name, :a),
s(:true))

View File

@ -1,5 +0,0 @@
5 / 3
-- -- --
s(:operator_value, :/,
s(:int, 5),
s(:int, 3))

View File

@ -1,5 +0,0 @@
5 > 3
-- -- --
s(:operator_value, :>,
s(:int, 5),
s(:int, 3))

View File

@ -1,5 +0,0 @@
5 - 3
-- -- --
s(:operator_value, :-,
s(:int, 5),
s(:int, 3))

View File

@ -1,5 +0,0 @@
5 * 3
-- -- --
s(:operator_value, :*,
s(:int, 5),
s(:int, 3))

View File

@ -1,5 +0,0 @@
5 + 3
-- -- --
s(:operator_value, :+,
s(:int, 5),
s(:int, 3))

View File

@ -1,5 +0,0 @@
5 < 3
-- -- --
s(:operator_value, :<,
s(:int, 5),
s(:int, 3))

View File

@ -1,9 +0,0 @@
gumbar & foo.bar
-- -- --
s(:operator_value, :&,
s(:name, :gumbar),
s(:field_access,
s(:receiver,
s(:name, :foo)),
s(:field,
s(:name, :bar))))

View File

@ -1,5 +0,0 @@
bar - gumbar
-- -- --
s(:operator_value, :-,
s(:name, :bar),
s(:name, :gumbar))

View File

@ -1,4 +0,0 @@
return 42
-- -- --
s(:return,
s(:int, 42))

View File

@ -1,4 +0,0 @@
return "hello"
-- -- --
s(:return,
s(:string, "hello"))

View File

@ -1,4 +0,0 @@
return foo
-- -- --
s(:return,
s(:name, :foo))

View File

@ -1,21 +0,0 @@
class FooBo
int main()
Bar.call(35)
end
end
-- -- --
s(:statements,
s(:class, :FooBo,
s(:derives, nil),
s(:statements,
s(:function, :Integer,
s(:name, :main),
s(:parameters),
s(:statements,
s(:call,
s(:name, :call),
s(:arguments,
s(:int, 35)),
s(:receiver,
s(:class_name, :Bar))))))))

View File

@ -1,34 +0,0 @@
class Object
int foo(String x)
a = 5
end
end
class Other < Object
int foo()
foo( 3 )
end
end
-- -- --
s(:statements,
s(:class, :Object,
s(:derives, nil),
s(:statements,
s(:function, :Integer,
s(:name, :foo),
s(:parameters,
s(:parameter, :String, :x)),
s(:statements,
s(:assignment,
s(:name, :a),
s(:int, 5)))))),
s(:class, :Other,
s(:derives, :Object),
s(:statements,
s(:function, :Integer,
s(:name, :foo),
s(:parameters),
s(:statements,
s(:call,
s(:name, :foo),
s(:arguments,
s(:int, 3))))))))

View File

@ -1,65 +0,0 @@
class Object
int fibonaccit(int n)
a = 0
b = 1
while_plus( n )
tmp = a
a = b
b = tmp + b
puts(b)
n = n - 1
end
end
int main()
fibonaccit( 10 )
end
end
-- -- --
s(:statements,
s(:class, :Object,
s(:derives, nil),
s(:statements,
s(:function, :Integer,
s(:name, :fibonaccit),
s(:parameters,
s(:parameter, :Integer, :n)),
s(:statements,
s(:assignment,
s(:name, :a),
s(:int, 0)),
s(:assignment,
s(:name, :b),
s(:int, 1)),
s(:while_statement, :plus,
s(:conditional,
s(:name, :n)),
s(:statements,
s(:assignment,
s(:name, :tmp),
s(:name, :a)),
s(:assignment,
s(:name, :a),
s(:name, :b)),
s(:assignment,
s(:name, :b),
s(:operator_value, :+,
s(:name, :tmp),
s(:name, :b))),
s(:call,
s(:name, :puts),
s(:arguments,
s(:name, :b))),
s(:assignment,
s(:name, :n),
s(:operator_value, :-,
s(:name, :n),
s(:int, 1))))))),
s(:function, :Integer,
s(:name, :main),
s(:parameters),
s(:statements,
s(:call,
s(:name, :fibonaccit),
s(:arguments,
s(:int, 10))))))))

View File

@ -1,12 +0,0 @@
if_zero(0)
four = 42
end
-- -- --
s(:if_statement, :zero,
s(:condition,
s(:int, 0)),
s(:true_statements,
s(:assignment,
s(:name, :four),
s(:int, 42))),
s(:false_statements, nil))

View File

@ -1,17 +0,0 @@
if_overflow(3 + 100000 )
Object.initialize(3)
end
-- -- --
s(:if_statement, :overflow,
s(:condition,
s(:operator_value, :+,
s(:int, 3),
s(:int, 100000))),
s(:true_statements,
s(:call,
s(:name, :initialize),
s(:arguments,
s(:int, 3)),
s(:receiver,
s(:class_name, :Object)))),
s(:false_statements, nil))

View File

@ -1,34 +0,0 @@
while_allgood( n > 1)
tmp = a
a = b
b = tmp + b
puts(b)
n = n - 1
end
-- -- --
s(:while_statement, :allgood,
s(:conditional,
s(:operator_value, :>,
s(:name, :n),
s(:int, 1))),
s(:statements,
s(:assignment,
s(:name, :tmp),
s(:name, :a)),
s(:assignment,
s(:name, :a),
s(:name, :b)),
s(:assignment,
s(:name, :b),
s(:operator_value, :+,
s(:name, :tmp),
s(:name, :b))),
s(:call,
s(:name, :puts),
s(:arguments,
s(:name, :b))),
s(:assignment,
s(:name, :n),
s(:operator_value, :-,
s(:name, :n),
s(:int, 1)))))

View File

@ -1,16 +0,0 @@
while_false(1)
tmp = a
puts(b)
end
-- -- --
s(:while_statement, :false,
s(:conditional,
s(:int, 1)),
s(:statements,
s(:assignment,
s(:name, :tmp),
s(:name, :a)),
s(:call,
s(:name, :puts),
s(:arguments,
s(:name, :b)))))

View File

@ -1,22 +0,0 @@
while_true(1)
tmp = String.new()
tmp.puts(i)
end
-- -- --
s(:while_statement, :true,
s(:conditional,
s(:int, 1)),
s(:statements,
s(:assignment,
s(:name, :tmp),
s(:call,
s(:name, :new),
s(:arguments),
s(:receiver,
s(:class_name, :String)))),
s(:call,
s(:name, :puts),
s(:arguments,
s(:name, :i)),
s(:receiver,
s(:name, :tmp)))))

View File

@ -1,87 +1,21 @@
require_relative "setup"
require "parslet/convenience"
require "ast"
# Older test harness
# Included in parser test will create tests methods
module ParserHelper
include AST::Sexp
def self.included(base)
base.send :include, InstanceMethods #provides helpers and setup
base.send :include, AST::true_statements
base.send :extend, ClassMethods #gets the method creation going
# base.send :include, InstanceMethods #provides helpers and setup
end
module InstanceMethods
def setup
@parser = Parser::Salama.new
@transform = Parser::Transform.new
end
# check that @string_input parses correctly to @parse_output
def check_parse
is = @parser.parse_with_debug(@string_input)
assert_equal @parse_output , is
end
#check that @parse_output transforms to @transform_output
def check_transform
is = @transform.apply @parse_output
#puts is.transform
assert_equal @transform_output , is
end
# check that @string_input parses and transforms to @transform_output
def check_ast
syntax = @parser.parse(@string_input)
is = @transform.apply(syntax)
puts is.inspect
assert_equal @transform_output , is
end
def check_write test
dirname = decamelize(self.class.name)[10 .. -1]
test = test[5 .. -1]
syntax = @parser.parse_with_debug(@string_input)
out = Parser::Transform.new.apply(syntax).inspect
dir = File.dirname(__FILE__) + "/" + dirname
FileUtils.mkdir_p(dir) unless File.exists?(dir)
out_file = File.new(dir + "/" + test + ".tst", "w")
out_file.puts @string_input
out_file.puts "-- -- --"
out_file.puts out
out_file.close
end
def decamelize str
str.gsub(/(^|[a-z])([A-Z])/) do
($1.empty?)? $2 : "#{$1}_#{$2}"
end.downcase
end
def camelize str
str.gsub(/(^|_)([a-z])/) { $2.upcase }
end
def setup
@parser = Bosl
end
module ClassMethods
# this creates test methods dynamically. For each test_* method we create
# three test_*[ast/parse/transf] methods that in turn check the three phases.
# runnable_methods is called by minitest to determine which tests to run
def runnable_methods
tests = []
public_instance_methods(true).grep(/^test_/).map(&:to_s).each do |test|
["write"].each do |what|
name = "#{test}_#{what}"
tests << name
self.send(:define_method, name ) do
send(test)
send("check_#{what}" , test)
end
end
end
tests
end
end
def check rule = :root
parse = @parser.parse(@input , :root => rule)
assert_equal @input , parse
assert_equal @output , parse.value
end
end

11
test/roots/test_all.rb Normal file
View File

@ -0,0 +1,11 @@
require_relative "test_basic"
require_relative "test_call_site"
require_relative "test_class"
require_relative "test_compound"
require_relative "test_conditional"
require_relative "test_function_definition"
require_relative "test_module"
require_relative "test_operators"
require_relative "test_return"
require_relative "test_root"
require_relative "test_while"

56
test/roots/test_basic.rb Normal file
View File

@ -0,0 +1,56 @@
require_relative "../parser_helper"
class RootTestBasic < MiniTest::Test
# include the magic (setup and parse -> test method translation), see there
include ParserHelper
def test_number
@input = '42 '
@output = Ast::ExpressionList.new( [Ast::IntegerExpression.new(42)])
end
def test_name
@input = 'foo '
@output = Ast::ExpressionList.new( [Ast::NameExpression.new(:foo)])
end
def test_name_underscode_start
@input = '_bar '
@output = Ast::ExpressionList.new( [Ast::NameExpression.new(:_bar)])
end
def test_name_underscode_middle
@input = 'foo_bar '
@output = Ast::ExpressionList.new( [Ast::NameExpression.new(:foo_bar)])
end
def test_instance_variable
@input = '@foo_bar '
@output = Ast::ExpressionList.new( [Ast::VariableExpression.new(:foo_bar)])
end
def test_module_name
@input = 'FooBar '
@output = Ast::ExpressionList.new( [Ast::ModuleName.new(:FooBar)])
end
def ttest_comment
out = "# i am a comment \n"
@input = out.dup #NEEDS the return, which is what delimits the comment
@output = @parse_output #dont transform
end
def test_string
@input = "\"hello\""
@parse_output = {:expression_list=>[{:string=>[{:char=>"h"}, {:char=>"e"}, {:char=>"l"}, {:char=>"l"}, {:char=>"o"}]}]}
@output = Ast::ExpressionList.new( [Ast::StringExpression.new("hello")])
end
def test_string_escapes
out = 'hello \nyou'
@input = '"' + out + '"'
@parse_output = {:expression_list=>[{:string=>[{:char=>"h"}, {:char=>"e"}, {:char=>"l"}, {:char=>"l"}, {:char=>"o"}, {:char=>" "}, {:char=>" "}, {:esc=>"n"}, {:char=>"y"}, {:char=>"o"}, {:char=>"u"}]}]}
@output = Ast::ExpressionList.new( [Ast::StringExpression.new(out)])
end
end

33
test/roots/test_block.rb Normal file
View File

@ -0,0 +1,33 @@
require_relative "../parser_helper"
class RootTestBlock < MiniTest::Test
# include the magic (setup and parse -> test method translation), see there
include ParserHelper
def pest_block
@input = <<HERE
self.call(1) do |a , b|
tmp = a
puts(b)
end
HERE
@parse_output = nil
@output = nil
@root = :
end
def pest_big_block
@input = <<HERE
self.call( true , false) do | a , b |
tmp = a
a = b
b = tmp + b
puts(b)
n = n - 1
end
HERE
@parse_output = {:expression_list=>[{:while=>"while", :while_cond=>{:l=>{:name=>"n"}, :o=>"> ", :r=>{:integer=>"1"}}, :do=>"do", :body=>{:expressions=>[{:l=>{:name=>"tmp"}, :o=>"= ", :r=>{:name=>"a"}}, {:l=>{:name=>"a"}, :o=>"= ", :r=>{:name=>"b"}}, {:l=>{:name=>"b"}, :o=>"= ", :r=>{:l=>{:name=>"tmp"}, :o=>"+ ", :r=>{:name=>"b"}}}, {:call_site=>{:name=>"puts"}, :argument_list=>[{:argument=>{:name=>"b"}}]}, {:l=>{:name=>"n"}, :o=>"= ", :r=>{:l=>{:name=>"n"}, :o=>"- ", :r=>{:integer=>"1"}}}], :end=>"end"}}]}
@output = Ast::ExpressionList.new( [Ast::BlockExpression.new(Ast::OperatorExpression.new(">", Ast::NameExpression.new(:n),Ast::IntegerExpression.new(1)), [Ast::AssignmentExpression.new(Ast::NameExpression.new(:tmp),Ast::NameExpression.new(:a)), Ast::AssignmentExpression.new(Ast::NameExpression.new(:a),Ast::NameExpression.new(:b)), Ast::AssignmentExpression.new(Ast::NameExpression.new(:b),Ast::OperatorExpression.new("+", Ast::NameExpression.new(:tmp),Ast::NameExpression.new(:b))), Ast::CallSiteExpression.new(:puts, [Ast::NameExpression.new(:b)] ,Ast::NameExpression.new(:self)), Ast::AssignmentExpression.new(Ast::NameExpression.new(:n),Ast::OperatorExpression.new("-", Ast::NameExpression.new(:n),Ast::IntegerExpression.new(1)))] )])
@root = :
end
end

View File

@ -0,0 +1,111 @@
require_relative "../parser_helper"
class RootTestCallSite < MiniTest::Test
# include the magic (setup and parse -> test method translation), see there
include ParserHelper
def test_single_argument
@input = 'foo(42)'
@parse_output = {:expression_list=>[{:call_site=>{:name=>"foo"}, :argument_list=>[{:argument=>{:integer=>"42"}}]}]}
@output = Ast::ExpressionList.new( [Ast::CallSiteExpression.new(:foo, [Ast::IntegerExpression.new(42)] ,Ast::NameExpression.new(:self))])
@root = :
end
def test_single_self
@input = 'self.foo(42)'
@parse_output = {:expression_list=>[{:receiver=>{:name=>"self"}, :call_site=>{:name=>"foo"}, :argument_list=>[{:argument=>{:integer=>"42"}}]}]}
@output = Ast::ExpressionList.new( [Ast::CallSiteExpression.new(:foo, [Ast::IntegerExpression.new(42)] ,Ast::NameExpression.new(:self))])
@root = :
end
def test_single_instance
@input = '@var.foo(42)'
@parse_output = {:expression_list=>[{:receiver=>{:instance_variable=>{:name=>"var"}}, :call_site=>{:name=>"foo"}, :argument_list=>[{:argument=>{:integer=>"42"}}]}]}
@output = Ast::ExpressionList.new( [Ast::CallSiteExpression.new(:foo, [Ast::IntegerExpression.new(42)] ,Ast::VariableExpression.new(:var))])
@root = :
end
def test_single_name
@input = 'my_my.foo(42)'
@parse_output = {:expression_list=>[{:receiver=>{:name=>"my_my"}, :call_site=>{:name=>"foo"}, :argument_list=>[{:argument=>{:integer=>"42"}}]}]}
@output = Ast::ExpressionList.new( [Ast::CallSiteExpression.new(:foo, [Ast::IntegerExpression.new(42)] ,Ast::NameExpression.new(:my_my))])
@root = :
end
def test_int_receiver
@input = '42.put()'
@parse_output = {:expression_list=>[{:receiver=>{:integer=>"42"}, :call_site=>{:name=>"put"}, :argument_list=>[]}]}
@output = Ast::ExpressionList.new( [Ast::CallSiteExpression.new(:put, [] ,Ast::IntegerExpression.new(42))])
@root = :
end
def test_string_receiver
@input = '"hello".puts()'
@parse_output = {:expression_list=>[{:receiver=>{:string=>[{:char=>"h"}, {:char=>"e"}, {:char=>"l"}, {:char=>"l"}, {:char=>"o"}]}, :call_site=>{:name=>"puts"}, :argument_list=>[]}]}
@output = Ast::ExpressionList.new( [Ast::CallSiteExpression.new(:puts, [] ,Ast::StringExpression.new("hello"))])
@root = :
end
def test_single_class
@input = 'Object.foo(42)'
@parse_output = {:expression_list=>[{:receiver=>{:module_name=>"Object"}, :call_site=>{:name=>"foo"}, :argument_list=>[{:argument=>{:integer=>"42"}}]}]}
@output = Ast::ExpressionList.new( [Ast::CallSiteExpression.new(:foo, [Ast::IntegerExpression.new(42)] ,Ast::ModuleName.new(:Object))])
@root = :
end
def test_call_site_multi
@input = 'baz(42, foo)'
@parse_output = {:expression_list=>[{:call_site=>{:name=>"baz"}, :argument_list=>[{:argument=>{:integer=>"42"}}, {:argument=>{:name=>"foo"}}]}]}
@output = Ast::ExpressionList.new( [Ast::CallSiteExpression.new(:baz, [Ast::IntegerExpression.new(42),Ast::NameExpression.new(:foo)] ,Ast::NameExpression.new(:self))])
@root = :
end
def test_call_site_string
@input = 'puts( "hello")'
@parse_output = {:expression_list=>[{:call_site=>{:name=>"puts"}, :argument_list=>[{:argument=>{:string=>[{:char=>"h"}, {:char=>"e"}, {:char=>"l"}, {:char=>"l"}, {:char=>"o"}]}}]}]}
@output = Ast::ExpressionList.new( [Ast::CallSiteExpression.new(:puts, [Ast::StringExpression.new("hello")] ,Ast::NameExpression.new(:self))])
@root = :
end
def test_call_operator
@input = 'puts( 3 + 5)'
@parse_output = {:expression_list=>[{:call_site=>{:name=>"puts"}, :argument_list=>[{:argument=>{:l=>{:integer=>"3"}, :o=>"+ ", :r=>{:integer=>"5"}}}]}]}
@output = Ast::ExpressionList.new( [Ast::CallSiteExpression.new(:puts, [Ast::OperatorExpression.new("+", Ast::IntegerExpression.new(3),Ast::IntegerExpression.new(5))] ,Ast::NameExpression.new(:self))])
@root = :
end
def test_call_two_operators
@input = 'puts(3 + 5 , a - 3)'
@parse_output = {:expression_list=>[{:call_site=>{:name=>"puts"}, :argument_list=>[{:argument=>{:l=>{:integer=>"3"}, :o=>"+ ", :r=>{:integer=>"5"}}}, {:argument=>{:l=>{:name=>"a"}, :o=>"- ", :r=>{:integer=>"3"}}}]}]}
@output = Ast::ExpressionList.new( [Ast::CallSiteExpression.new(:puts, [Ast::OperatorExpression.new("+", Ast::IntegerExpression.new(3),Ast::IntegerExpression.new(5)),Ast::OperatorExpression.new("-", Ast::NameExpression.new(:a),Ast::IntegerExpression.new(3))] ,Ast::NameExpression.new(:self))])
@root = :
end
def test_call_chaining
@input = 'puts(putint(3 + 5 ), a - 3)'
@parse_output = {:expression_list=>[{:call_site=>{:name=>"puts"}, :argument_list=>[{:argument=>{:call_site=>{:name=>"putint"}, :argument_list=>[{:argument=>{:l=>{:integer=>"3"}, :o=>"+ ", :r=>{:integer=>"5"}}}]}}, {:argument=>{:l=>{:name=>"a"}, :o=>"- ", :r=>{:integer=>"3"}}}]}]}
@output = Ast::ExpressionList.new( [Ast::CallSiteExpression.new(:puts, [Ast::CallSiteExpression.new(:putint, [Ast::OperatorExpression.new("+", Ast::IntegerExpression.new(3),Ast::IntegerExpression.new(5))] ,Ast::NameExpression.new(:self)),Ast::OperatorExpression.new("-", Ast::NameExpression.new(:a),Ast::IntegerExpression.new(3))] ,Ast::NameExpression.new(:self))])
@root = :
end
def test_call_chaining_name
@input = 'puts(name.putint(4), a)'
@parse_output = {:expression_list=>[{:call_site=>{:name=>"puts"}, :argument_list=>[{:argument=>{:receiver=>{:name=>"name"}, :call_site=>{:name=>"putint"}, :argument_list=>[{:argument=>{:integer=>"4"}}]}}, {:argument=>{:name=>"a"}}]}]}
@output = Ast::ExpressionList.new( [Ast::CallSiteExpression.new(:puts, [Ast::CallSiteExpression.new(:putint, [Ast::IntegerExpression.new(4)] ,Ast::NameExpression.new(:name)),Ast::NameExpression.new(:a)] ,Ast::NameExpression.new(:self))])
@root = :
end
def test_call_chaining_class
@input = 'Class.new(self.get(4))'
@parse_output = {:expression_list=>[{:receiver=>{:module_name=>"Class"}, :call_site=>{:name=>"new"}, :argument_list=>[{:argument=>{:receiver=>{:name=>"self"}, :call_site=>{:name=>"get"}, :argument_list=>[{:argument=>{:integer=>"4"}}]}}]}]}
@output = Ast::ExpressionList.new( [Ast::CallSiteExpression.new(:new, [Ast::CallSiteExpression.new(:get, [Ast::IntegerExpression.new(4)] ,Ast::NameExpression.new(:self))] ,Ast::ModuleName.new(:Class))])
@root = :
end
def test_call_chaining_instance
@input = '@class.new(self.get(4))'
@parse_output = {:expression_list=>[{:receiver=>{:instance_variable=>{:name=>"class"}}, :call_site=>{:name=>"new"}, :argument_list=>[{:argument=>{:receiver=>{:name=>"self"}, :call_site=>{:name=>"get"}, :argument_list=>[{:argument=>{:integer=>"4"}}]}}]}]}
@output = Ast::ExpressionList.new( [Ast::CallSiteExpression.new(:new, [Ast::CallSiteExpression.new(:get, [Ast::IntegerExpression.new(4)] ,Ast::NameExpression.new(:self))] ,Ast::VariableExpression.new(:class))])
@root = :
end
end

90
test/roots/test_class.rb Normal file
View File

@ -0,0 +1,90 @@
require_relative "../parser_helper"
class RootTestClassDef < MiniTest::Test
# include the magic (setup and parse -> test method translation), see there
include ParserHelper
def test_simplest_class
@input = <<HERE
class Foo
5
end
HERE
@parse_output = {:expression_list=>[{:module_name=>"Foo", :derived_name=>nil, :class_expressions=>[{:integer=>"5"}], :end=>"end"}]}
@output = Ast::ExpressionList.new( [Ast::ClassExpression.new(:Foo ,nil, [Ast::IntegerExpression.new(5)] )])
end
def test_class_ops
@input = <<HERE
class Opers
def foo(x)
@abba = 5
2 + 5
end
end
HERE
@parse_output = {:expression_list=>[{:module_name=>"Opers", :derived_name=>nil, :class_expressions=>[{:function_name=>{:name=>"foo"}, :parameter_list=>[{:parameter=>{:name=>"x"}}], :expressions=>[{:l=>{:instance_variable=>{:name=>"abba"}}, :o=>"= ", :r=>{:integer=>"5"}}, {:l=>{:integer=>"2"}, :o=>"+ ", :r=>{:integer=>"5"}}], :end=>"end"}], :end=>"end"}]}
@output = Ast::ExpressionList.new( [Ast::ClassExpression.new(:Opers ,nil, [Ast::FunctionExpression.new(:foo, [Ast::NameExpression.new(:x)] , [Ast::AssignmentExpression.new(Ast::VariableExpression.new(:abba),Ast::IntegerExpression.new(5)),Ast::OperatorExpression.new("+", Ast::IntegerExpression.new(2),Ast::IntegerExpression.new(5))] ,nil )] )])
end
def test_class_if
@input = <<HERE
class Ifi
def ofthen(n)
if(0)
isit = 42
else
maybenot = 667
end
end
end
HERE
@parse_output = {:expression_list=>[{:module_name=>"Ifi", :derived_name=>nil, :class_expressions=>[{:function_name=>{:name=>"ofthen"}, :parameter_list=>[{:parameter=>{:name=>"n"}}], :expressions=>[{:if=>"if", :conditional=>{:integer=>"0"}, :if_true=>{:expressions=>[{:l=>{:name=>"isit"}, :o=>"= ", :r=>{:integer=>"42"}}], :else=>"else"}, :if_false=>{:expressions=>[{:l=>{:name=>"maybenot"}, :o=>"= ", :r=>{:integer=>"667"}}], :end=>"end"}}], :end=>"end"}], :end=>"end"}]}
@output = Ast::ExpressionList.new( [Ast::ClassExpression.new(:Ifi ,nil, [Ast::FunctionExpression.new(:ofthen, [Ast::NameExpression.new(:n)] , [Ast::IfExpression.new(Ast::IntegerExpression.new(0), [Ast::AssignmentExpression.new(Ast::NameExpression.new(:isit),Ast::IntegerExpression.new(42))],[Ast::AssignmentExpression.new(Ast::NameExpression.new(:maybenot),Ast::IntegerExpression.new(667))] )] ,nil )] )])
end
def test_class_function
@input = <<HERE
class Pifi
ofthen(3+4 , var)
def ofthen(n,m)
44
end
end
HERE
@parse_output = {:expression_list=>[{:module_name=>"Pifi", :derived_name=>nil, :class_expressions=>[{:call_site=>{:name=>"ofthen"}, :argument_list=>[{:argument=>{:l=>{:integer=>"3"}, :o=>"+", :r=>{:integer=>"4"}}}, {:argument=>{:name=>"var"}}]}, {:function_name=>{:name=>"ofthen"}, :parameter_list=>[{:parameter=>{:name=>"n"}}, {:parameter=>{:name=>"m"}}], :expressions=>[{:integer=>"44"}], :end=>"end"}], :end=>"end"}]}
@output = Ast::ExpressionList.new( [Ast::ClassExpression.new(:Pifi ,nil, [Ast::CallSiteExpression.new(:ofthen, [Ast::OperatorExpression.new("+", Ast::IntegerExpression.new(3),Ast::IntegerExpression.new(4)),Ast::NameExpression.new(:var)] ,Ast::NameExpression.new(:self)), Ast::FunctionExpression.new(:ofthen, [Ast::NameExpression.new(:n),Ast::NameExpression.new(:m)] , [Ast::IntegerExpression.new(44)] ,nil )] )])
end
def test_class_module
@input = <<HERE
class Foo
module Boo
funcall(3+4 , var)
end
end
HERE
@parse_output = {:expression_list=>[{:module_name=>"Foo", :derived_name=>nil, :class_expressions=>[{:module_name=>"Boo", :module_expressions=>[{:call_site=>{:name=>"funcall"}, :argument_list=>[{:argument=>{:l=>{:integer=>"3"}, :o=>"+", :r=>{:integer=>"4"}}}, {:argument=>{:name=>"var"}}]}], :end=>"end"}], :end=>"end"}]}
@output = Ast::ExpressionList.new( [Ast::ClassExpression.new(:Foo ,nil, [Ast::ModuleExpression.new(:Boo ,[Ast::CallSiteExpression.new(:funcall, [Ast::OperatorExpression.new("+", Ast::IntegerExpression.new(3),Ast::IntegerExpression.new(4)),Ast::NameExpression.new(:var)] ,Ast::NameExpression.new(:self))] )] )])
end
def test_class_derived
@input = <<HERE
class Foo < Object
ofthen(3+4 , var)
end
HERE
@parse_output = {:expression_list=>[{:module_name=>"Foo", :derived_name=>{:module_name=>"Object"}, :class_expressions=>[{:call_site=>{:name=>"ofthen"}, :argument_list=>[{:argument=>{:l=>{:integer=>"3"}, :o=>"+", :r=>{:integer=>"4"}}}, {:argument=>{:name=>"var"}}]}], :end=>"end"}]}
@output = Ast::ExpressionList.new( [Ast::ClassExpression.new(:Foo ,:Object, [Ast::CallSiteExpression.new(:ofthen, [Ast::OperatorExpression.new("+", Ast::IntegerExpression.new(3),Ast::IntegerExpression.new(4)),Ast::NameExpression.new(:var)] ,Ast::NameExpression.new(:self))] )])
end
def test_class_method
@input = <<HERE
class Foo < Object
def Foo.test()
43
end
end
HERE
@parse_output = {:expression_list=>[{:module_name=>"Foo", :derived_name=>{:module_name=>"Object"}, :class_expressions=>[{:receiver=>{:module_name=>"Foo"}, :function_name=>{:name=>"test"}, :parameter_list=>[], :expressions=>[{:integer=>"43"}], :end=>"end"}], :end=>"end"}]}
@output = Ast::ExpressionList.new( [Ast::ClassExpression.new(:Foo ,:Object, [Ast::FunctionExpression.new(:test, [] , [Ast::IntegerExpression.new(43)] ,Ast::ModuleName.new(:Foo) )] )])
end
end

View File

@ -0,0 +1,43 @@
require_relative "../parser_helper"
class RootTestCompound < MiniTest::Test
# include the magic (setup and parse -> test method translation), see there
include ParserHelper
def test_one_array
@input = '[42]'
@parse_output = {:expression_list=>[{:array_constant=>[{:array_element=>{:integer=>"42"}}]}]}
@output = Ast::ExpressionList.new( [Ast::ArrayExpression.new([Ast::IntegerExpression.new(42)])])
end
def test_array_list
@input = '[42, foo]'
@parse_output = {:expression_list=>[{:array_constant=>[{:array_element=>{:integer=>"42"}}, {:array_element=>{:name=>"foo"}}]}]}
@output = Ast::ExpressionList.new( [Ast::ArrayExpression.new([Ast::IntegerExpression.new(42), Ast::NameExpression.new(:foo)])])
end
def test_array_ops
@input = '[ 3 + 4 , foo(22) ]'
@parse_output = {:expression_list=>[{:array_constant=>[{:array_element=>{:l=>{:integer=>"3"}, :o=>"+ ", :r=>{:integer=>"4"}}}, {:array_element=>{:call_site=>{:name=>"foo"}, :argument_list=>[{:argument=>{:integer=>"22"}}]}}]}]}
@output = Ast::ExpressionList.new( [Ast::ArrayExpression.new([Ast::OperatorExpression.new("+", Ast::IntegerExpression.new(3),Ast::IntegerExpression.new(4)), Ast::CallSiteExpression.new(:foo, [Ast::IntegerExpression.new(22)] ,Ast::NameExpression.new(:self))])])
end
def test_hash
@input = '{ foo => 33 }'
@parse_output = {:expression_list=>[{:hash_constant=>[{:hash_pair=>{:hash_key=>{:name=>"foo"}, :hash_value=>{:integer=>"33"}}}]}]}
@output = Ast::ExpressionList.new( [Ast::HashExpression.new([Ast::AssociationExpression.new(Ast::NameExpression.new(:foo) , Ast::IntegerExpression.new(33))])])
end
def test_hash2
@input = '{ foo => true }'
@parse_output = {:expression_list=>[{:hash_constant=>[{:hash_pair=>{:hash_key=>{:name=>"foo"}, :hash_value=>{:true=>"true"}}}]}]}
@output = Ast::ExpressionList.new( [Ast::HashExpression.new([Ast::AssociationExpression.new(Ast::NameExpression.new(:foo) , Ast::TrueExpression.new())])])
end
def test_hash_list
@input = "{foo => 33 , bar => 42}"
@parse_output = {:expression_list=>[{:hash_constant=>[{:hash_pair=>{:hash_key=>{:name=>"foo"}, :hash_value=>{:integer=>"33"}}}, {:hash_pair=>{:hash_key=>{:name=>"bar"}, :hash_value=>{:integer=>"42"}}}]}]}
@output = Ast::ExpressionList.new( [Ast::HashExpression.new([Ast::AssociationExpression.new(Ast::NameExpression.new(:foo) , Ast::IntegerExpression.new(33)), Ast::AssociationExpression.new(Ast::NameExpression.new(:bar) , Ast::IntegerExpression.new(42))])])
end
end

View File

@ -0,0 +1,70 @@
require_relative "../parser_helper"
class RootTestConditional < MiniTest::Test
# include the magic (setup and parse -> test method translation), see there
include ParserHelper
def test_if_else
@input = <<HERE
if(0)
42
else
667
end
HERE
@parse_output = {:expression_list=>[{:if=>"if", :conditional=>{:integer=>"0"}, :if_true=>{:expressions=>[{:integer=>"42"}], :else=>"else"}, :if_false=>{:expressions=>[{:integer=>"667"}], :end=>"end"}}]}
@output = Ast::ExpressionList.new( [Ast::IfExpression.new(Ast::IntegerExpression.new(0), [Ast::IntegerExpression.new(42)],[Ast::IntegerExpression.new(667)] )])
@root = :
end
def test_if_else_expressions
@input = <<HERE
if(3 > var)
Object.initialize(3)
else
var.new(33)
end
HERE
@input
@parse_output = {:expression_list=>[{: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"}}]}
@output = Ast::ExpressionList.new( [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))] )])
end
def pest_if_end
@input = <<HERE
if(0)
42
end
HERE
@parse_output = {:expression_list=>[{:if=>"if", :conditional=>{:integer=>"0"}, :if_true=>{:expressions=>[{:integer=>"42"}], :else=>"else"}, :if_false=>{:expressions=>[{:integer=>"667"}], :end=>"end"}}]}
@output = Ast::ExpressionList.new( [Ast::IfExpression.new(Ast::IntegerExpression.new(0), [Ast::IntegerExpression.new(42)],[Ast::IntegerExpression.new(667)] )])
@root = :
end
def pest_if_end_expressions
@input = <<HERE
if(3 > var)
Object.initialize(3)
end
HERE
@input
@parse_output = {:expression_list=>[{: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"}}]}
@output = Ast::ExpressionList.new( [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))] )])
end
def pest_if_reverse
@input = "42 if(0)"
@parse_output = {:expression_list=>[{:if=>"if", :conditional=>{:integer=>"0"}, :if_true=>{:expressions=>[{:integer=>"42"}], :else=>"else"}, :if_false=>{:expressions=>[{:integer=>"667"}], :end=>"end"}}]}
@output = Ast::ExpressionList.new( [Ast::IfExpression.new(Ast::IntegerExpression.new(0), [Ast::IntegerExpression.new(42)],[Ast::IntegerExpression.new(667)] )])
@root = :
end
def pest_if_end_expressions
@input = "Object.initialize(3) if(3 > var)"
@input
@parse_output = {:expression_list=>[{: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"}}]}
@output = Ast::ExpressionList.new( [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))] )])
end
end

View File

@ -0,0 +1,164 @@
require_relative "../parser_helper"
class RootTestFunctionDefinition < MiniTest::Test
# include the magic (setup and parse -> test method translation), see there
include ParserHelper
def test_simplest_function
@input = <<HERE
def foo(x)
5
end
HERE
@parse_output = {:expression_list=>[{:function_name=>{:name=>"foo"}, :parameter_list=>[{:parameter=>{:name=>"x"}}], :expressions=>[{:integer=>"5"}], :end=>"end"}]}
@output = Ast::ExpressionList.new( [Ast::FunctionExpression.new(:foo, [Ast::NameExpression.new(:x)] , [Ast::IntegerExpression.new(5)] ,nil )])
@root = :
end
def test_function_no_braces
@input = <<HERE
def foo
5
end
HERE
@parse_output = {:expression_list=>[{:function_name=>{:name=>"foo"}, :expressions=>[{:integer=>"5"}], :end=>"end"}]}
@output = Ast::ExpressionList.new( [Ast::FunctionExpression.new(:foo, [] , [Ast::IntegerExpression.new(5)] ,nil )])
@root = :
end
def pest_function_no_braces_and_args
@input = <<HERE
def foo n , m
n
end
HERE
@parse_output = {:expression_list=>[{:function_name=>{:name=>"foo"}, :expressions=>[{:name=>"n"}], :end=>"end"}]}
@output = Ast::ExpressionList.new( [Ast::FunctionExpression.new(:foo, [] , [Ast::NameExpression.new(:n)] ,nil )])
@root = :
end
def test_class_function
@input = <<HERE
def String.length(x)
@length
end
HERE
@parse_output = {:expression_list=>[{:receiver=>{:module_name=>"String"}, :function_name=>{:name=>"length"}, :parameter_list=>[{:parameter=>{:name=>"x"}}], :expressions=>[{:instance_variable=>{:name=>"length"}}], :end=>"end"}]}
@output = Ast::ExpressionList.new( [Ast::FunctionExpression.new(:length, [Ast::NameExpression.new(:x)] , [Ast::VariableExpression.new(:length)] ,Ast::ModuleName.new(:String) )])
@root = :
end
def pest_function_default_args
@input = <<HERE
def foo(x = true )
abba = 5
2 + 5
end
HERE
@parse_output = {:expression_list=>[{:function_name=>{:name=>"foo"}, :parameter_list=>[{:parameter=>{:name=>"x"}}], :expressions=>[{:l=>{:name=>"abba"}, :o=>"= ", :r=>{:integer=>"5"}}, {:l=>{:integer=>"2"}, :o=>"+ ", :r=>{:integer=>"5"}}], :end=>"end"}]}
@output = Ast::ExpressionList.new( [Ast::FunctionExpression.new(:foo, [Ast::NameExpression.new(:x)] , [Ast::AssignmentExpression.new(Ast::NameExpression.new(:abba),Ast::IntegerExpression.new(5)),Ast::OperatorExpression.new("+", Ast::IntegerExpression.new(2),Ast::IntegerExpression.new(5))] ,nil )])
@root = :
end
def test_function_ops
@input = <<HERE
def foo(x)
abba = 5
2 + 5
end
HERE
@parse_output = {:expression_list=>[{:function_name=>{:name=>"foo"}, :parameter_list=>[{:parameter=>{:name=>"x"}}], :expressions=>[{:l=>{:name=>"abba"}, :o=>"= ", :r=>{:integer=>"5"}}, {:l=>{:integer=>"2"}, :o=>"+ ", :r=>{:integer=>"5"}}], :end=>"end"}]}
@output = Ast::ExpressionList.new( [Ast::FunctionExpression.new(:foo, [Ast::NameExpression.new(:x)] , [Ast::AssignmentExpression.new(Ast::NameExpression.new(:abba),Ast::IntegerExpression.new(5)),Ast::OperatorExpression.new("+", Ast::IntegerExpression.new(2),Ast::IntegerExpression.new(5))] ,nil )])
@root = :
end
def test_function_if
@input = <<HERE
def ofthen(n)
if(0)
isit = 42
else
maybenot = 667
end
end
HERE
@parse_output = {:expression_list=>[{:function_name=>{:name=>"ofthen"}, :parameter_list=>[{:parameter=>{:name=>"n"}}], :expressions=>[{:if=>"if", :conditional=>{:integer=>"0"}, :if_true=>{:expressions=>[{:l=>{:name=>"isit"}, :o=>"= ", :r=>{:integer=>"42"}}], :else=>"else"}, :if_false=>{:expressions=>[{:l=>{:name=>"maybenot"}, :o=>"= ", :r=>{:integer=>"667"}}], :end=>"end"}}], :end=>"end"}]}
@output = Ast::ExpressionList.new( [Ast::FunctionExpression.new(:ofthen, [Ast::NameExpression.new(:n)] , [Ast::IfExpression.new(Ast::IntegerExpression.new(0), [Ast::AssignmentExpression.new(Ast::NameExpression.new(:isit),Ast::IntegerExpression.new(42))],[Ast::AssignmentExpression.new(Ast::NameExpression.new(:maybenot),Ast::IntegerExpression.new(667))] )] ,nil )])
@root = :
end
def test_function_return
@input = <<HERE
def retvar(n)
i = 5
return i
end
HERE
@parse_output = {:expression_list=>[{:function_name=>{:name=>"retvar"}, :parameter_list=>[{:parameter=>{:name=>"n"}}], :expressions=>[{:l=>{:name=>"i"}, :o=>"= ", :r=>{:integer=>"5"}}, {:return=>"return", :return_expression=>{:name=>"i"}}], :end=>"end"}]}
@output = Ast::ExpressionList.new( [Ast::FunctionExpression.new(:retvar, [Ast::NameExpression.new(:n)] , [Ast::AssignmentExpression.new(Ast::NameExpression.new(:i),Ast::IntegerExpression.new(5)),Ast::ReturnExpression.new(Ast::NameExpression.new(:i) )] ,nil )])
@root = :
end
def test_function_return_if
@input = <<HERE
def retvar(n)
if( n > 5)
return 10
else
return 20
end
end
HERE
@parse_output = {:expression_list=>[{:function_name=>{:name=>"retvar"}, :parameter_list=>[{:parameter=>{:name=>"n"}}], :expressions=>[{:if=>"if", :conditional=>{:l=>{:name=>"n"}, :o=>"> ", :r=>{:integer=>"5"}}, :if_true=>{:expressions=>[{:return=>"return", :return_expression=>{:integer=>"10"}}], :else=>"else"}, :if_false=>{:expressions=>[{:return=>"return", :return_expression=>{:integer=>"20"}}], :end=>"end"}}], :end=>"end"}]}
@output = Ast::ExpressionList.new( [Ast::FunctionExpression.new(:retvar, [Ast::NameExpression.new(:n)] , [Ast::IfExpression.new(Ast::OperatorExpression.new(">", Ast::NameExpression.new(:n),Ast::IntegerExpression.new(5)), [Ast::ReturnExpression.new(Ast::IntegerExpression.new(10) )],[Ast::ReturnExpression.new(Ast::IntegerExpression.new(20) )] )] ,nil )])
@root = :
end
def test_function_return_while
@input = <<HERE
def retvar(n)
while( n > 5) do
n = n + 1
return n
end
end
HERE
@parse_output = {:expression_list=>[{:function_name=>{:name=>"retvar"}, :parameter_list=>[{:parameter=>{:name=>"n"}}], :expressions=>[{:while=>"while", :while_cond=>{:l=>{:name=>"n"}, :o=>"> ", :r=>{:integer=>"5"}}, :do=>"do", :body=>{:expressions=>[{:l=>{:name=>"n"}, :o=>"= ", :r=>{:l=>{:name=>"n"}, :o=>"+ ", :r=>{:integer=>"1"}}}, {:return=>"return", :return_expression=>{:name=>"n"}}], :end=>"end"}}], :end=>"end"}]}
@output = Ast::ExpressionList.new( [Ast::FunctionExpression.new(:retvar, [Ast::NameExpression.new(:n)] , [Ast::WhileExpression.new(Ast::OperatorExpression.new(">", Ast::NameExpression.new(:n),Ast::IntegerExpression.new(5)), [Ast::AssignmentExpression.new(Ast::NameExpression.new(:n),Ast::OperatorExpression.new("+", Ast::NameExpression.new(:n),Ast::IntegerExpression.new(1))), Ast::ReturnExpression.new(Ast::NameExpression.new(:n) )] )] ,nil )])
@root = :
end
def test_function_while
@input = <<HERE
def fibonaccit(n)
a = 0
while (n) do
some = 43
other = some * 4
end
end
HERE
@parse_output = {:expression_list=>[{:function_name=>{:name=>"fibonaccit"}, :parameter_list=>[{:parameter=>{:name=>"n"}}], :expressions=>[{:l=>{:name=>"a"}, :o=>"= ", :r=>{:integer=>"0"}}, {:while=>"while", :while_cond=>{:name=>"n"}, :do=>"do", :body=>{:expressions=>[{:l=>{:name=>"some"}, :o=>"= ", :r=>{:integer=>"43"}}, {:l=>{:name=>"other"}, :o=>"= ", :r=>{:l=>{:name=>"some"}, :o=>"* ", :r=>{:integer=>"4"}}}], :end=>"end"}}], :end=>"end"}]}
@output = Ast::ExpressionList.new( [Ast::FunctionExpression.new(:fibonaccit, [Ast::NameExpression.new(:n)] , [Ast::AssignmentExpression.new(Ast::NameExpression.new(:a),Ast::IntegerExpression.new(0)),Ast::WhileExpression.new(Ast::NameExpression.new(:n), [Ast::AssignmentExpression.new(Ast::NameExpression.new(:some),Ast::IntegerExpression.new(43)), Ast::AssignmentExpression.new(Ast::NameExpression.new(:other),Ast::OperatorExpression.new("*", Ast::NameExpression.new(:some),Ast::IntegerExpression.new(4)))] )] ,nil )])
@root = :
end
def test_function_big_while
@input = <<HERE
def fibonaccit(n)
a = 0
b = 1
while( n > 1 ) do
tmp = a
a = b
b = tmp + b
puts(b)
n = n - 1
end
end
HERE
@parse_output = {:expression_list=>[{:function_name=>{:name=>"fibonaccit"}, :parameter_list=>[{:parameter=>{:name=>"n"}}], :expressions=>[{:l=>{:name=>"a"}, :o=>"= ", :r=>{:integer=>"0"}}, {:l=>{:name=>"b"}, :o=>"= ", :r=>{:integer=>"1"}}, {:while=>"while", :while_cond=>{:l=>{:name=>"n"}, :o=>"> ", :r=>{:integer=>"1"}}, :do=>"do", :body=>{:expressions=>[{:l=>{:name=>"tmp"}, :o=>"= ", :r=>{:name=>"a"}}, {:l=>{:name=>"a"}, :o=>"= ", :r=>{:name=>"b"}}, {:l=>{:name=>"b"}, :o=>"= ", :r=>{:l=>{:name=>"tmp"}, :o=>"+ ", :r=>{:name=>"b"}}}, {:call_site=>{:name=>"puts"}, :argument_list=>[{:argument=>{:name=>"b"}}]}, {:l=>{:name=>"n"}, :o=>"= ", :r=>{:l=>{:name=>"n"}, :o=>"- ", :r=>{:integer=>"1"}}}], :end=>"end"}}], :end=>"end"}]}
@output = Ast::ExpressionList.new( [Ast::FunctionExpression.new(:fibonaccit, [Ast::NameExpression.new(:n)] , [Ast::AssignmentExpression.new(Ast::NameExpression.new(:a),Ast::IntegerExpression.new(0)),Ast::AssignmentExpression.new(Ast::NameExpression.new(:b),Ast::IntegerExpression.new(1)),Ast::WhileExpression.new(Ast::OperatorExpression.new(">", Ast::NameExpression.new(:n),Ast::IntegerExpression.new(1)), [Ast::AssignmentExpression.new(Ast::NameExpression.new(:tmp),Ast::NameExpression.new(:a)), Ast::AssignmentExpression.new(Ast::NameExpression.new(:a),Ast::NameExpression.new(:b)), Ast::AssignmentExpression.new(Ast::NameExpression.new(:b),Ast::OperatorExpression.new("+", Ast::NameExpression.new(:tmp),Ast::NameExpression.new(:b))), Ast::CallSiteExpression.new(:puts, [Ast::NameExpression.new(:b)] ,Ast::NameExpression.new(:self)), Ast::AssignmentExpression.new(Ast::NameExpression.new(:n),Ast::OperatorExpression.new("-", Ast::NameExpression.new(:n),Ast::IntegerExpression.new(1)))] )] ,nil )])
@root = :
end
end

92
test/roots/test_module.rb Normal file
View File

@ -0,0 +1,92 @@
require_relative "../parser_helper"
class RootTestModuleDef < MiniTest::Test
# include the magic (setup and parse -> test method translation), see there
include ParserHelper
def test_simplest_module
@input = <<HERE
module Simple
5
end
HERE
@parse_output = {:expression_list=>[{:module_name=>"Simple", :module_expressions=>[{:integer=>"5"}], :end=>"end"}]}
@output = Ast::ExpressionList.new( [Ast::ModuleExpression.new(:Simple ,[Ast::IntegerExpression.new(5)] )])
end
def test_module_ops
@input = <<HERE
module Opers
def foo(x)
abba = 5
2 + 5
end
end
HERE
@parse_output = {:expression_list=>[{:module_name=>"Opers", :module_expressions=>[{:function_name=>{:name=>"foo"}, :parameter_list=>[{:parameter=>{:name=>"x"}}], :expressions=>[{:l=>{:name=>"abba"}, :o=>"= ", :r=>{:integer=>"5"}}, {:l=>{:integer=>"2"}, :o=>"+ ", :r=>{:integer=>"5"}}], :end=>"end"}], :end=>"end"}]}
@output = Ast::ExpressionList.new( [Ast::ModuleExpression.new(:Opers ,[Ast::FunctionExpression.new(:foo, [Ast::NameExpression.new(:x)] , [Ast::AssignmentExpression.new(Ast::NameExpression.new(:abba),Ast::IntegerExpression.new(5)),Ast::OperatorExpression.new("+", Ast::IntegerExpression.new(2),Ast::IntegerExpression.new(5))] ,nil )] )])
end
def test_module_assign_instance
@input = <<HERE
module Opers
@abba = 5
end
HERE
@parse_output = {:expression_list=>[{:module_name=>"Opers", :module_expressions=>[{:l=>{:instance_variable=>{:name=>"abba"}}, :o=>"= ", :r=>{:integer=>"5"}}], :end=>"end"}]}
@output = Ast::ExpressionList.new( [Ast::ModuleExpression.new(:Opers ,[Ast::AssignmentExpression.new(Ast::VariableExpression.new(:abba),Ast::IntegerExpression.new(5))] )])
end
def test_module_if
@input = <<HERE
module Foo
def ofthen(n)
if(0)
isit = 42
else
maybenot = 667
end
end
end
HERE
@parse_output = {:expression_list=>[{:module_name=>"Foo", :module_expressions=>[{:function_name=>{:name=>"ofthen"}, :parameter_list=>[{:parameter=>{:name=>"n"}}], :expressions=>[{:if=>"if", :conditional=>{:integer=>"0"}, :if_true=>{:expressions=>[{:l=>{:name=>"isit"}, :o=>"= ", :r=>{:integer=>"42"}}], :else=>"else"}, :if_false=>{:expressions=>[{:l=>{:name=>"maybenot"}, :o=>"= ", :r=>{:integer=>"667"}}], :end=>"end"}}], :end=>"end"}], :end=>"end"}]}
@output = Ast::ExpressionList.new( [Ast::ModuleExpression.new(:Foo ,[Ast::FunctionExpression.new(:ofthen, [Ast::NameExpression.new(:n)] , [Ast::IfExpression.new(Ast::IntegerExpression.new(0), [Ast::AssignmentExpression.new(Ast::NameExpression.new(:isit),Ast::IntegerExpression.new(42))],[Ast::AssignmentExpression.new(Ast::NameExpression.new(:maybenot),Ast::IntegerExpression.new(667))] )] ,nil )] )])
end
def test_module_function
@input = <<HERE
module Soho
ofthen(3+4 , var)
def ofthen(n,m)
44
end
end
HERE
@parse_output = {:expression_list=>[{:module_name=>"Soho", :module_expressions=>[{:call_site=>{:name=>"ofthen"}, :argument_list=>[{:argument=>{:l=>{:integer=>"3"}, :o=>"+", :r=>{:integer=>"4"}}}, {:argument=>{:name=>"var"}}]}, {:function_name=>{:name=>"ofthen"}, :parameter_list=>[{:parameter=>{:name=>"n"}}, {:parameter=>{:name=>"m"}}], :expressions=>[{:integer=>"44"}], :end=>"end"}], :end=>"end"}]}
@output = Ast::ExpressionList.new( [Ast::ModuleExpression.new(:Soho ,[Ast::CallSiteExpression.new(:ofthen, [Ast::OperatorExpression.new("+", Ast::IntegerExpression.new(3),Ast::IntegerExpression.new(4)),Ast::NameExpression.new(:var)] ,Ast::NameExpression.new(:self)), Ast::FunctionExpression.new(:ofthen, [Ast::NameExpression.new(:n),Ast::NameExpression.new(:m)] , [Ast::IntegerExpression.new(44)] ,nil )] )])
end
def test_function_without_braces
@input = <<HERE
class Foo
def bar
4
end
end
HERE
@parse_output = {:expression_list=>[{:module_name=>"Foo", :derived_name=>nil, :class_expressions=>[{:function_name=>{:name=>"bar"}, :expressions=>[{:integer=>"4"}], :end=>"end"}], :end=>"end"}]}
@output = Ast::ExpressionList.new( [Ast::ClassExpression.new(:Foo ,nil, [Ast::FunctionExpression.new(:bar, [] , [Ast::IntegerExpression.new(4)] ,nil )] )])
end
def test_module_class
@input = <<HERE
module Foo
class Bar
funcall(3+4 , var)
end
end
HERE
@parse_output = {:expression_list=>[{:module_name=>"Foo", :module_expressions=>[{:module_name=>"Bar", :derived_name=>nil, :class_expressions=>[{:call_site=>{:name=>"funcall"}, :argument_list=>[{:argument=>{:l=>{:integer=>"3"}, :o=>"+", :r=>{:integer=>"4"}}}, {:argument=>{:name=>"var"}}]}], :end=>"end"}], :end=>"end"}]}
@output = Ast::ExpressionList.new( [Ast::ModuleExpression.new(:Foo ,[Ast::ClassExpression.new(:Bar ,nil, [Ast::CallSiteExpression.new(:funcall, [Ast::OperatorExpression.new("+", Ast::IntegerExpression.new(3),Ast::IntegerExpression.new(4)),Ast::NameExpression.new(:var)] ,Ast::NameExpression.new(:self))] )] )])
end
end

View File

@ -0,0 +1,81 @@
require_relative "../parser_helper"
class RootTestExpressions < MiniTest::Test
# include the magic (setup and parse -> test method translation), see there
include ParserHelper
def simple_op op
@input = "5 #{op} 3"
@parse_output = {:expression_list=>[{:l=>{:integer=>"5"}, :o=>"#{op} ", :r=>{:integer=>"3"}}]}
@output = Ast::ExpressionList.new( [Ast::OperatorExpression.new(op, Ast::IntegerExpression.new(5),Ast::IntegerExpression.new(3))])
end
def test_simple_multiply
simple_op "*"
end
def test_simple_devide
simple_op "/"
end
def test_simple_plus
simple_op "+"
end
def test_simple_minus
simple_op "-"
end
def test_simple_greater
simple_op ">"
end
def test_simple_smaller
simple_op "<"
end
def test_op_variable
@input = "a + 35"
@parse_output = {:expression_list=>[{:l=>{:name=>"a"}, :o=>"+ ", :r=>{:integer=>"35"}}]}
@output = Ast::ExpressionList.new( [Ast::OperatorExpression.new("+", Ast::NameExpression.new(:a),Ast::IntegerExpression.new(35))])
end
def test_op_two_variable
@input = "a - b"
@parse_output = {:expression_list=>[{:l=>{:name=>"a"}, :o=>"- ", :r=>{:name=>"b"}}]}
@output = Ast::ExpressionList.new( [Ast::OperatorExpression.new("-", Ast::NameExpression.new(:a),Ast::NameExpression.new(:b))])
end
def test_op_instance_variable
@input = "@a - 5"
@parse_output = {:expression_list=>[{:l=>{:instance_variable=>{:name=>"a"}}, :o=>"- ", :r=>{:integer=>"5"}}]}
@output = Ast::ExpressionList.new( [Ast::OperatorExpression.new("-", Ast::VariableExpression.new(:a),Ast::IntegerExpression.new(5))])
end
def test_op_variable_string
@input = 'a - "st"'
@parse_output = {:expression_list=>[{:l=>{:name=>"a"}, :o=>"- ", :r=>{:string=>[{:char=>"s"}, {:char=>"t"}]}}]}
@output = Ast::ExpressionList.new( [Ast::OperatorExpression.new("-", Ast::NameExpression.new(:a),Ast::StringExpression.new("st"))])
end
def test_op_variable_true
@input = 'a == true'
@parse_output = {:expression_list=>[{:l=>{:name=>"a"}, :o=>"== ", :r=>{:true=>"true"}}]}
@output = Ast::ExpressionList.new( [Ast::OperatorExpression.new("==", Ast::NameExpression.new(:a),Ast::TrueExpression.new())])
end
def test_two_same_ops
@input = '2 + 3 + 4'
@parse_output = {:expression_list=>[{:l=>{:l=>{:integer=>"2"}, :o=>"+ ", :r=>{:integer=>"3"}}, :o=>"+ ", :r=>{:integer=>"4"}}]}
@output = Ast::ExpressionList.new( [Ast::OperatorExpression.new("+", Ast::OperatorExpression.new("+", Ast::IntegerExpression.new(2),Ast::IntegerExpression.new(3)),Ast::IntegerExpression.new(4))])
end
def test_two_different_ops
@input = '2 + 3 * 4'
@parse_output = {:expression_list=>[{:l=>{:integer=>"2"}, :o=>"+ ", :r=>{:l=>{:integer=>"3"}, :o=>"* ", :r=>{:integer=>"4"}}}]}
@output = Ast::ExpressionList.new( [Ast::OperatorExpression.new("+", Ast::IntegerExpression.new(2),Ast::OperatorExpression.new("*", Ast::IntegerExpression.new(3),Ast::IntegerExpression.new(4)))])
end
def test_two_different_ops_order
@input = '2 * 3 + 4'
@parse_output = {:expression_list=>[{:l=>{:l=>{:integer=>"2"}, :o=>"* ", :r=>{:integer=>"3"}}, :o=>"+ ", :r=>{:integer=>"4"}}]}
@output = Ast::ExpressionList.new( [Ast::OperatorExpression.new("+", Ast::OperatorExpression.new("*", Ast::IntegerExpression.new(2),Ast::IntegerExpression.new(3)),Ast::IntegerExpression.new(4))])
end
def test_assignment
@input = "a = 5"
@parse_output = {:expression_list=>[{:l=>{:name=>"a"}, :o=>"= ", :r=>{:integer=>"5"}}]}
@output = Ast::ExpressionList.new( [Ast::AssignmentExpression.new(Ast::NameExpression.new(:a),Ast::IntegerExpression.new(5))])
end
def test_assignment_instance
@input = "@a = 5"
@parse_output = {:expression_list=>[{:l=>{:instance_variable=>{:name=>"a"}}, :o=>"= ", :r=>{:integer=>"5"}}]}
@output = Ast::ExpressionList.new( [Ast::AssignmentExpression.new(Ast::VariableExpression.new(:a),Ast::IntegerExpression.new(5))])
end
end

25
test/roots/test_return.rb Normal file
View File

@ -0,0 +1,25 @@
require_relative "../parser_helper"
class RootTestReturn < MiniTest::Test
# include the magic (setup and parse -> test method translation), see there
include ParserHelper
def test_return_int
@input = 'return 42'
@parse_output = {:expression_list=>[{:name=>"return"}, {:integer=>"42"}]}
@output = Ast::ExpressionList.new( [Ast::NameExpression.new(:return),Ast::IntegerExpression.new(42)])
end
def test_return_variable
@input = 'return foo'
@parse_output = {:expression_list=>[{:name=>"return"}, {:name=>"foo"}]}
@output = Ast::ExpressionList.new( [Ast::NameExpression.new(:return),Ast::NameExpression.new(:foo)])
end
def test_return_string
@input = 'return "hello"'
@parse_output = {:expression_list=>[{:name=>"return"}, {:string=>[{:char=>"h"}, {:char=>"e"}, {:char=>"l"}, {:char=>"l"}, {:char=>"o"}]}]}
@output = Ast::ExpressionList.new( [Ast::NameExpression.new(:return),Ast::StringExpression.new("hello")])
end
end

111
test/roots/test_root.rb Normal file
View File

@ -0,0 +1,111 @@
require_relative "../parser_helper"
class RootTestRoot < MiniTest::Test
# include the magic (setup and parse -> test method translation), see there
include ParserHelper
def test_double_root
@input = <<HERE
def foo(x)
a = 5
end
foo( 3 )
HERE
@parse_output = {:expression_list=>[{:function_name=>{:name=>"foo"}, :parameter_list=>[{:parameter=>{:name=>"x"}}], :expressions=>[{:l=>{:name=>"a"}, :o=>"= ", :r=>{:integer=>"5"}}], :end=>"end"}, {:call_site=>{:name=>"foo"}, :argument_list=>[{:argument=>{:integer=>"3"}}]}]}
@output = Ast::ExpressionList.new( [Ast::FunctionExpression.new(:foo, [Ast::NameExpression.new(:x)] , [Ast::AssignmentExpression.new(Ast::NameExpression.new(:a),Ast::IntegerExpression.new(5))] ,nil ),Ast::CallSiteExpression.new(:foo, [Ast::IntegerExpression.new(3)] ,Ast::NameExpression.new(:self))])
end
def test_comments
@input = <<HERE
def foo(x) #here
a = 0 # a == r1
b = 1 # b = r2
while(n<1) do #comment
tmp = a # r3 <- r1
a = b # r1 <- r2
b = tmp + b # r4 = r2 + r3 (r4 transient) r2 <- r4
putstring(b)
n = n - 1 #me
end #no
end #anywhere
foo( 3 ) #and more
HERE
@parse_output = {:expression_list=>[{:function_name=>{:name=>"foo"}, :parameter_list=>[{:parameter=>{:name=>"x"}}], :expressions=>[{:l=>{:name=>"a"}, :o=>"= ", :r=>{:integer=>"0"}}, {:l=>{:name=>"b"}, :o=>"= ", :r=>{:integer=>"1"}}, {:while=>"while", :while_cond=>{:l=>{:name=>"n"}, :o=>"<", :r=>{:integer=>"1"}}, :do=>"do", :body=>{:expressions=>[{:l=>{:name=>"tmp"}, :o=>"= ", :r=>{:name=>"a"}}, {:l=>{:name=>"a"}, :o=>"= ", :r=>{:name=>"b"}}, {:l=>{:name=>"b"}, :o=>"= ", :r=>{:l=>{:name=>"tmp"}, :o=>"+ ", :r=>{:name=>"b"}}}, {:call_site=>{:name=>"putstring"}, :argument_list=>[{:argument=>{:name=>"b"}}]}, {:l=>{:name=>"n"}, :o=>"= ", :r=>{:l=>{:name=>"n"}, :o=>"- ", :r=>{:integer=>"1"}}}], :end=>"end"}}], :end=>"end"}, {:call_site=>{:name=>"foo"}, :argument_list=>[{:argument=>{:integer=>"3"}}]}]}
@output = Ast::ExpressionList.new( [Ast::FunctionExpression.new(:foo, [Ast::NameExpression.new(:x)] , [Ast::AssignmentExpression.new(Ast::NameExpression.new(:a),Ast::IntegerExpression.new(0)),Ast::AssignmentExpression.new(Ast::NameExpression.new(:b),Ast::IntegerExpression.new(1)),Ast::WhileExpression.new(Ast::OperatorExpression.new("<", Ast::NameExpression.new(:n),Ast::IntegerExpression.new(1)), [Ast::AssignmentExpression.new(Ast::NameExpression.new(:tmp),Ast::NameExpression.new(:a)), Ast::AssignmentExpression.new(Ast::NameExpression.new(:a),Ast::NameExpression.new(:b)), Ast::AssignmentExpression.new(Ast::NameExpression.new(:b),Ast::OperatorExpression.new("+", Ast::NameExpression.new(:tmp),Ast::NameExpression.new(:b))), Ast::CallSiteExpression.new(:putstring, [Ast::NameExpression.new(:b)] ,Ast::NameExpression.new(:self)), Ast::AssignmentExpression.new(Ast::NameExpression.new(:n),Ast::OperatorExpression.new("-", Ast::NameExpression.new(:n),Ast::IntegerExpression.new(1)))] )] ,nil ),Ast::CallSiteExpression.new(:foo, [Ast::IntegerExpression.new(3)] ,Ast::NameExpression.new(:self))])
end
def test_fibo1
@input = <<HERE
def fibonaccit(n)
a = 0
b = 1
while( n > 1 ) do
tmp = a
a = b
b = tmp + b
puts(b)
n = n - 1
end
end
fibonaccit( 10 )
HERE
@parse_output = {:expression_list=>[{:function_name=>{:name=>"fibonaccit"}, :parameter_list=>[{:parameter=>{:name=>"n"}}], :expressions=>[{:l=>{:name=>"a"}, :o=>"= ", :r=>{:integer=>"0"}}, {:l=>{:name=>"b"}, :o=>"= ", :r=>{:integer=>"1"}}, {:while=>"while", :while_cond=>{:l=>{:name=>"n"}, :o=>"> ", :r=>{:integer=>"1"}}, :do=>"do", :body=>{:expressions=>[{:l=>{:name=>"tmp"}, :o=>"= ", :r=>{:name=>"a"}}, {:l=>{:name=>"a"}, :o=>"= ", :r=>{:name=>"b"}}, {:l=>{:name=>"b"}, :o=>"= ", :r=>{:l=>{:name=>"tmp"}, :o=>"+ ", :r=>{:name=>"b"}}}, {:call_site=>{:name=>"puts"}, :argument_list=>[{:argument=>{:name=>"b"}}]}, {:l=>{:name=>"n"}, :o=>"= ", :r=>{:l=>{:name=>"n"}, :o=>"- ", :r=>{:integer=>"1"}}}], :end=>"end"}}], :end=>"end"}, {:call_site=>{:name=>"fibonaccit"}, :argument_list=>[{:argument=>{:integer=>"10"}}]}]}
@output = Ast::ExpressionList.new( [Ast::FunctionExpression.new(:fibonaccit, [Ast::NameExpression.new(:n)] , [Ast::AssignmentExpression.new(Ast::NameExpression.new(:a),Ast::IntegerExpression.new(0)),Ast::AssignmentExpression.new(Ast::NameExpression.new(:b),Ast::IntegerExpression.new(1)),Ast::WhileExpression.new(Ast::OperatorExpression.new(">", Ast::NameExpression.new(:n),Ast::IntegerExpression.new(1)), [Ast::AssignmentExpression.new(Ast::NameExpression.new(:tmp),Ast::NameExpression.new(:a)), Ast::AssignmentExpression.new(Ast::NameExpression.new(:a),Ast::NameExpression.new(:b)), Ast::AssignmentExpression.new(Ast::NameExpression.new(:b),Ast::OperatorExpression.new("+", Ast::NameExpression.new(:tmp),Ast::NameExpression.new(:b))), Ast::CallSiteExpression.new(:puts, [Ast::NameExpression.new(:b)] ,Ast::NameExpression.new(:self)), Ast::AssignmentExpression.new(Ast::NameExpression.new(:n),Ast::OperatorExpression.new("-", Ast::NameExpression.new(:n),Ast::IntegerExpression.new(1)))] )] ,nil ),Ast::CallSiteExpression.new(:fibonaccit, [Ast::IntegerExpression.new(10)] ,Ast::NameExpression.new(:self))])
end
def test_module_method
@input = <<HERE
module Fibo
def fibonaccit(n)
a = 0
end
fibonaccit( 10 )
end
HERE
@parse_output = {:expression_list=>[{:module_name=>"Fibo", :module_expressions=>[{:function_name=>{:name=>"fibonaccit"}, :parameter_list=>[{:parameter=>{:name=>"n"}}], :expressions=>[{:l=>{:name=>"a"}, :o=>"= ", :r=>{:integer=>"0"}}], :end=>"end"}, {:call_site=>{:name=>"fibonaccit"}, :argument_list=>[{:argument=>{:integer=>"10"}}]}], :end=>"end"}]}
@output = Ast::ExpressionList.new( [Ast::ModuleExpression.new(:Fibo ,[Ast::FunctionExpression.new(:fibonaccit, [Ast::NameExpression.new(:n)] , [Ast::AssignmentExpression.new(Ast::NameExpression.new(:a),Ast::IntegerExpression.new(0))] ,nil ), Ast::CallSiteExpression.new(:fibonaccit, [Ast::IntegerExpression.new(10)] ,Ast::NameExpression.new(:self))] )])
end
def test_module_assignment
@input = <<HERE
module Fibo
a = 5 + foo
bar( b , a , r)
end
HERE
@parse_output = {:expression_list=>[{:module_name=>"Fibo", :module_expressions=>[{:l=>{:name=>"a"}, :o=>"= ", :r=>{:l=>{:integer=>"5"}, :o=>"+ ", :r=>{:name=>"foo"}}}, {:call_site=>{:name=>"bar"}, :argument_list=>[{:argument=>{:name=>"b"}}, {:argument=>{:name=>"a"}}, {:argument=>{:name=>"r"}}]}], :end=>"end"}]}
@output = Ast::ExpressionList.new( [Ast::ModuleExpression.new(:Fibo ,[Ast::AssignmentExpression.new(Ast::NameExpression.new(:a),Ast::OperatorExpression.new("+", Ast::IntegerExpression.new(5),Ast::NameExpression.new(:foo))), Ast::CallSiteExpression.new(:bar, [Ast::NameExpression.new(:b),Ast::NameExpression.new(:a),Ast::NameExpression.new(:r)] ,Ast::NameExpression.new(:self))] )])
end
def test_root_module_class
@input = <<HERE
module FooBo
class Bar
a = 5 + foo
end
end
HERE
@parse_output = {:expression_list=>[{:module_name=>"FooBo", :module_expressions=>[{:module_name=>"Bar", :derived_name=>nil, :class_expressions=>[{:l=>{:name=>"a"}, :o=>"= ", :r=>{:l=>{:integer=>"5"}, :o=>"+ ", :r=>{:name=>"foo"}}}], :end=>"end"}], :end=>"end"}]}
@output = Ast::ExpressionList.new( [Ast::ModuleExpression.new(:FooBo ,[Ast::ClassExpression.new(:Bar ,nil, [Ast::AssignmentExpression.new(Ast::NameExpression.new(:a),Ast::OperatorExpression.new("+", Ast::IntegerExpression.new(5),Ast::NameExpression.new(:foo)))] )] )])
end
def test_class_method
@input = <<HERE
class FooBo
Bar.call(35)
end
HERE
@parse_output = {:expression_list=>[{:module_name=>"FooBo", :derived_name=>nil, :class_expressions=>[{:receiver=>{:module_name=>"Bar"}, :call_site=>{:name=>"call"}, :argument_list=>[{:argument=>{:integer=>"35"}}]}], :end=>"end"}]}
@output = Ast::ExpressionList.new( [Ast::ClassExpression.new(:FooBo ,nil, [Ast::CallSiteExpression.new(:call, [Ast::IntegerExpression.new(35)] ,Ast::ModuleName.new(:Bar))] )])
end
end

52
test/roots/test_while.rb Normal file
View File

@ -0,0 +1,52 @@
require_relative "../parser_helper"
class RootTestWhile < MiniTest::Test
# include the magic (setup and parse -> test method translation), see there
include ParserHelper
def test_while
@input = <<HERE
while(1) do
tmp = a
puts(b)
end
HERE
@parse_output = {:expression_list=>[{:while=>"while", :while_cond=>{:integer=>"1"}, :do=>"do", :body=>{:expressions=>[{:l=>{:name=>"tmp"}, :o=>"= ", :r=>{:name=>"a"}}, {:call_site=>{:name=>"puts"}, :argument_list=>[{:argument=>{:name=>"b"}}]}], :end=>"end"}}]}
@output = Ast::ExpressionList.new( [Ast::WhileExpression.new(Ast::IntegerExpression.new(1), [Ast::AssignmentExpression.new(Ast::NameExpression.new(:tmp),Ast::NameExpression.new(:a)), Ast::CallSiteExpression.new(:puts, [Ast::NameExpression.new(:b)] ,Ast::NameExpression.new(:self))] )])
@root = :
end
def pest_while_reverse
@input = "puts '1' while true "
@parse_output = {:expression_list=>[{:while=>"while", :while_cond=>{:integer=>"1"}, :do=>"do", :body=>{:expressions=>[{:l=>{:name=>"tmp"}, :o=>"= ", :r=>{:name=>"a"}}, {:call_site=>{:name=>"puts"}, :argument_list=>[{:argument=>{:name=>"b"}}]}], :end=>"end"}}]}
@output = Ast::ExpressionList.new( [Ast::WhileExpression.new(Ast::IntegerExpression.new(1), [Ast::AssignmentExpression.new(Ast::NameExpression.new(:tmp),Ast::NameExpression.new(:a)), Ast::CallSiteExpression.new(:puts, [Ast::NameExpression.new(:b)] ,Ast::NameExpression.new(:self))] )])
@root = :
end
def test_while_method
@input = <<HERE
while(1) do
tmp = String.new()
tmp.puts(i)
end
HERE
@parse_output = {:expression_list=>[{:while=>"while", :while_cond=>{:integer=>"1"}, :do=>"do", :body=>{:expressions=>[{:l=>{:name=>"tmp"}, :o=>"= ", :r=>{:receiver=>{:module_name=>"String"}, :call_site=>{:name=>"new"}, :argument_list=>[]}}, {:receiver=>{:name=>"tmp"}, :call_site=>{:name=>"puts"}, :argument_list=>[{:argument=>{:name=>"i"}}]}], :end=>"end"}}]}
@output = Ast::ExpressionList.new( [Ast::WhileExpression.new(Ast::IntegerExpression.new(1), [Ast::AssignmentExpression.new(Ast::NameExpression.new(:tmp),Ast::CallSiteExpression.new(:new, [] ,Ast::ModuleName.new(:String))), Ast::CallSiteExpression.new(:puts, [Ast::NameExpression.new(:i)] ,Ast::NameExpression.new(:tmp))] )])
@root = :
end
def test_big_while
@input = <<HERE
while( n > 1) do
tmp = a
a = b
b = tmp + b
puts(b)
n = n - 1
end
HERE
@parse_output = {:expression_list=>[{:while=>"while", :while_cond=>{:l=>{:name=>"n"}, :o=>"> ", :r=>{:integer=>"1"}}, :do=>"do", :body=>{:expressions=>[{:l=>{:name=>"tmp"}, :o=>"= ", :r=>{:name=>"a"}}, {:l=>{:name=>"a"}, :o=>"= ", :r=>{:name=>"b"}}, {:l=>{:name=>"b"}, :o=>"= ", :r=>{:l=>{:name=>"tmp"}, :o=>"+ ", :r=>{:name=>"b"}}}, {:call_site=>{:name=>"puts"}, :argument_list=>[{:argument=>{:name=>"b"}}]}, {:l=>{:name=>"n"}, :o=>"= ", :r=>{:l=>{:name=>"n"}, :o=>"- ", :r=>{:integer=>"1"}}}], :end=>"end"}}]}
@output = Ast::ExpressionList.new( [Ast::WhileExpression.new(Ast::OperatorExpression.new(">", Ast::NameExpression.new(:n),Ast::IntegerExpression.new(1)), [Ast::AssignmentExpression.new(Ast::NameExpression.new(:tmp),Ast::NameExpression.new(:a)), Ast::AssignmentExpression.new(Ast::NameExpression.new(:a),Ast::NameExpression.new(:b)), Ast::AssignmentExpression.new(Ast::NameExpression.new(:b),Ast::OperatorExpression.new("+", Ast::NameExpression.new(:tmp),Ast::NameExpression.new(:b))), Ast::CallSiteExpression.new(:puts, [Ast::NameExpression.new(:b)] ,Ast::NameExpression.new(:self)), Ast::AssignmentExpression.new(Ast::NameExpression.new(:n),Ast::OperatorExpression.new("-", Ast::NameExpression.new(:n),Ast::IntegerExpression.new(1)))] )])
@root = :
end
end

View File

@ -13,7 +13,7 @@ if ENV['CODECLIMATE_REPO_TOKEN']
CodeClimate::TestReporter.start
end
require 'soml-parser'
require 'salama-reader'
require "minitest"
require "minitest/autorun"

View File

@ -1,60 +1,2 @@
require_relative "setup"
require "parslet/convenience"
require "ast/sexp"
require "pp"
class TestAll < MiniTest::Test
include AST::Sexp
SEPERATOR = "-- -- --"
def check_file file
inn , out = File.new(file).read.split(SEPERATOR)
sexp = eval(out)
begin
syntax = Parser::Salama.new.parse(inn)
rescue
root = file.split("/")[2]
parser = Parser::Salama.new.send root.to_sym
syntax = parser.parse_with_debug(inn )
end
result = Parser::Transform.new.apply(syntax)
equal = (sexp == result)
unless equal
if ENV["FIX"]
out_file = File.new(file, "w")
out_file.puts inn
out_file.puts SEPERATOR
out_file.puts result.inspect
out_file.close
puts "Fixed #{file}"
sexp = result
else
pp syntax
puts result.inspect
end
end
assert_equal sexp , result
check_transform sexp
end
def check_transform sexp
code = Soml.ast_to_code sexp
assert code.is_a?(Soml::Code) , "Returned #{code}"
end
# this creates test methods dynamically. For each test_* method we create
# three test_*[ast/parse/transf] methods that in turn check the three phases.
# runnable_methods is called by minitest to determine which tests to run
def self.runnable_methods
all = Dir["test/cases/*/*.tst"]
tests =[]
all.each do |file|
name = file.sub("test/cases/","").sub("/","_").sub(".tst","")
tests << name
self.send(:define_method, name ) do
send("check_file" , file)
end
end
tests
end
end
require_relative "unit/test_all"
#require_relative "roots/test_all"

13
test/unit/test_all.rb Normal file
View File

@ -0,0 +1,13 @@
#require_relative "test_arguments"
require_relative "test_basic"
require_relative "test_call_site"
#require_relative "test_class"
#require_relative "test_compound"
require_relative "test_conditional"
#require_relative "test_expressions"
#require_relative "test_function_definition"
#require_relative "test_module"
#require_relative "test_operators"
#require_relative "test_return"
#require_relative "test_while"

View File

@ -0,0 +1,37 @@
require_relative "../parser_helper"
class TestArguments < MiniTest::Test
# include the magic (setup and parse -> test method translation), see there
include ParserHelper
def test_one_argument
@input = '(42)'
@parse_output = {:argument_list => [{:argument => {:integer => '42'}}] }
@output = [Ast::IntegerExpression.new(42) ]
@root = :argument_list
end
def test_argument_list
@input = '(42, foo)'
@parse_output = {:argument_list => [{:argument => {:integer => '42'}},
{:argument => {:name => 'foo'}}]}
@output = [Ast::IntegerExpression.new(42), Ast::NameExpression.new('foo')]
@root = :argument_list
end
def test_parameter
@input = "(foo)"
@parse_output = {:parameter_list=>[{:parameter=>{:name=>"foo"}}]}
@output = [Ast::NameExpression.new('foo')]
@root = :parameter_list
end
def test_parameter_list
@input = "( foo , bar)"
@parse_output = {:parameter_list => [{:parameter => { :name => "foo"}},
{:parameter => { :name => "bar"}} ]}
@output = [Ast::NameExpression.new('foo') , Ast::NameExpression.new('bar')]
@root = :parameter_list
end
end

68
test/unit/test_basic.rb Normal file
View File

@ -0,0 +1,68 @@
require_relative "../parser_helper"
class TestBasic < MiniTest::Test
#test basics and keyword expressions. Keywords includes BasicTypes
include ParserHelper
def test_true
@input = 'true '
@output = s(:true)
check :keyword_true
end
def test_false
@input = 'false '
@output = s(:false)
check :keyword_false
end
def test_nil
@input = 'nil '
@output = s(:nil)
check :keyword_nil
end
def test_integer
@input = '42 '
@output = s(:int , 42)
check :integer_expression
end
def test_name
@input = 'foo '
@output = 'foo'
check :name_expression
end
def test_name_underscode_start
@input = '_bar '
@output = '_bar'
check :name_expression
end
def test_name_underscode_middle
@input = 'foo_bar '
@output = 'foo_bar'
check :name_expression
end
def test_module_name
@input = 'FooBar '
@output = s(:aspect , "FooBar")
check :aspect_name_expression
end
def test_string
@input = '"hello"'
@output = s(:string , 'hello')
check :string_expression
end
def test_string_escapes
out = 'hello \nyou'
@input = '"' + out + '"'
@output = s(:string , out)
check :string_expression
end
end

48
test/unit/test_block.rb Normal file
View File

@ -0,0 +1,48 @@
require_relative "../parser_helper"
class TestBlock < MiniTest::Test
# include the magic (setup and parse -> test method translation), see there
include ParserHelper
def pest_block
@input = <<HERE
while(1) do
tmp = a
puts(b)
end
HERE
@input.chop!
@parse_output = {:while=>"while", :while_cond=>{:integer=>"1"}, :do=>"do", :body=>{:expressions=>[{:l=>{:name=>"tmp"}, :o=>"= ", :r=>{:name=>"a"}}, {:call_site=>{:name=>"puts"}, :argument_list=>[{:argument=>{:name=>"b"}}]}], :end=>"end"}}
@output = Ast::BlockExpression.new(Ast::IntegerExpression.new(1), [Ast::AssignmentExpression.new(Ast::NameExpression.new(:tmp),Ast::NameExpression.new(:a)), Ast::CallSiteExpression.new(:puts, [Ast::NameExpression.new(:b)] ,Ast::NameExpression.new(:self))] )
@root = :block
end
def pest_block_method
@input = <<HERE
while(1) do
tmp = String.new()
tmp.puts(i)
end
HERE
@input.chop!
@parse_output = {:while=>"while", :while_cond=>{:integer=>"1"}, :do=>"do", :body=>{:expressions=>[{:l=>{:name=>"tmp"}, :o=>"= ", :r=>{:receiver=>{:module_name=>"String"}, :call_site=>{:name=>"new"}, :argument_list=>[]}}, {:receiver=>{:name=>"tmp"}, :call_site=>{:name=>"puts"}, :argument_list=>[{:argument=>{:name=>"i"}}]}], :end=>"end"}}
@output = Ast::BlockExpression.new(Ast::IntegerExpression.new(1), [Ast::AssignmentExpression.new(Ast::NameExpression.new(:tmp),Ast::CallSiteExpression.new(:new, [] ,Ast::ModuleName.new(:String))), Ast::CallSiteExpression.new(:puts, [Ast::NameExpression.new(:i)] ,Ast::NameExpression.new(:tmp))] )
@root = :block
end
def pest_big_block
@input = <<HERE
while( n > 1) do
tmp = a
a = b
b = tmp + b
puts(b)
n = n - 1
end
HERE
@input.chop!
@parse_output = {:while=>"while", :while_cond=>{:l=>{:name=>"n"}, :o=>"> ", :r=>{:integer=>"1"}}, :do=>"do", :body=>{:expressions=>[{:l=>{:name=>"tmp"}, :o=>"= ", :r=>{:name=>"a"}}, {:l=>{:name=>"a"}, :o=>"= ", :r=>{:name=>"b"}}, {:l=>{:name=>"b"}, :o=>"= ", :r=>{:l=>{:name=>"tmp"}, :o=>"+ ", :r=>{:name=>"b"}}}, {:call_site=>{:name=>"puts"}, :argument_list=>[{:argument=>{:name=>"b"}}]}, {:l=>{:name=>"n"}, :o=>"= ", :r=>{:l=>{:name=>"n"}, :o=>"- ", :r=>{:integer=>"1"}}}], :end=>"end"}}
@output = Ast::BlockExpression.new(Ast::OperatorExpression.new(">", Ast::NameExpression.new(:n),Ast::IntegerExpression.new(1)), [Ast::AssignmentExpression.new(Ast::NameExpression.new(:tmp),Ast::NameExpression.new(:a)), Ast::AssignmentExpression.new(Ast::NameExpression.new(:a),Ast::NameExpression.new(:b)), Ast::AssignmentExpression.new(Ast::NameExpression.new(:b),Ast::OperatorExpression.new("+", Ast::NameExpression.new(:tmp),Ast::NameExpression.new(:b))), Ast::CallSiteExpression.new(:puts, [Ast::NameExpression.new(:b)] ,Ast::NameExpression.new(:self)), Ast::AssignmentExpression.new(Ast::NameExpression.new(:n),Ast::OperatorExpression.new("-", Ast::NameExpression.new(:n),Ast::IntegerExpression.new(1)))] )
@root = :block
end
end

View File

@ -0,0 +1,43 @@
require_relative "../parser_helper"
class TestCallSite < MiniTest::Test
include ParserHelper
def test_single_self
@input = 'self.foo(42)'
@output = s(:call, s(:field , "self" , "foo") , [s(:int, 42)])
check :call_site
end
def test_single_name
@input = 'my_my.foo(42)'
@parse_output = {:receiver=>{:name=>"my_my"}, :call_site=>{:name=>"foo"}, :argument_list=>[{:argument=>{:integer=>"42"}}]}
@output = s(:call, s(:field, "my_my", "foo"), [s(:int, 42)])
check :call_site
end
def test_int_receiver
@input = '42.put()'
@output = s(:call, s(:field, s(:int , 42) , "put") , [])
check :call_site
end
def test_string_receiver
@input = '"hello".puts()'
@output = s(:call , s(:field , s(:string , "hello") , "puts") , [])
check :call_site
end
def test_call_site_2
@input = 'self.baz(42, foo)'
@output = s(:call ,s(:field , "self", "baz") , [s(:int, 42), "foo"])
check :call_site
end
def test_call_site_3
@input = 'self.baz(42, foo , bar)'
@output = s(:call, s(:field, "self", "baz"), [s(:int, 42), "foo", "bar"])
check :call_site
end
end

97
test/unit/test_class.rb Normal file
View File

@ -0,0 +1,97 @@
require_relative "../parser_helper"
class TestClassDef < MiniTest::Test
# include the magic (setup and parse -> test method translation), see there
include ParserHelper
def test_simplest_class
@input = <<HERE
class Foo
5
end
HERE
@parse_output = {:module_name=>"Foo", :derived_name=>nil, :class_expressions=>[{:integer=>"5"}], :end=>"end"}
@output = Ast::ClassExpression.new(:Foo ,nil, [Ast::IntegerExpression.new(5)] )
@root = :class_definition
end
def test_class_ops
@input = <<HERE
class Opers
def foo(x)
@abba = 5
2 + 5
end
end
HERE
@parse_output = {:module_name=>"Opers", :derived_name=>nil, :class_expressions=>[{:function_name=>{:name=>"foo"}, :parameter_list=>[{:parameter=>{:name=>"x"}}], :expressions=>[{:l=>{:instance_variable=>{:name=>"abba"}}, :o=>"= ", :r=>{:integer=>"5"}}, {:l=>{:integer=>"2"}, :o=>"+ ", :r=>{:integer=>"5"}}], :end=>"end"}], :end=>"end"}
@output = Ast::ClassExpression.new(:Opers ,nil, [Ast::FunctionExpression.new(:foo, [Ast::NameExpression.new(:x)] , [Ast::AssignmentExpression.new(Ast::VariableExpression.new(:abba),Ast::IntegerExpression.new(5)),Ast::OperatorExpression.new("+", Ast::IntegerExpression.new(2),Ast::IntegerExpression.new(5))] ,nil )] )
@root = :class_definition
end
def test_class_if
@input = <<HERE
class Ifi
def ofthen(n)
if(0)
isit = 42
else
maybenot = 667
end
end
end
HERE
@parse_output = {:module_name=>"Ifi", :derived_name=>nil, :class_expressions=>[{:function_name=>{:name=>"ofthen"}, :parameter_list=>[{:parameter=>{:name=>"n"}}], :expressions=>[{:if=>"if", :conditional=>{:integer=>"0"}, :if_true=>{:expressions=>[{:l=>{:name=>"isit"}, :o=>"= ", :r=>{:integer=>"42"}}], :else=>"else"}, :if_false=>{:expressions=>[{:l=>{:name=>"maybenot"}, :o=>"= ", :r=>{:integer=>"667"}}], :end=>"end"}}], :end=>"end"}], :end=>"end"}
@output = Ast::ClassExpression.new(:Ifi ,nil, [Ast::FunctionExpression.new(:ofthen, [Ast::NameExpression.new(:n)] , [Ast::IfExpression.new(Ast::IntegerExpression.new(0), [Ast::AssignmentExpression.new(Ast::NameExpression.new(:isit),Ast::IntegerExpression.new(42))],[Ast::AssignmentExpression.new(Ast::NameExpression.new(:maybenot),Ast::IntegerExpression.new(667))] )] ,nil )] )
@root = :class_definition
end
def test_class_function
@input = <<HERE
class Pifi
ofthen(3+4 , var)
def ofthen(n,m)
44
end
end
HERE
@parse_output = {:module_name=>"Pifi", :derived_name=>nil, :class_expressions=>[{:call_site=>{:name=>"ofthen"}, :argument_list=>[{:argument=>{:l=>{:integer=>"3"}, :o=>"+", :r=>{:integer=>"4"}}}, {:argument=>{:name=>"var"}}]}, {:function_name=>{:name=>"ofthen"}, :parameter_list=>[{:parameter=>{:name=>"n"}}, {:parameter=>{:name=>"m"}}], :expressions=>[{:integer=>"44"}], :end=>"end"}], :end=>"end"}
@output = Ast::ClassExpression.new(:Pifi ,nil, [Ast::CallSiteExpression.new(:ofthen, [Ast::OperatorExpression.new("+", Ast::IntegerExpression.new(3),Ast::IntegerExpression.new(4)),Ast::NameExpression.new(:var)] ,Ast::NameExpression.new(:self)), Ast::FunctionExpression.new(:ofthen, [Ast::NameExpression.new(:n),Ast::NameExpression.new(:m)] , [Ast::IntegerExpression.new(44)] ,nil )] )
@root = :class_definition
end
def test_class_module
@input = <<HERE
class Foo
module Boo
funcall(3+4 , var)
end
end
HERE
@parse_output = {:module_name=>"Foo", :derived_name=>nil, :class_expressions=>[{:module_name=>"Boo", :module_expressions=>[{:call_site=>{:name=>"funcall"}, :argument_list=>[{:argument=>{:l=>{:integer=>"3"}, :o=>"+", :r=>{:integer=>"4"}}}, {:argument=>{:name=>"var"}}]}], :end=>"end"}], :end=>"end"}
@output = Ast::ClassExpression.new(:Foo ,nil, [Ast::ModuleExpression.new(:Boo ,[Ast::CallSiteExpression.new(:funcall, [Ast::OperatorExpression.new("+", Ast::IntegerExpression.new(3),Ast::IntegerExpression.new(4)),Ast::NameExpression.new(:var)] ,Ast::NameExpression.new(:self))] )] )
@root = :class_definition
end
def test_class_derived
@input = <<HERE
class Foo < Object
ofthen(3+4 , var)
end
HERE
@parse_output = {:module_name=>"Foo", :derived_name=>{:module_name=>"Object"}, :class_expressions=>[{:call_site=>{:name=>"ofthen"}, :argument_list=>[{:argument=>{:l=>{:integer=>"3"}, :o=>"+", :r=>{:integer=>"4"}}}, {:argument=>{:name=>"var"}}]}], :end=>"end"}
@output = Ast::ClassExpression.new(:Foo ,:Object, [Ast::CallSiteExpression.new(:ofthen, [Ast::OperatorExpression.new("+", Ast::IntegerExpression.new(3),Ast::IntegerExpression.new(4)),Ast::NameExpression.new(:var)] ,Ast::NameExpression.new(:self))] )
@root = :class_definition
end
def test_class_method
@input = <<HERE
class Foo < Object
def Foo.test()
43
end
end
HERE
@parse_output = {:module_name=>"Foo", :derived_name=>{:module_name=>"Object"}, :class_expressions=>[{:receiver=>{:module_name=>"Foo"}, :function_name=>{:name=>"test"}, :parameter_list=>[], :expressions=>[{:integer=>"43"}], :end=>"end"}], :end=>"end"}
@output = Ast::ClassExpression.new(:Foo ,:Object, [Ast::FunctionExpression.new(:test, [] , [Ast::IntegerExpression.new(43)] ,Ast::ModuleName.new(:Foo) )] )
@root = :class_definition
end
end

View File

@ -0,0 +1,51 @@
require_relative "../parser_helper"
class TestCompound < MiniTest::Test
# include the magic (setup and parse -> test method translation), see there
include ParserHelper
def test_one_array
@input = '[42]'
@parse_output = {:array_constant=>[{:array_element=>{:integer=>"42"}}]}
@output = Ast::ArrayExpression.new([Ast::IntegerExpression.new(42)])
@root = :array_constant
end
def test_array_list
@input = '[42, foo]'
@parse_output = {:array_constant=>[{:array_element=>{:integer=>"42"}}, {:array_element=>{:name=>"foo"}}]}
@output = Ast::ArrayExpression.new([Ast::IntegerExpression.new(42), Ast::NameExpression.new("foo")])
@root = :array_constant
end
def test_array_list2
@input = '[42, nil]'
@parse_output = {:array_constant=>[{:array_element=>{:integer=>"42"}}, {:array_element=>{:nil=>"nil"}}]}
@output = Ast::ArrayExpression.new([Ast::IntegerExpression.new(42), Ast::NilExpression.new()])
@root = :array_constant
end
def test_array_ops
@input = '[ 3 + 4 , foo(22) ]'
@parse_output = {:array_constant=>[{:array_element=>{:l=>{:integer=>"3"}, :o=>"+ ", :r=>{:integer=>"4"}}}, {:array_element=>{:call_site=>{:name=>"foo"}, :argument_list=>[{:argument=>{:integer=>"22"}}]}}]}
@output = Ast::ArrayExpression.new(
[Ast::OperatorExpression.new("+", Ast::IntegerExpression.new(3),Ast::IntegerExpression.new(4)),
Ast::CallSiteExpression.new("foo", [Ast::IntegerExpression.new(22)] )])
@root = :array_constant
end
def test_hash
@input = '{ foo => 33 }'
@parse_output = {:hash_constant=>[{:hash_pair=>{:hash_key=>{:name=>"foo"}, :hash_value=>{:integer=>"33"}}}]}
@output = Ast::HashExpression.new([Ast::AssociationExpression.new(Ast::NameExpression.new("foo") , Ast::IntegerExpression.new(33))])
@root = :hash_constant
end
def test_hash_list
@input = "{foo => 33 , bar => 42}"
@parse_output = {:hash_constant=>[{:hash_pair=>{:hash_key=>{:name=>"foo"}, :hash_value=>{:integer=>"33"}}}, {:hash_pair=>{:hash_key=>{:name=>"bar"}, :hash_value=>{:integer=>"42"}}}]}
@output = Ast::HashExpression.new([Ast::AssociationExpression.new(Ast::NameExpression.new("foo") , Ast::IntegerExpression.new(33)),Ast::AssociationExpression.new(Ast::NameExpression.new("bar") , Ast::IntegerExpression.new(42))])
@root = :hash_constant
end
end

View File

@ -0,0 +1,74 @@
require_relative "../parser_helper"
class TestConditional < MiniTest::Test
include ParserHelper
def test_assignment
@input = "myvar = 42"
@output = s(:assign, :myvar, s(:int, 42))
check :assignment
end
def test_variable_declaration
@input = "int myvar"
@output = s(:variable,s(:type, "int", "myvar"), nil)
check :variable_definition
end
def test_variable_declaration_value
@input = "int myvar = 42"
@output = s(:variable, s(:type, "int", "myvar"), s(:int, 42))
check :variable_definition
end
def test_if
@input = <<HERE
if( 1 )
int num = 42
end
HERE
@output = s(:if, s(:cond, s(:int, 1)), s(:then, [s(:variable,
s(:type, "int", "num"),
s(:int, 42))]))
check :conditional
end
def test_conditional_with_calls
@input = <<HERE
if(var)
42.add(5)
end
HERE
@output = s(:if,
s(:cond, "var"),
s(:then, [s(:call, s(:field, s(:int, 42), "add"), [s(:int, 5)])]))
check :conditional
end
def ttest_conditional_nil
@input = <<HERE
if(3 == nil)
3
else
4
end
HERE
@input.chop!
@parse_output = {:if=>"if", :conditional=>{:l=>{:integer=>"3"}, :o=>"== ", :r=>{:nil=>"nil"}}, :if_true=>{:expressions=>[{:integer=>"3"}], :else=>"else"}, :if_false=>{:expressions=>[{:integer=>"4"}], :end=>"end"}}
@output = nil
@root = :conditional
end
def pest_simple_if
@input = <<HERE
if(3 == nil)
3
end
HERE
@input.chop!
@parse_output = {:if=>"if", :conditional=>{:l=>{:integer=>"3"}, :o=>"== ", :r=>{:nil=>"nil"}}, :if_true=>{:expressions=>[{:integer=>"3"}], :else=>"else"}, :if_false=>{:expressions=>[{:integer=>"4"}], :end=>"end"}}
@output = nil
@root = :conditional
end
end

View File

@ -0,0 +1,40 @@
require_relative "../parser_helper"
class TestExpressions < MiniTest::Test
# include the magic (setup and parse -> test method translation), see there
include ParserHelper
def test_expression_else
@input = <<HERE
dud
fuu(3)
else
HERE
@input.chop!
@parse_output = {:expressions=>[{:name=>"dud"},
{:call_site=>{:name=>"fuu"}, :argument_list=>[{:argument=>{:integer=>"3"}}]}],
:else=>"else"}
@output ={:expressions=>[Ast::NameExpression.new("dud"),
Ast::CallSiteExpression.new("fuu", [Ast::IntegerExpression.new(3)] )], :else=>"else"}
@root = :expressions_else
end
def test_expression_end
@input = <<HERE
name
call(4,6)
end
HERE
@input.chop!
@parse_output = {:expressions=>[{:name=>"name"},
{:call_site=>{:name=>"call"}, :argument_list=>[{:argument=>{:integer=>"4"}}, {:argument=>{:integer=>"6"}}]}],
:end=>"end"}
@output = {:expressions=>[Ast::NameExpression.new("name"),
Ast::CallSiteExpression.new("call", [Ast::IntegerExpression.new(4),Ast::IntegerExpression.new(6)] )],
:end=>"end"}
@root = :expressions_end
end
end

26
test/unit/test_fails.rb Normal file
View File

@ -0,0 +1,26 @@
require_relative "../parser_helper"
# some cases that fail, and fail badly.
# These make the parse "hang", ie there is some looping going on in the parser, but not straight down, as theey don't
# throw even StackError
# Annoyingly, the user error is quite small, a missing bracket or things
class TestFails < MiniTest::Test
# include the magic (setup and parse -> test method translation), see there
include ParserHelper
def test_fail_function
@input = <<HERE
class Foo
def bar
4
end
end
HERE
@parse_output = nil
@output = nil
@root = :root
end
end

View File

@ -0,0 +1,130 @@
require_relative "../parser_helper"
class TestFunctionDefinition < MiniTest::Test
# include the magic (setup and parse -> test method translation), see there
include ParserHelper
def test_simplest_function
@input = <<HERE
def foo(x)
5
end
HERE
@parse_output = {:function_name=>{:name=>"foo"}, :parameter_list=>[{:parameter=>{:name=>"x"}}], :expressions=>[{:integer=>"5"}], :end=>"end"}
@output = Ast::FunctionExpression.new(:foo, [Ast::NameExpression.new("x")] , [Ast::IntegerExpression.new(5)] )
@root = :function_definition
end
def test_class_function
@input = <<HERE
def String.length(x)
@length
end
HERE
@parse_output = {:receiver=>{:module_name=>"String"}, :function_name=>{:name=>"length"}, :parameter_list=>[{:parameter=>{:name=>"x"}}], :expressions=>[{:instance_variable=>{:name=>"length"}}], :end=>"end"}
@output = Ast::FunctionExpression.new(:length, [Ast::NameExpression.new("x")] , [Ast::VariableExpression.new(:length)] ,Ast::ModuleName.new("String") )
@root = :function_definition
end
def test_function_ops
@input = <<HERE
def foo(x)
abba = 5
2 + 5
end
HERE
@parse_output = {:function_name=>{:name=>"foo"}, :parameter_list=>[{:parameter=>{:name=>"x"}}], :expressions=>[{:l=>{:name=>"abba"}, :o=>"= ", :r=>{:integer=>"5"}}, {:l=>{:integer=>"2"}, :o=>"+ ", :r=>{:integer=>"5"}}], :end=>"end"}
@output = Ast::FunctionExpression.new(:foo, [Ast::NameExpression.new(:x)] , [Ast::AssignmentExpression.new(Ast::NameExpression.new(:abba),Ast::IntegerExpression.new(5)),Ast::OperatorExpression.new("+", Ast::IntegerExpression.new(2),Ast::IntegerExpression.new(5))] ,nil )
@root = :function_definition
end
def test_function_if
@input = <<HERE
def ofthen(n)
if(0)
isit = 42
else
maybenot = 667
end
end
HERE
@parse_output = {:function_name=>{:name=>"ofthen"}, :parameter_list=>[{:parameter=>{:name=>"n"}}], :expressions=>[{:if=>"if", :conditional=>{:integer=>"0"}, :if_true=>{:expressions=>[{:l=>{:name=>"isit"}, :o=>"= ", :r=>{:integer=>"42"}}], :else=>"else"}, :if_false=>{:expressions=>[{:l=>{:name=>"maybenot"}, :o=>"= ", :r=>{:integer=>"667"}}], :end=>"end"}}], :end=>"end"}
@output = Ast::FunctionExpression.new(:ofthen, [Ast::NameExpression.new(:n)] , [Ast::IfExpression.new(Ast::IntegerExpression.new(0), [Ast::AssignmentExpression.new(Ast::NameExpression.new(:isit),Ast::IntegerExpression.new(42))],[Ast::AssignmentExpression.new(Ast::NameExpression.new(:maybenot),Ast::IntegerExpression.new(667))] )] ,nil )
@root = :function_definition
end
def test_function_return
@input = <<HERE
def retvar(n)
i = 5
return i
end
HERE
@parse_output = {:function_name=>{:name=>"retvar"}, :parameter_list=>[{:parameter=>{:name=>"n"}}], :expressions=>[{:l=>{:name=>"i"}, :o=>"= ", :r=>{:integer=>"5"}}, {:return=>"return", :return_expression=>{:name=>"i"}}], :end=>"end"}
@output = Ast::FunctionExpression.new(:retvar, [Ast::NameExpression.new(:n)] , [Ast::AssignmentExpression.new(Ast::NameExpression.new(:i),Ast::IntegerExpression.new(5)),Ast::ReturnExpression.new(Ast::NameExpression.new(:i) )] ,nil )
@root = :function_definition
end
def test_function_return_if
@input = <<HERE
def retvar(n)
if( n > 5)
return 10
else
return 20
end
end
HERE
@parse_output = {:function_name=>{:name=>"retvar"}, :parameter_list=>[{:parameter=>{:name=>"n"}}], :expressions=>[{:if=>"if", :conditional=>{:l=>{:name=>"n"}, :o=>"> ", :r=>{:integer=>"5"}}, :if_true=>{:expressions=>[{:return=>"return", :return_expression=>{:integer=>"10"}}], :else=>"else"}, :if_false=>{:expressions=>[{:return=>"return", :return_expression=>{:integer=>"20"}}], :end=>"end"}}], :end=>"end"}
@output = Ast::FunctionExpression.new(:retvar, [Ast::NameExpression.new("n")] , [Ast::IfExpression.new(Ast::OperatorExpression.new(">", Ast::NameExpression.new("n"),Ast::IntegerExpression.new(5)), [Ast::ReturnExpression.new(Ast::IntegerExpression.new(10) )],[Ast::ReturnExpression.new(Ast::IntegerExpression.new(20) )] )] )
@root = :function_definition
end
def test_function_return_while
@input = <<HERE
def retvar(n)
while( n > 5) do
n = n + 1
return n
end
end
HERE
@parse_output = {:function_name=>{:name=>"retvar"}, :parameter_list=>[{:parameter=>{:name=>"n"}}], :expressions=>[{:while=>"while", :while_cond=>{:l=>{:name=>"n"}, :o=>"> ", :r=>{:integer=>"5"}}, :do=>"do", :body=>{:expressions=>[{:l=>{:name=>"n"}, :o=>"= ", :r=>{:l=>{:name=>"n"}, :o=>"+ ", :r=>{:integer=>"1"}}}, {:return=>"return", :return_expression=>{:name=>"n"}}], :end=>"end"}}], :end=>"end"}
@output = Ast::FunctionExpression.new(:retvar, [Ast::NameExpression.new(:n)] , [Ast::WhileExpression.new(Ast::OperatorExpression.new(">", Ast::NameExpression.new(:n),Ast::IntegerExpression.new(5)), [Ast::AssignmentExpression.new(Ast::NameExpression.new(:n),Ast::OperatorExpression.new("+", Ast::NameExpression.new(:n),Ast::IntegerExpression.new(1))), Ast::ReturnExpression.new(Ast::NameExpression.new(:n) )] )] ,nil )
@root = :function_definition
end
def test_function_while
@input = <<HERE
def fibonaccit(n)
a = 0
while (n) do
some = 43
other = some * 4
end
end
HERE
@parse_output = {:function_name=>{:name=>"fibonaccit"}, :parameter_list=>[{:parameter=>{:name=>"n"}}], :expressions=>[{:l=>{:name=>"a"}, :o=>"= ", :r=>{:integer=>"0"}}, {:while=>"while", :while_cond=>{:name=>"n"}, :do=>"do", :body=>{:expressions=>[{:l=>{:name=>"some"}, :o=>"= ", :r=>{:integer=>"43"}}, {:l=>{:name=>"other"}, :o=>"= ", :r=>{:l=>{:name=>"some"}, :o=>"* ", :r=>{:integer=>"4"}}}], :end=>"end"}}], :end=>"end"}
@output = Ast::FunctionExpression.new(:fibonaccit, [Ast::NameExpression.new(:n)] , [Ast::AssignmentExpression.new(Ast::NameExpression.new(:a),Ast::IntegerExpression.new(0)),Ast::WhileExpression.new(Ast::NameExpression.new(:n), [Ast::AssignmentExpression.new(Ast::NameExpression.new(:some),Ast::IntegerExpression.new(43)), Ast::AssignmentExpression.new(Ast::NameExpression.new(:other),Ast::OperatorExpression.new("*", Ast::NameExpression.new(:some),Ast::IntegerExpression.new(4)))] )] ,nil )
@root = :function_definition
end
def test_function_big_while
@input = <<HERE
def fibonaccit(n)
a = 0
b = 1
while( n > 1 ) do
tmp = a
a = b
b = tmp + b
puts(b)
n = n - 1
end
end
HERE
@parse_output = {:function_name=>{:name=>"fibonaccit"}, :parameter_list=>[{:parameter=>{:name=>"n"}}], :expressions=>[{:l=>{:name=>"a"}, :o=>"= ", :r=>{:integer=>"0"}}, {:l=>{:name=>"b"}, :o=>"= ", :r=>{:integer=>"1"}}, {:while=>"while", :while_cond=>{:l=>{:name=>"n"}, :o=>"> ", :r=>{:integer=>"1"}}, :do=>"do", :body=>{:expressions=>[{:l=>{:name=>"tmp"}, :o=>"= ", :r=>{:name=>"a"}}, {:l=>{:name=>"a"}, :o=>"= ", :r=>{:name=>"b"}}, {:l=>{:name=>"b"}, :o=>"= ", :r=>{:l=>{:name=>"tmp"}, :o=>"+ ", :r=>{:name=>"b"}}}, {:call_site=>{:name=>"puts"}, :argument_list=>[{:argument=>{:name=>"b"}}]}, {:l=>{:name=>"n"}, :o=>"= ", :r=>{:l=>{:name=>"n"}, :o=>"- ", :r=>{:integer=>"1"}}}], :end=>"end"}}], :end=>"end"}
@output = Ast::FunctionExpression.new(:fibonaccit, [Ast::NameExpression.new(:n)] , [Ast::AssignmentExpression.new(Ast::NameExpression.new(:a),Ast::IntegerExpression.new(0)),Ast::AssignmentExpression.new(Ast::NameExpression.new(:b),Ast::IntegerExpression.new(1)),Ast::WhileExpression.new(Ast::OperatorExpression.new(">", Ast::NameExpression.new(:n),Ast::IntegerExpression.new(1)), [Ast::AssignmentExpression.new(Ast::NameExpression.new(:tmp),Ast::NameExpression.new(:a)), Ast::AssignmentExpression.new(Ast::NameExpression.new(:a),Ast::NameExpression.new(:b)), Ast::AssignmentExpression.new(Ast::NameExpression.new(:b),Ast::OperatorExpression.new("+", Ast::NameExpression.new(:tmp),Ast::NameExpression.new(:b))), Ast::CallSiteExpression.new(:puts, [Ast::NameExpression.new(:b)] ,Ast::NameExpression.new(:self)), Ast::AssignmentExpression.new(Ast::NameExpression.new(:n),Ast::OperatorExpression.new("-", Ast::NameExpression.new(:n),Ast::IntegerExpression.new(1)))] )] ,nil )
@root = :function_definition
end
end

86
test/unit/test_module.rb Normal file
View File

@ -0,0 +1,86 @@
require_relative "../parser_helper"
class TestModuleDef < MiniTest::Test
# include the magic (setup and parse -> test method translation), see there
include ParserHelper
def test_simplest_module
@input = <<HERE
module Simple
5
end
HERE
@parse_output = {:module_name=>"Simple", :module_expressions=>[{:integer=>"5"}], :end=>"end"}
@output = Ast::ModuleExpression.new(:Simple ,[Ast::IntegerExpression.new(5)] )
@root = :module_definition
end
def test_module_ops
@input = <<HERE
module Opers
def foo(x)
abba = 5
2 + 5
end
end
HERE
@parse_output = {:module_name=>"Opers", :module_expressions=>[{:function_name=>{:name=>"foo"}, :parameter_list=>[{:parameter=>{:name=>"x"}}], :expressions=>[{:l=>{:name=>"abba"}, :o=>"= ", :r=>{:integer=>"5"}}, {:l=>{:integer=>"2"}, :o=>"+ ", :r=>{:integer=>"5"}}], :end=>"end"}], :end=>"end"}
@output = Ast::ModuleExpression.new(:Opers ,[Ast::FunctionExpression.new(:foo, [Ast::NameExpression.new(:x)] , [Ast::AssignmentExpression.new(Ast::NameExpression.new(:abba),Ast::IntegerExpression.new(5)),Ast::OperatorExpression.new("+", Ast::IntegerExpression.new(2),Ast::IntegerExpression.new(5))] ,nil )] )
@root = :module_definition
end
def test_module_assign_instance
@input = <<HERE
module Opers
@abba = 5
end
HERE
@parse_output = {:module_name=>"Opers", :module_expressions=>[{:l=>{:instance_variable=>{:name=>"abba"}}, :o=>"= ", :r=>{:integer=>"5"}}], :end=>"end"}
@output = Ast::ModuleExpression.new(:Opers ,[Ast::AssignmentExpression.new(Ast::VariableExpression.new(:abba),Ast::IntegerExpression.new(5))] )
@root = :module_definition
end
def test_module_if
@input = <<HERE
module Foo
def ofthen(n)
if(0)
isit = 42
else
maybenot = 667
end
end
end
HERE
@parse_output = {:module_name=>"Foo", :module_expressions=>[{:function_name=>{:name=>"ofthen"}, :parameter_list=>[{:parameter=>{:name=>"n"}}], :expressions=>[{:if=>"if", :conditional=>{:integer=>"0"}, :if_true=>{:expressions=>[{:l=>{:name=>"isit"}, :o=>"= ", :r=>{:integer=>"42"}}], :else=>"else"}, :if_false=>{:expressions=>[{:l=>{:name=>"maybenot"}, :o=>"= ", :r=>{:integer=>"667"}}], :end=>"end"}}], :end=>"end"}], :end=>"end"}
@output = Ast::ModuleExpression.new(:Foo ,[Ast::FunctionExpression.new(:ofthen, [Ast::NameExpression.new(:n)] , [Ast::IfExpression.new(Ast::IntegerExpression.new(0), [Ast::AssignmentExpression.new(Ast::NameExpression.new(:isit),Ast::IntegerExpression.new(42))],[Ast::AssignmentExpression.new(Ast::NameExpression.new(:maybenot),Ast::IntegerExpression.new(667))] )] ,nil )] )
@root = :module_definition
end
def test_module_function
@input = <<HERE
module Soho
ofthen(3+4 , var)
def ofthen(n,m)
44
end
end
HERE
@parse_output = {:module_name=>"Soho", :module_expressions=>[{:call_site=>{:name=>"ofthen"}, :argument_list=>[{:argument=>{:l=>{:integer=>"3"}, :o=>"+", :r=>{:integer=>"4"}}}, {:argument=>{:name=>"var"}}]}, {:function_name=>{:name=>"ofthen"}, :parameter_list=>[{:parameter=>{:name=>"n"}}, {:parameter=>{:name=>"m"}}], :expressions=>[{:integer=>"44"}], :end=>"end"}], :end=>"end"}
@output = Ast::ModuleExpression.new(:Soho ,[Ast::CallSiteExpression.new(:ofthen, [Ast::OperatorExpression.new("+", Ast::IntegerExpression.new(3),Ast::IntegerExpression.new(4)),Ast::NameExpression.new("var")] ), Ast::FunctionExpression.new(:ofthen, [Ast::NameExpression.new("n"),Ast::NameExpression.new("m")] , [Ast::IntegerExpression.new(44)] )] )
@root = :module_definition
end
def test_module_class
@input = <<HERE
module Foo
class Bar
funcall(3+4 , var)
end
end
HERE
@parse_output = {:module_name=>"Foo", :module_expressions=>[{:module_name=>"Bar", :derived_name=>nil, :class_expressions=>[{:call_site=>{:name=>"funcall"}, :argument_list=>[{:argument=>{:l=>{:integer=>"3"}, :o=>"+", :r=>{:integer=>"4"}}}, {:argument=>{:name=>"var"}}]}], :end=>"end"}], :end=>"end"}
@output = Ast::ModuleExpression.new(:Foo ,[Ast::ClassExpression.new(:Bar ,nil, [Ast::CallSiteExpression.new(:funcall, [Ast::OperatorExpression.new("+", Ast::IntegerExpression.new(3),Ast::IntegerExpression.new(4)),Ast::NameExpression.new(:var)] ,Ast::NameExpression.new(:self))] )] )
@root = :module_definition
end
end

View File

@ -0,0 +1,86 @@
require_relative "../parser_helper"
class TestExpressions < MiniTest::Test
# include the magic (setup and parse -> test method translation), see there
include ParserHelper
def simple_op op
@input = "5 #{op} 3"
@parse_output = {:l=>{:integer=>"5"}, :o=>"#{op} ", :r=>{:integer=>"3"}}
@output = Ast::OperatorExpression.new(op, Ast::IntegerExpression.new(5),Ast::IntegerExpression.new(3))
@root = :operator_expression
end
def test_simple_multiply
simple_op "*"
end
def test_simple_devide
simple_op "/"
end
def test_simple_plus
simple_op "+"
end
def test_simple_minus
simple_op "-"
end
def test_simple_greater
simple_op ">"
end
def test_simple_smaller
simple_op "<"
end
def test_op_variable
@input = "a + 35"
@parse_output = {:l=>{:name=>"a"}, :o=>"+ ", :r=>{:integer=>"35"}}
@output = Ast::OperatorExpression.new("+", Ast::NameExpression.new(:a),Ast::IntegerExpression.new(35))
@root = :operator_expression
end
def test_op_two_variable
@input = "a - b"
@parse_output = {:l=>{:name=>"a"}, :o=>"- ", :r=>{:name=>"b"}}
@output = Ast::OperatorExpression.new("-", Ast::NameExpression.new(:a),Ast::NameExpression.new("b"))
@root = :operator_expression
end
def test_op_instance_variable
@input = "@a - 5"
@parse_output = {:l=>{:instance_variable=>{:name=>"a"}}, :o=>"- ", :r=>{:integer=>"5"}}
@output = Ast::OperatorExpression.new("-", Ast::VariableExpression.new(:a),Ast::IntegerExpression.new(5))
@root = :operator_expression
end
def test_op_variable_string
@input = 'a - "st"'
@parse_output = {:l=>{:name=>"a"}, :o=>"- ", :r=>{:string=>[{:char=>"s"}, {:char=>"t"}]}}
@output = Ast::OperatorExpression.new("-", Ast::NameExpression.new(:a),Ast::StringExpression.new("st"))
@root = :operator_expression
end
def test_two_same_ops
@input = '2 + 3 + 4'
@parse_output = {:l=>{:l=>{:integer=>"2"}, :o=>"+ ", :r=>{:integer=>"3"}}, :o=>"+ ", :r=>{:integer=>"4"}}
@output = Ast::OperatorExpression.new("+", Ast::OperatorExpression.new("+", Ast::IntegerExpression.new(2),Ast::IntegerExpression.new(3)),Ast::IntegerExpression.new(4))
@root = :operator_expression
end
def test_two_different_ops
@input = '2 + 3 * 4'
@parse_output = {:l=>{:integer=>"2"}, :o=>"+ ", :r=>{:l=>{:integer=>"3"}, :o=>"* ", :r=>{:integer=>"4"}}}
@output = Ast::OperatorExpression.new("+", Ast::IntegerExpression.new(2),Ast::OperatorExpression.new("*", Ast::IntegerExpression.new(3),Ast::IntegerExpression.new(4)))
@root = :operator_expression
end
def test_two_different_ops_order
@input = '2 * 3 + 4'
@parse_output = {:l=>{:l=>{:integer=>"2"}, :o=>"* ", :r=>{:integer=>"3"}}, :o=>"+ ", :r=>{:integer=>"4"}}
@output = Ast::OperatorExpression.new("+", Ast::OperatorExpression.new("*", Ast::IntegerExpression.new(2),Ast::IntegerExpression.new(3)),Ast::IntegerExpression.new(4))
@root = :operator_expression
end
def test_assignment
@input = "a = 5"
@parse_output = {:l=>{:name=>"a"}, :o=>"= ", :r=>{:integer=>"5"}}
@output = Ast::AssignmentExpression.new(Ast::NameExpression.new(:a),Ast::IntegerExpression.new(5))
@root = :operator_expression
end
def test_assignment_instance
@input = "@a = 5"
@parse_output = {:l=>{:instance_variable=>{:name=>"a"}}, :o=>"= ", :r=>{:integer=>"5"}}
@output = Ast::AssignmentExpression.new(Ast::VariableExpression.new(:a),Ast::IntegerExpression.new(5))
@root = :operator_expression
end
end

35
test/unit/test_return.rb Normal file
View File

@ -0,0 +1,35 @@
require_relative "../parser_helper"
class TestReturn < MiniTest::Test
# include the magic (setup and parse -> test method translation), see there
include ParserHelper
def test_return_int
@input = 'return 42'
@parse_output = {:return=>"return", :return_expression=>{:integer=>"42"}}
@output = Ast::ReturnExpression.new(Ast::IntegerExpression.new(42) )
@root = :simple_return
end
def test_return_variable
@input = 'return foo'
@parse_output = {:return=>"return", :return_expression=>{:name=>"foo"}}
@output = Ast::ReturnExpression.new(Ast::NameExpression.new("foo") )
@root = :simple_return
end
def test_return_string
@input = 'return "hello"'
@parse_output = {:return=>"return", :return_expression=>{:string=>[{:char=>"h"}, {:char=>"e"}, {:char=>"l"}, {:char=>"l"}, {:char=>"o"}]}}
@output = Ast::ReturnExpression.new(Ast::StringExpression.new("hello") )
@root = :simple_return
end
def test_return_true
@input = 'return true'
@parse_output = {:return=>"return", :return_expression=>{:true=>"true"}}
@output = Ast::ReturnExpression.new(Ast::TrueExpression.new() )
@root = :simple_return
end
end

55
test/unit/test_while.rb Normal file
View File

@ -0,0 +1,55 @@
require_relative "../parser_helper"
class TestWhile < MiniTest::Test
# include the magic (setup and parse -> test method translation), see there
include ParserHelper
def test_while
@input = <<HERE
while(1) do
tmp = a
puts(b)
end
HERE
@input.chop!
@parse_output = {:while=>"while", :while_cond=>{:integer=>"1"}, :do=>"do", :body=>{:expressions=>[{:l=>{:name=>"tmp"}, :o=>"= ", :r=>{:name=>"a"}}, {:call_site=>{:name=>"puts"}, :argument_list=>[{:argument=>{:name=>"b"}}]}], :end=>"end"}}
@output = Ast::WhileExpression.new(Ast::IntegerExpression.new(1), [Ast::AssignmentExpression.new(Ast::NameExpression.new(:tmp),Ast::NameExpression.new(:a)), Ast::CallSiteExpression.new(:puts, [Ast::NameExpression.new(:b)] ,Ast::NameExpression.new(:self))] )
@root = :while_do
end
def pest_while_reverse
@input = "puts '1' while true "
@parse_output = {:while=>"while", :while_cond=>{:integer=>"1"}, :do=>"do", :body=>{:expressions=>[{:l=>{:name=>"tmp"}, :o=>"= ", :r=>{:name=>"a"}}, {:call_site=>{:name=>"puts"}, :argument_list=>[{:argument=>{:name=>"b"}}]}], :end=>"end"}}
@output = Ast::WhileExpression.new(Ast::IntegerExpression.new(1), [Ast::AssignmentExpression.new(Ast::NameExpression.new(:tmp),Ast::NameExpression.new(:a)), Ast::CallSiteExpression.new(:puts, [Ast::NameExpression.new(:b)] ,Ast::NameExpression.new(:self))] )
@root = :while_do
end
def test_while_method
@input = <<HERE
while(1) do
tmp = String.new()
tmp.puts(i)
end
HERE
@input.chop!
@parse_output = {:while=>"while", :while_cond=>{:integer=>"1"}, :do=>"do", :body=>{:expressions=>[{:l=>{:name=>"tmp"}, :o=>"= ", :r=>{:receiver=>{:module_name=>"String"}, :call_site=>{:name=>"new"}, :argument_list=>[]}}, {:receiver=>{:name=>"tmp"}, :call_site=>{:name=>"puts"}, :argument_list=>[{:argument=>{:name=>"i"}}]}], :end=>"end"}}
@output = Ast::WhileExpression.new(Ast::IntegerExpression.new(1), [Ast::AssignmentExpression.new(Ast::NameExpression.new(:tmp),Ast::CallSiteExpression.new(:new, [] ,Ast::ModuleName.new(:String))), Ast::CallSiteExpression.new(:puts, [Ast::NameExpression.new(:i)] ,Ast::NameExpression.new(:tmp))] )
@root = :while_do
end
def test_big_while
@input = <<HERE
while( n > 1) do
tmp = a
a = b
b = tmp + b
puts(b)
n = n - 1
end
HERE
@input.chop!
@parse_output = {:while=>"while", :while_cond=>{:l=>{:name=>"n"}, :o=>"> ", :r=>{:integer=>"1"}}, :do=>"do", :body=>{:expressions=>[{:l=>{:name=>"tmp"}, :o=>"= ", :r=>{:name=>"a"}}, {:l=>{:name=>"a"}, :o=>"= ", :r=>{:name=>"b"}}, {:l=>{:name=>"b"}, :o=>"= ", :r=>{:l=>{:name=>"tmp"}, :o=>"+ ", :r=>{:name=>"b"}}}, {:call_site=>{:name=>"puts"}, :argument_list=>[{:argument=>{:name=>"b"}}]}, {:l=>{:name=>"n"}, :o=>"= ", :r=>{:l=>{:name=>"n"}, :o=>"- ", :r=>{:integer=>"1"}}}], :end=>"end"}}
@output = Ast::WhileExpression.new(Ast::OperatorExpression.new(">", Ast::NameExpression.new(:n),Ast::IntegerExpression.new(1)), [Ast::AssignmentExpression.new(Ast::NameExpression.new(:tmp),Ast::NameExpression.new(:a)), Ast::AssignmentExpression.new(Ast::NameExpression.new(:a),Ast::NameExpression.new(:b)), Ast::AssignmentExpression.new(Ast::NameExpression.new(:b),Ast::OperatorExpression.new("+", Ast::NameExpression.new(:tmp),Ast::NameExpression.new(:b))), Ast::CallSiteExpression.new(:puts, [Ast::NameExpression.new(:b)] ,Ast::NameExpression.new(:self)), Ast::AssignmentExpression.new(Ast::NameExpression.new(:n),Ast::OperatorExpression.new("-", Ast::NameExpression.new(:n),Ast::IntegerExpression.new(1)))] )
@root = :while_do
end
end

View File

@ -1,36 +0,0 @@
require 'rubygems'
require 'bundler'
Bundler.setup(:default, :development)
require 'soml-parser'
require "parslet/convenience"
require "ast/sexp"
require "pp"
class Converter
include AST::Sexp
SEPERATOR = "-- -- --"
def to_ast
file = ARGV[0]
inn = File.new(file).read.split(SEPERATOR).first
begin
syntax = Parser::Salama.new.parse(inn)
rescue
root = file.split("/").last.split(".").first
parser = Parser::Salama.new.send root.to_sym
syntax = parser.parse_with_debug(inn )
end
result = Parser::Transform.new.apply(syntax)
out_file = File.new(file, "w")
out_file.puts inn
out_file.puts SEPERATOR
out_file.puts result.inspect
out_file.close
end
end
Converter.new.to_ast