Skip to content
This repository has been archived by the owner on Dec 24, 2023. It is now read-only.

Commit

Permalink
Added construction, removed functions, replaced with closures
Browse files Browse the repository at this point in the history
  • Loading branch information
deathbeam committed May 13, 2016
1 parent 47dc26e commit 54ac494
Show file tree
Hide file tree
Showing 6 changed files with 90 additions and 123 deletions.
3 changes: 2 additions & 1 deletion examples/compilertest.spoon
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,15 @@ a = 15 + 8 * 6 / 7 + (8 - 7)
test = (a = 5) ->
print "Jajaja"

function log-message message = "hello"
log-message = (message = "hello") ->
message = "bb"
a = 25
b = 10
print message
return a, b

b = 15
test = new TestClass!

log-message "Hello World"

Expand Down
2 changes: 1 addition & 1 deletion examples/test.spoon
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
# Above is just for nice syntax highlighting
# as Spoon syntax is very similar to CoffeeScript

function log message
log = (message) ->
if message
print "[INFO] #{@message}"
else
Expand Down
109 changes: 47 additions & 62 deletions lib/spoon/compiler.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,14 @@ def initialize(path = "main")
@nodes = {
:root => Root,
:block => Block,
:function => Function,
:closure => Closure,
:if => If,
:for => For,
:while => While,
:assign => Assign,
:op => Operation,
:call => Call,
:new => New,
:return => Return,
:import => Import,
:param => Param,
Expand Down Expand Up @@ -141,6 +142,41 @@ def compile
end
end

class Assign < Base
def compile
children = @node.children.dup

@content << "(" if @parent.node.type == :op

left = children.shift
content = compile_next(left)

if left.type == :value
if @compiler.scope.push content
@content << "var "
end
elsif left.type == :self || left.type == :this
name = compile_next(left.children.first)

if left.type == :self
raise ArgumentError, 'Self call cannot be used outside of class' unless @compiler.in_class
@compiler.class_scope.push name
elsif left.type == :this
if @compiler.in_class
@compiler.instance_scope.push name
else
@compiler.class_scope.push name
end
end
end

@content << content << " = " << compile_next(children.shift)
@content << ")" if @parent.node.type == :op

super
end
end

class Operation < Base
def compile
children = @node.children.dup
Expand All @@ -150,31 +186,7 @@ def compile

case @node.option :operation
when :infix
left = children.shift
content = compile_next(left)

if operator == "="
if left.type == :value
if @compiler.scope.push content
@content << "var "
end
elsif left.type == :self || left.type == :this
name = compile_next(left.children.first)

if left.type == :self
raise ArgumentError, 'Self call cannot be used outside of class' unless @compiler.in_class
@compiler.class_scope.push name
elsif left.type == :this
if @compiler.in_class
@compiler.instance_scope.push name
else
@compiler.class_scope.push name
end
end
end
end

@content << content
@content << compile_next(children.shift)
@content << " #{operator} "
@content << compile_next(children.shift)
when :prefix
Expand All @@ -191,6 +203,14 @@ def compile
end
end

class New < Base
def compile
@content << "new "
@content << compile_next(@node.children.dup.shift)
super
end
end

class This < Base
def compile
child = @node.children.dup.shift
Expand Down Expand Up @@ -240,7 +260,7 @@ def compile
class Call < Base
def compile
children = @node.children.dup
@content << compile_str(children.shift.to_s)
@content << compile_next(children.shift)
@content << "("

children.each do |child|
Expand Down Expand Up @@ -268,41 +288,6 @@ def compile
end
end

class Function < Base
def compile
@compiler.scope.add

children = @node.children.dup
first = children.shift

if first.is_a?(String) || first.is_a?(Fixnum) || [true, false].include?(first)
name = compile_str(first.to_s)
@content << "function #{name}("
elsif first.type == :self
name = compile_next(first.children.first)
@content << "static function #{name}("
end

if children.length > 1
children.each do |child|
name = child.children.first.to_s
@compiler.scope.push name

unless child == children.last
@content << compile_next(child)
@content << ", " unless child == children[children.length - 2]
end
end
end

@content << ") "
@content << compile_next(children.last)

@compiler.scope.pop
super
end
end

class Closure < Base
def compile
@compiler.scope.add
Expand Down
51 changes: 21 additions & 30 deletions lib/spoon/parser.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ def initialize
@keywords = [
:if,
:else,
:function,
:return,
:and,
:is,
Expand All @@ -26,7 +25,8 @@ def initialize
:unless,
:do,
:while,
:import
:import,
:new
]
end

Expand Down Expand Up @@ -68,7 +68,7 @@ def initialize
# TODO: Add decorators (postfix if, unless, for and while)
rule(:expression) {
space.maybe >>
(statement | operation | value) >>
(assign | operation | value) >>
endline.maybe
}

Expand All @@ -92,6 +92,20 @@ def initialize
)
}

# Matches object construction
# example: new Foo!
rule(:construct) {
NEW() >> whitespace.maybe >> call.as(:construct)
}

# Matches assign
# example: foo = bar
rule(:assign) {
(
parens(value).as(:l) >> ASSIGN() >> expression.as(:r)
).as(:assign)
}

# Matches unary operation
# example: !foo
# TODO: Enable spaces between operator and value
Expand All @@ -107,6 +121,7 @@ def initialize
for_loop |
while_loop |
closure |
construct |
call |
import |
ret |
Expand All @@ -116,12 +131,6 @@ def initialize
word.as(:identifier)
}

# Matches statement, so everything that is unassignable
# TODO: Add classes, interfaces and so
rule(:statement) {
function
}

rule(:self_call) {
str('@@') >> value.as(:self)
}
Expand Down Expand Up @@ -199,7 +208,9 @@ def initialize
# Matches function call
# example: a(b, c, d, e, f)
rule(:call) {
(word.as(:name) >> space.maybe >> (EXCLAMATION() | argument_list.as(:args))).as(:call)
(
(word.as(:identifier) | self_call | this_call).as(:name) >>
space.maybe >> (EXCLAMATION() | argument_list.as(:args))).as(:call)
}

# Matches function parameter
Expand Down Expand Up @@ -231,26 +242,6 @@ def initialize
).as(:closure)
}

# Matches function definition
# example: def (a) b
rule(:function) {
(
FUNCTION() >>
space.maybe >>
word.as(:name) >>
function_body
).as(:function)
}

# Matches function body
rule(:function_body) {
(
space.maybe >>
parameter_list.as(:params).maybe >>
body.as(:body)
) | body.as(:body)
}

# Matches for loop
rule(:for_loop) {
(
Expand Down
42 changes: 15 additions & 27 deletions lib/spoon/transformer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,13 @@ class Transformer < Parslet::Transform
AST::Node.new :param, [ name.to_v, value]
}

rule(:assign => {
:l => simple(:left),
:r => simple(:right)
}) {
AST::Node.new :assign, [ left, right ]
}

rule(:l => simple(:left), :o => simple(:op), :r => simple(:right)) {
AST::Node.new :op, [ op.to_op, left, right ], :operation => :infix
}
Expand All @@ -80,8 +87,8 @@ class Transformer < Parslet::Transform
AST::Node.new :op, [ op.to_op, right ], :operation => :prefix
}

rule(:call => { :name => simple(:name) }) {
AST::Node.new :call, [ name.to_v ]
rule(:construct => simple(:object)) {
AST::Node.new :new, [ object ]
}

rule(:return => simple(:args)) {
Expand All @@ -96,18 +103,22 @@ class Transformer < Parslet::Transform
AST::Node.new :return, args
}

rule(:call => { :name => simple(:name) }) {
AST::Node.new :call, [ name ]
}

rule(:call => {
:name => simple(:name),
:args => simple(:args)
}) {
AST::Node.new :call, [ name.to_v, args ]
AST::Node.new :call, [ name, args ]
}

rule(:call => {
:name => simple(:name),
:args => sequence(:args)
}) {
AST::Node.new :call, [ name.to_v ] + args
AST::Node.new :call, [ name ] + args
}

rule(:closure => {
Expand All @@ -130,29 +141,6 @@ class Transformer < Parslet::Transform
AST::Node.new :closure, params + [ body ]
}

rule(:function => {
:name => simple(:name),
:body => simple(:body)
}) {
AST::Node.new :function, [ name.to_v, body ]
}

rule(:function => {
:name => simple(:name),
:params => simple(:params),
:body => simple(:body)
}) {
AST::Node.new :function, [ name.to_v , params, body ]
}

rule(:function => {
:name => simple(:name),
:params => sequence(:params),
:body => simple(:body)
}) {
AST::Node.new :function, [ name.to_v ] + params + [ body ]
}

rule(:if => {
:condition => simple(:condition),
:true => simple(:if_true)
Expand Down
6 changes: 4 additions & 2 deletions lib/spoon/util/parser_literals.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,6 @@ class Spoon::Util::IndentParser

rule(:FOR) { key :for }

rule(:FUNCTION) { key :function }

rule(:IF) { key :if }

rule(:IMPORT) { key :import }
Expand All @@ -30,6 +28,8 @@ class Spoon::Util::IndentParser

rule(:WHILE) { key :while }

rule(:NEW) { key :new }

####################################
# Special characters
####################################
Expand All @@ -40,6 +40,8 @@ class Spoon::Util::IndentParser

rule(:HASH) { str "#" }

rule(:DOUBLE_DOT) { str ":" }

####################################
# Operators
####################################
Expand Down

0 comments on commit 54ac494

Please sign in to comment.