sweet-lang 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/bin/sweet +2 -0
- data/lib/interpreter.rb +141 -0
- data/lib/lexer.rb +3 -0
- data/lib/lexer/core.rb +128 -0
- data/lib/lexer/strings.rb +13 -0
- data/lib/lexer/tags.rb +100 -0
- data/lib/nodes.rb +56 -0
- data/lib/parser.rb +673 -0
- data/lib/runtime.rb +7 -0
- data/lib/runtime/bootstrap.rb +41 -0
- data/lib/runtime/class.rb +43 -0
- data/lib/runtime/context.rb +24 -0
- data/lib/runtime/method.rb +19 -0
- data/lib/runtime/object.rb +18 -0
- data/lib/sweet.rb +28 -0
- metadata +64 -0
data/lib/runtime.rb
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
# Bootstrap the runtime. This is where we assemble all the classes and objects together
|
2
|
+
# to form the runtime.
|
3
|
+
# What's happening in the runtime:
|
4
|
+
sweet_class = SweetClass.new # Class
|
5
|
+
sweet_class.runtime_class = sweet_class # Class.class = Class
|
6
|
+
object_class = SweetClass.new # Object = Class.new
|
7
|
+
object_class.runtime_class = sweet_class # Object.class = Class
|
8
|
+
|
9
|
+
# Create the Runtime object (the root context) on which all code will start its
|
10
|
+
# evaluation.
|
11
|
+
Runtime = Context.new(object_class.new)
|
12
|
+
|
13
|
+
Runtime["Class"] = sweet_class
|
14
|
+
Runtime["Object"] = object_class
|
15
|
+
Runtime["Number"] = SweetClass.new
|
16
|
+
Runtime["String"] = SweetClass.new
|
17
|
+
|
18
|
+
# Everything is an object in our language, even true, false and nil. So they need
|
19
|
+
# to have a class too.
|
20
|
+
Runtime["TrueClass"] = SweetClass.new
|
21
|
+
Runtime["FalseClass"] = SweetClass.new
|
22
|
+
Runtime["NilClass"] = SweetClass.new
|
23
|
+
|
24
|
+
Runtime["true"] = Runtime["TrueClass"].new_with_value(true)
|
25
|
+
Runtime["false"] = Runtime["FalseClass"].new_with_value(false)
|
26
|
+
Runtime["nil"] = Runtime["NilClass"].new_with_value(nil)
|
27
|
+
|
28
|
+
# Add a few core methods to the runtime.
|
29
|
+
|
30
|
+
# Add the `new` method to classes, used to instantiate a class:
|
31
|
+
# eg.: Object.new, Number.new, String.new, etc.
|
32
|
+
Runtime["Class"].runtime_methods["new"] = proc do |receiver, arguments|
|
33
|
+
receiver.new
|
34
|
+
end
|
35
|
+
|
36
|
+
# Print an object to the console.
|
37
|
+
# eg.: print("hi there!")
|
38
|
+
Runtime["Object"].runtime_methods["print"] = proc do |receiver, arguments|
|
39
|
+
@code << arguments.first.ruby_value
|
40
|
+
Runtime["nil"]
|
41
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# Represents a Sweet class in the Ruby world. Classes are objects in Sweet so they
|
2
|
+
# inherit from SweetObject.
|
3
|
+
class SweetClass < SweetObject
|
4
|
+
attr_reader :runtime_methods
|
5
|
+
|
6
|
+
# Creates a new class. Number is an instance of Class for example.
|
7
|
+
def initialize
|
8
|
+
@runtime_methods = {}
|
9
|
+
|
10
|
+
# Check if we're bootstrapping (launching the runtime). During this process the
|
11
|
+
# runtime is not fully initialized and core classes do not yet exists, so we defer
|
12
|
+
# using those once the language is bootstrapped.
|
13
|
+
# This solves the chicken-or-the-egg problem with the Class class. We can
|
14
|
+
# initialize Class then set Class.class = Class.
|
15
|
+
if defined?(Runtime)
|
16
|
+
runtime_class = Runtime["Class"]
|
17
|
+
else
|
18
|
+
runtime_class = nil
|
19
|
+
end
|
20
|
+
|
21
|
+
super(runtime_class)
|
22
|
+
end
|
23
|
+
|
24
|
+
# Lookup a method
|
25
|
+
def lookup(method_name)
|
26
|
+
method = @runtime_methods[method_name]
|
27
|
+
unless method
|
28
|
+
raise "Method not found: #{method_name}"
|
29
|
+
end
|
30
|
+
method
|
31
|
+
end
|
32
|
+
|
33
|
+
# Create a new instance of this class
|
34
|
+
def new
|
35
|
+
SweetObject.new(self)
|
36
|
+
end
|
37
|
+
|
38
|
+
# Create an instance of this Sweet class that holds a Ruby value. Like a String,
|
39
|
+
# Number or true.
|
40
|
+
def new_with_value(value)
|
41
|
+
SweetObject.new(self, value)
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# The evaluation context.
|
2
|
+
class Context
|
3
|
+
attr_reader :locals, :current_self, :current_class
|
4
|
+
|
5
|
+
# We store constants as class variable (class variables start with @@ and instance
|
6
|
+
# variables start with @ in Ruby) since they are globally accessible. If you want to
|
7
|
+
# implement namespacing of constants, you could store it in the instance of this
|
8
|
+
# class.
|
9
|
+
@@constants = {}
|
10
|
+
|
11
|
+
def initialize(current_self, current_class=current_self.runtime_class)
|
12
|
+
@locals = {}
|
13
|
+
@current_self = current_self
|
14
|
+
@current_class = current_class
|
15
|
+
end
|
16
|
+
|
17
|
+
# Shortcuts to access constants, Runtime[...] instead of Runtime.constants[...]
|
18
|
+
def [](name)
|
19
|
+
@@constants[name]
|
20
|
+
end
|
21
|
+
def []=(name, value)
|
22
|
+
@@constants[name] = value
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# Represents a method defined in the runtime.
|
2
|
+
class SweetMethod
|
3
|
+
def initialize(params, body)
|
4
|
+
@params = params
|
5
|
+
@body = body
|
6
|
+
end
|
7
|
+
|
8
|
+
def call(receiver, arguments)
|
9
|
+
# Create a context of evaluation in which the method will execute.
|
10
|
+
context = Context.new(receiver)
|
11
|
+
|
12
|
+
# Assign arguments to local variables
|
13
|
+
@params.each_with_index do |param, index|
|
14
|
+
context.locals[param] = arguments[index]
|
15
|
+
end
|
16
|
+
|
17
|
+
@body.eval(context)
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# Represents an Sweet object instance in the Ruby world.
|
2
|
+
class SweetObject
|
3
|
+
attr_accessor :runtime_class, :ruby_value
|
4
|
+
|
5
|
+
# Each object have a class (named runtime_class to prevent errors with Ruby's class
|
6
|
+
# method). Optionaly an object can hold a Ruby value (eg.: numbers and strings).
|
7
|
+
def initialize(runtime_class, ruby_value=self)
|
8
|
+
@runtime_class = runtime_class
|
9
|
+
@ruby_value = ruby_value
|
10
|
+
end
|
11
|
+
|
12
|
+
# Call a method on the object.
|
13
|
+
def call(method, arguments=[])
|
14
|
+
# Like a typical Class-based runtime model, we store methods in the class of the
|
15
|
+
# object.
|
16
|
+
@runtime_class.lookup(method).call(self, arguments)
|
17
|
+
end
|
18
|
+
end
|
data/lib/sweet.rb
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "interpreter"
|
4
|
+
require "readline"
|
5
|
+
|
6
|
+
interpreter = Interpreter.new
|
7
|
+
|
8
|
+
# If a file is given we eval it.
|
9
|
+
if file = ARGV.first
|
10
|
+
@filename = ARGV.first.split(".")[0]
|
11
|
+
File.new("#{@filename}.html", "w")
|
12
|
+
interpreter.eval File.read(file)
|
13
|
+
File.open("#{@filename}.html", 'a') do |f|
|
14
|
+
f.puts @code.join("\n")
|
15
|
+
end
|
16
|
+
puts "Sweet code compiled!"
|
17
|
+
|
18
|
+
# Start the REPL, read-eval-print-loop, or interactive interpreter
|
19
|
+
else
|
20
|
+
puts "Sweet Interactive Console, CTRL+C to quit"
|
21
|
+
loop do
|
22
|
+
line = Readline::readline(">> ")
|
23
|
+
Readline::HISTORY.push(line)
|
24
|
+
value = interpreter.eval(line)
|
25
|
+
puts "=> #{value.ruby_value.inspect}"
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
metadata
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: sweet-lang
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- João Moura
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2011-12-13 00:00:00.000000000Z
|
13
|
+
dependencies: []
|
14
|
+
description: ! "Sweet is a pre-compiled language based on Ruby and Python. \n Sweet
|
15
|
+
makes possible to use all the class, defs, conditionals, global and \n local
|
16
|
+
variables power of server-side languages, defining blocks by indentation \n like
|
17
|
+
Python in your Sweet HTML markup.\n It comes with a SIC ( Sweet Interactive Console
|
18
|
+
) the you can test your code and ideas\n before really right and compile it."
|
19
|
+
email: joaomdmoura@gmail.com
|
20
|
+
executables:
|
21
|
+
- sweet
|
22
|
+
extensions: []
|
23
|
+
extra_rdoc_files: []
|
24
|
+
files:
|
25
|
+
- lib/sweet.rb
|
26
|
+
- lib/interpreter.rb
|
27
|
+
- lib/lexer.rb
|
28
|
+
- lib/nodes.rb
|
29
|
+
- lib/parser.rb
|
30
|
+
- lib/runtime.rb
|
31
|
+
- lib/lexer/core.rb
|
32
|
+
- lib/lexer/strings.rb
|
33
|
+
- lib/lexer/tags.rb
|
34
|
+
- lib/runtime/bootstrap.rb
|
35
|
+
- lib/runtime/class.rb
|
36
|
+
- lib/runtime/context.rb
|
37
|
+
- lib/runtime/method.rb
|
38
|
+
- lib/runtime/object.rb
|
39
|
+
- bin/sweet
|
40
|
+
homepage: https://github.com/joaomdmoura/sweet
|
41
|
+
licenses: []
|
42
|
+
post_install_message:
|
43
|
+
rdoc_options: []
|
44
|
+
require_paths:
|
45
|
+
- lib
|
46
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
47
|
+
none: false
|
48
|
+
requirements:
|
49
|
+
- - ! '>='
|
50
|
+
- !ruby/object:Gem::Version
|
51
|
+
version: '0'
|
52
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
53
|
+
none: false
|
54
|
+
requirements:
|
55
|
+
- - ! '>='
|
56
|
+
- !ruby/object:Gem::Version
|
57
|
+
version: '0'
|
58
|
+
requirements: []
|
59
|
+
rubyforge_project:
|
60
|
+
rubygems_version: 1.8.11
|
61
|
+
signing_key:
|
62
|
+
specification_version: 3
|
63
|
+
summary: A Sweet language, that compiles for HTML
|
64
|
+
test_files: []
|