Permalink
Browse files

first commit. what an afternoonn

  • Loading branch information...
Josep M. Bach
Josep M. Bach committed Sep 11, 2011
0 parents commit add31a8b5a615376c90c5bc38fd356908b30a447
@@ -0,0 +1,4 @@
+*.gem
+.bundle
+Gemfile.lock
+pkg/*
@@ -0,0 +1,4 @@
+source "http://rubygems.org"
+
+# Specify your gem's dependencies in noscript.gemspec
+gemspec
@@ -0,0 +1,21 @@
+# encoding: utf-8
+require 'bundler'
+Bundler::GemHelper.install_tasks
+
+desc "Regenerate Noscript's lexer and parser."
+task :regenerate do
+ has_rex = `which rex`
+ has_racc = `which racc`
+
+ if has_rex && has_racc
+ `rex lib/noscript/parser/noscript.rex`
+ `racc lib/noscript/parser/noscript.racc`
+ else
+ puts "You need both Rexical and Racc to do that. Install them by doing:"
+ puts
+ puts "\t\tgem install rexical"
+ puts "\t\tgem install racc"
+ puts
+ puts "Or just type `bundle install`."
+ end
+end
@@ -0,0 +1,23 @@
+# noscript
+
+Noscript is an object-oriented scripting language written in pure Ruby.
+
+It's basically a way for me to learn about language design in a practical way.
+In the future this language will run on the [Rubinius VM](http://rubini.us),
+but for now I prefer to deal as much as possible with implementation detail,
+learn as much as I can, and then go for the kick-ass Rubinius VM :)
+
+Feel free to criticize and give advice, I'm happy to hear it!
+
+## Install
+
+Although for now it's in a **SUPER ALPHA** stage, you can try and run some
+example scripts doing this:
+
+ git clone git://github.com/txus/noscript
+ cd noscript
+ ./bin/noscript examples/hello_world.ns
+
+## Copyright
+
+Copyright (c) 2011 Josep M. Bach. See LICENSE for details.
@@ -0,0 +1,44 @@
+#!/usr/bin/env ruby
+
+begin
+ require 'noscript'
+rescue LoadError
+ require_relative '../lib/noscript'
+end
+
+require 'pp'
+
+parser = Noscript::Parser.new
+
+filename = ARGV.shift
+if filename && code = File.read(filename)
+ code.strip!
+else
+ puts "No input file provided. Bye!"
+ exit(1)
+end
+
+args = ARGV
+
+ast = parser.scan_str(code)
+
+if args.any?{|a| a =~ '--tokens'}
+ puts "-----TOKENS-----"
+ lexer = Noscript::Parser.new
+ lexer.load_file filename
+ while token = rex.next_token
+ p token
+ end
+end
+
+if args.any?{|a| a =~ '--ast'}
+ puts "-----AST-----"
+ pp ast
+end
+
+result = ast.compile(Noscript::Context.generate)
+
+if args.any?{|a| a =~ '--return'}
+ puts "-----RETURN VALUE-----"
+ pp result
+end
@@ -0,0 +1,22 @@
+myvar = 'hello world'
+johnny = 4000
+
+def foo(johnny=3)
+ 34 + johnny * 3
+end
+
+def bar(johnny=3)
+ (34 + johnny) * 3
+end
+
+print('Negative johnny is', -johnny)
+
+print(foo())
+print(foo(242))
+
+print(bar())
+print(bar(242))
+
+print('Johnny is still', johnny)
+
+print(myvar)
@@ -0,0 +1,8 @@
+require_relative 'noscript/ast'
+require_relative 'noscript/parser'
+
+require_relative 'noscript/context'
+require_relative 'noscript/method'
+
+module Noscript
+end
@@ -0,0 +1,127 @@
+module Noscript
+ module AST
+ class Nodes < Struct.new(:nodes)
+ def compile(context)
+ nodes.map do |node|
+ node.compile(context)
+ end.last
+ end
+ end
+
+ class AssignNode < Struct.new(:lhs, :rhs)
+ def compile(context)
+ context.store_var(lhs, rhs)
+ rhs
+ end
+ end
+
+ class Identifier < Struct.new(:name)
+ def compile(context)
+ context.lookup_var(name) || raise("Undefined local variable: #{name}")
+ end
+ def to_s
+ name
+ end
+ end
+
+ class DefaultParameter < Struct.new(:name, :value)
+ def compile(context)
+ value
+ end
+ def to_s
+ name
+ end
+ end
+
+ class String < Struct.new(:val)
+ def compile(context)
+ val.to_s
+ end
+ def to_s
+ "'#{val.to_s}'"
+ end
+ end
+
+ class DefMethod < Struct.new(:name, :params, :body)
+ def compile(context)
+ context.store_method(name, params, body)
+ end
+ end
+
+ class MethodCall < Struct.new(:name, :args)
+ def compile(context)
+ if method = context.lookup_method(name)
+ method.call(context, *args)
+ else
+ raise "Undefined procedure: #{name}"
+ end
+ end
+ end
+
+ ## ARITHMETIC
+
+ class Digit < Struct.new(:val)
+ def -@
+ Digit.new(-to_i)
+ end
+
+ def +(num)
+ Digit.new num.to_i + to_i
+ end
+
+ def -(num)
+ Digit.new to_i - num.to_i
+ end
+
+ def *(num)
+ Digit.new to_i * num.to_i
+ end
+
+ def /(num)
+ Digit.new to_i / num.to_i
+ end
+
+ def compile(context)
+ self
+ end
+
+ def to_s
+ val.to_s
+ end
+
+ def to_i
+ val.to_i
+ end
+ end
+
+ class AddNode < Struct.new(:lhs, :rhs)
+ def compile(context)
+ lhs.compile(context) + rhs.compile(context)
+ end
+ end
+
+ class SubtractNode < Struct.new(:lhs, :rhs)
+ def compile(context)
+ lhs.compile(context) - rhs.compile(context)
+ end
+ end
+
+ class MultiplicationNode < Struct.new(:lhs, :rhs)
+ def compile(context)
+ lhs.compile(context) * rhs.compile(context)
+ end
+ end
+
+ class DivisionNode < Struct.new(:lhs, :rhs)
+ def compile(context)
+ lhs.compile(context) / rhs.compile(context)
+ end
+ end
+
+ class UnaryMinus < Struct.new(:val)
+ def compile(context)
+ -(val.compile(context))
+ end
+ end
+ end
+end
@@ -0,0 +1,47 @@
+module Noscript
+ class Context
+ def self.generate
+ ctx = new
+
+ # Define native ruby methods
+ ctx.store_ruby_method('print') do |context, *args|
+ puts *(args.map! {|a| a.compile(context).to_s })
+ end
+
+ ctx
+ end
+
+ attr_accessor :lvars, :functions
+
+ def initialize(parent_context = nil)
+ @parent = parent_context
+ @lvars = {}
+ @functions = {}
+ end
+
+ def lookup_var(symbol)
+ result = @lvars[symbol.to_s] ||
+ (@parent.lookup_var(symbol.to_s) if @parent)
+ return result || raise("Undefined local variable: #{symbol}")
+ end
+
+ def store_var(symbol, value)
+ @lvars[symbol.to_s] = value
+ end
+
+ def lookup_method(symbol)
+ result = @functions[symbol.to_s] ||
+ (@parent.lookup_method(symbol.to_s) if @parent)
+ return result || raise("Undefined procedure: #{symbol}")
+ end
+
+ def store_method(symbol, params, body)
+ @functions[symbol.to_s] = Method.new(params, body)
+ end
+
+ def store_ruby_method(symbol, &body)
+ raise "Body must be a ruby proc" unless body.is_a?(Proc)
+ @functions[symbol.to_s] = body
+ end
+ end
+end
@@ -0,0 +1,22 @@
+module Noscript
+ class Method
+ def initialize(params, body)
+ @params = params
+ @body = body
+ end
+
+ def call(context, *args)
+ ctx = Context.new(context)
+ @params.each_with_index do |param, idx|
+ if passed_value = args[idx]
+ ctx.store_var(param.name, passed_value.compile(ctx))
+ elsif param.is_a?(AST::DefaultParameter)
+ ctx.store_var(param.name, param.value)
+ else
+ raise "This method expected #{@params.size} arguments, not #{args.size}"
+ end
+ end
+ @body.compile(ctx)
+ end
+ end
+end
@@ -0,0 +1 @@
+require_relative 'parser/noscript.tab'
Oops, something went wrong.

0 comments on commit add31a8

Please sign in to comment.