copied everything over from 2012 and removed all of the actual robot code except the drivetrain stuff


git-svn-id: https://robotics.mvla.net/svn/frc971/2013/trunk/src@4078 f308d9b7-e957-4cde-b6ac-9a88185e7312
diff --git a/aos/build/queues/objects/errors.rb b/aos/build/queues/objects/errors.rb
new file mode 100644
index 0000000..d776a22
--- /dev/null
+++ b/aos/build/queues/objects/errors.rb
@@ -0,0 +1,43 @@
+class QError < Exception
+	def initialize(msg)
+		super()
+		@msg = msg
+		@qstacktrace = []
+	end
+	def self.set_name(name)
+		@pretty_name = name
+	end
+	def self.pretty_name()
+		@pretty_name
+	end
+	def to_s
+		msg = "Error:(#{self.class.pretty_name})\n\t"
+		msg += @msg
+		msg += "\n" if(msg[-1] != "\n")
+		@qstacktrace.each do |part|
+			part = part.q_stack_name if(part.respond_to?(:q_stack_name))
+			msg += "\tfrom: #{part}\n"
+		end
+		return msg
+	end
+	set_name("Base Level Exception.")
+	attr_accessor :qstacktrace
+end
+class QSyntaxError < QError
+	def initialize(msg)
+		super(msg)
+	end
+	set_name("Syntax Error")
+end
+class QNamespaceCollision < QError
+	def initialize(msg)
+		super(msg)
+	end
+	set_name("Namespace Collision")
+end
+class QImportNotFoundError < QError
+	def initialize(msg)
+		super(msg)
+	end
+	set_name("Couldn't Find Target of Import Statement")
+end
diff --git a/aos/build/queues/objects/interface.rb b/aos/build/queues/objects/interface.rb
new file mode 100644
index 0000000..52332b6
--- /dev/null
+++ b/aos/build/queues/objects/interface.rb
@@ -0,0 +1,69 @@
+class MessageElementReq
+	def initialize(type,name)
+		@type = type
+		@name = name
+	end
+	def self.parse(tokens)
+		type = tokens.expect(:tWord).data
+		name = tokens.expect(:tWord).data
+		tokens.expect(:tSemi)
+		return self.new(type,name)
+	end 
+end 
+class QueueReq
+	def initialize(name,type = nil)
+		@name = name
+		@type = type
+	end
+	def self.parse(tokens)
+		type_or_name = tokens.expect(:tWord).data
+		if(tokens.peak == :tSemi)
+			tokens.expect(:tSemi)
+			return self.new(type_or_name)
+		else
+			name = tokens.expect(:tWord).data
+			tokens.expect(:tSemi)
+			return self.new(name,type_or_name)
+		end
+	end
+end
+class InterfaceStmt < QStmt
+	def initialize(name,elements)
+		@name = name
+		@elements = elements
+	end
+	def q_eval(locals)
+
+	end
+	def self.check_type(tokens,new_type,old_type)
+		return new_type if(old_type == nil)
+		if(new_type != old_type)
+			tokens.qError(<<ERROR_MSG)
+error: intermixing queue definitions (a queue_group feature)
+	and type definitions (a message_group feature)
+	this results in an undefined type value.
+	Wot. Wot.
+ERROR_MSG
+		end
+		return old_type
+	end
+	def self.parse(tokens)
+		name = tokens.expect(:tWord).data
+		values = []
+		type = nil
+		tokens.expect(:tOpenB)
+		while(tokens.peak != :tCloseB)
+			if(tokens.peak.data == "queue")
+				tokens.expect(:tWord)
+				values << QueueReq.parse(tokens)
+				type = check_type(tokens,:queue_group,type)
+			else
+				values << MessageElementReq.parse(tokens)
+				type = check_type(tokens,:message_group,type)
+			end
+		end 
+		tokens.expect(:tCloseB)
+		tokens.expect(:tSemi)
+		self.new(name,values)
+	end
+end
diff --git a/aos/build/queues/objects/namespaces.rb b/aos/build/queues/objects/namespaces.rb
new file mode 100644
index 0000000..b799d36
--- /dev/null
+++ b/aos/build/queues/objects/namespaces.rb
@@ -0,0 +1,210 @@
+class LocalSituation
+	attr_accessor :globals,:local
+	def initialize(globals)
+		@globals = globals
+		@local = nil
+	end
+	def package=(qualified)
+		if(@local)
+			raise QSyntaxError.new(<<ERROR_MSG)
+You are redefining the package path.
+ Stuff might break if you do that.
+ Other options include: using another header file, and just using the same namespace.
+ If you are confident that you need this you can remove this check at
+ 	#{__FILE__}:#{__LINE__}.
+ Or file a feature request.
+ But that would be weird...
+ Wot. Wot.
+ERROR_MSG
+		end
+		@local = @globals.space
+		qualified.names.each do |name|
+			@local = @local.get_make(name)
+		end
+	end
+	def register(value)
+		if(!@local)
+			raise QError.new(<<ERROR_MSG)
+There is no package path defined, This is a big problem because
+ we are kindof expecting you to have a package path...
+ use a :
+    package my_super.cool.project;
+ statement to remedy this situation. (or file a feature request)
+ Wot. Wot.
+ERROR_MSG
+		end
+		@local[value.name] = value
+		value.parent = @local if(value.respond_to?(:parent=))
+		value.loc = @local
+	end
+	def bind(bind_to)
+		return BoundSituation.new(self,bind_to)
+	end
+end
+class BoundSituation < LocalSituation
+	def initialize(locals,bind_to)
+		@globals = globals
+		@local = bind_to
+	end
+end
+class NameSpace
+	attr_accessor :parent,:name
+	def initialize(name = nil,parent = nil)
+		@name = name
+		@parent = parent
+		@spaces = {}
+	end
+	def []=(key,val)
+		if(old_val = @spaces[key])
+			old_val = old_val.created_by if(old_val.respond_to?(:created_by) && old_val.created_by)
+			if(old_val.respond_to?(:q_stack_name))
+				old_val = old_val.q_stack_name
+			else
+				old_val = "eh, it is a #{old_val.class} thats all I know..."
+			end
+			raise QNamespaceCollision.new(<<ERROR_MSG)
+Woah! The name #{queue_name(key).inspect} is already taken by some chap at #{old_val}.
+\tFind somewhere else to peddle your wares.
+\tWot. Wot.
+ERROR_MSG
+		end
+		@spaces[key] = val
+	end
+	def to_cpp_id(name)
+		txt = @name + "::" + name
+		return @parent.to_cpp_id(txt) if(@parent && parent.name)
+		return "::" + txt
+	end
+	def queue_name(queue)
+		get_name() + "." + queue
+	end
+	def [](key)
+		#puts "getting #{get_name}.#{key}"
+		@spaces[key]
+	end
+	def get_make(name)
+		@spaces[name] ||= self.class.new(name,self)
+	end
+	def get_name()
+		if(@parent)
+			return "#{@parent.get_name}.#{@name}"
+		else
+			return "#{@name}"
+		end
+	end
+	def create(cpp_tree)
+		if(@parent && @parent.name)
+			parent = cpp_tree.get(@parent)
+		else
+			parent = cpp_tree
+		end
+		return cpp_tree.add_namespace(@name,parent)
+	end
+	def to_s()
+		"<NameSpace: #{get_name()}>"
+	end
+	def inspect()
+		"<NameSpace: #{get_name()}>"
+	end
+	def root()
+		return self if(@parent == nil)
+		return @parent.root
+	end
+end
+class Globals
+	attr_accessor :space
+	def initialize()
+		@space = NameSpace.new()
+		@space.get_make("aos")
+		@include_paths = []
+	end
+	def paths()
+		@include_paths
+	end
+	def add_path(path)
+		@include_paths << path
+	end
+	def find_file(filename)
+		@include_paths.each do |path_name|
+			new_path = File.expand_path(path_name) + "/" + filename
+			if(File.exists?(new_path))
+				return new_path 
+			end
+		end
+		raise QImportNotFoundError.new(<<ERROR_MSG)
+Problem Loading:#{filename.inspect} I looked in:
+\t#{(@include_paths.collect {|name| name.inspect}).join("\n\t")}
+\tbut alas, it was nowhere to be found.
+\tIt is popular to include the top of the repository as the include path start, 
+\tand then reference off of that. 
+\tI would suggest doing that and then trying to build again.
+\tWot. Wot.
+ERROR_MSG
+	end
+end
+class QualifiedName
+	attr_accessor :names,:off_root
+	def initialize(names,off_root = false)
+		@names = names
+		@off_root = off_root
+	end
+	def test_lookup(namespace)
+		@names.each do |name|
+			namespace = namespace[name]
+			return nil if(!namespace)
+		end
+		return namespace
+	end
+	def is_simple?()
+		return !@off_root && @names.length == 1
+	end
+	def to_simple()
+		return @names[-1]
+	end
+	def to_s()
+		if(@off_root)
+			return ".#{@names.join(".")}"
+		else
+			return @names.join(".")
+		end
+	end
+	def lookup(locals)
+		if(@off_root)
+			local = locals.globals.space
+		else
+			local = locals.local
+		end
+		target = nil
+		while(!target && local)
+			target = test_lookup(local)
+			local = local.parent
+		end
+		return target if(target)
+		if(@off_root)
+			raise QError.new(<<ERROR_MSG)
+I was looking for .#{@names.join(".")}, but alas, it was not under
+\tthe root namespace.
+\tI'm really sorry old chap.
+\tWot. Wot.
+ERROR_MSG
+		else
+			raise QError.new(<<ERROR_MSG)
+I was looking for #{@names.join(".")}, but alas, I could not find
+\tit in #{locals.local.get_name} or any parent namespaces.
+\tI'm really sorry old chap.
+\tWot. Wot.
+ERROR_MSG
+		end
+	end
+	def self.parse(tokens)
+		names = []
+		off_root = (tokens.peak == :tDot)
+		tokens.next if(off_root)
+		names << tokens.expect(:tWord).data
+		while(tokens.peak == :tDot)
+			tokens.next
+			names << tokens.expect(:tWord).data
+		end
+		return self.new(names,off_root)
+	end
+end
diff --git a/aos/build/queues/objects/q_file.rb b/aos/build/queues/objects/q_file.rb
new file mode 100644
index 0000000..f683dda
--- /dev/null
+++ b/aos/build/queues/objects/q_file.rb
@@ -0,0 +1,148 @@
+class QStmt
+	def set_line(val)
+		@file_line = val
+		return self
+	end
+	def q_stack_name()
+		@file_line
+	end
+	def self.method_added(name)
+		@wrapped ||= {}
+		return if(name != :q_eval && name != :q_eval_extern)
+		return if(@wrapped[name])
+		@wrapped[name] = true
+		method = self.instance_method(name)
+		define_method(name) do |*args,&blk|
+			begin 
+				method.bind(self).call(*args,&blk)
+			rescue QError => e
+				e.qstacktrace << self
+				raise e
+			end 
+		end 
+	end 
+
+end
+class ImportStmt < QStmt
+	def initialize(filename)
+		@filename = filename
+	end
+	def q_eval(locals)
+		filename = locals.globals.find_file(@filename)
+		#puts "importing #{filename.inspect}"
+		q_file = QFile.parse(filename)
+		q_output = q_file.q_eval_extern(locals.globals)
+		q_output.extern = true
+		return Target::QInclude.new(@filename + ".h")
+	end
+	def self.parse(tokens)
+		line = tokens.pos
+		to_import = (tokens.expect(:tString) do |token|
+			<<ERROR_MSG
+I found a #{token.humanize} at #{token.pos}.
+\tI was really looking for a "filename" for this import statement.
+\tSomething like: import "super_cool_file";
+\tWot.Wot
+ERROR_MSG
+		end).data
+		tokens.expect(:tSemi) do |token|
+			<<ERROR_MSG
+I found a #{token.humanize} at #{token.pos}.
+\tI was really looking for a ";" to finish off this import statement
+\tat line #{line};
+\tSomething like: import #{to_import.inspect};
+\tWot.Wot                #{" "*to_import.inspect.length}^
+ERROR_MSG
+		end
+		return self.new(to_import).set_line(line)
+	end
+end
+class PackageStmt < QStmt
+	def initialize(name)
+		@name = name
+	end
+	def q_eval(locals)
+		locals.package = @name
+		return nil
+	end
+	def self.parse(tokens)
+		line = tokens.pos
+		qualified_name = QualifiedName.parse(tokens)
+		tokens.expect(:tSemi)
+		return self.new(qualified_name).set_line(line)
+	end
+end
+class QFile
+	attr_accessor :namespace
+	def initialize(filename,suite)
+		@filename,@suite = filename,suite
+	end
+	def q_eval(globals = Globals.new())
+		local_pos = LocalSituation.new(globals)
+		q_file = Target::QFile.new()
+		@suite.each do |name|
+			val = name.q_eval(local_pos)
+			if(val)
+				if(val.respond_to?(:create))
+					q_file.add_type(val)
+				end
+			end
+		end
+		@namespace = local_pos.local
+		return q_file
+	end
+	def q_eval_extern(globals)
+		local_pos = LocalSituation.new(globals)
+		q_file = Target::QFile.new()
+		@suite.each do |name|
+			if(name.respond_to?(:q_eval_extern))
+				val = name.q_eval_extern(local_pos)
+			else
+				val = name.q_eval(local_pos)
+			end
+			if(val)
+				if(val.respond_to?(:create))
+					q_file.add_type(val)
+				end
+			end
+		end
+		return q_file
+	end
+	def self.parse(filename)
+		tokens = Tokenizer.new(filename)
+		suite = []
+		while(tokens.peak != :tEnd)
+			token = tokens.expect(:tWord) do |token| #symbol
+			<<ERROR_MSG
+I found a #{token.humanize} at #{token.pos}.
+\tI was really looking for a "package", "import", "queue_group",
+\t"message", or "queue" statement to get things moving.
+\tSomething like: import "super_cool_file";
+\tWot.Wot
+ERROR_MSG
+			end
+			case token.data
+			when "package"
+				suite << PackageStmt.parse(tokens)
+			when "import"
+				suite << ImportStmt.parse(tokens)
+			when "queue_group"
+				suite << QueueGroupStmt.parse(tokens)
+			when "message"
+				suite << MessageStmt.parse(tokens)
+			when "queue"
+				suite << QueueStmt.parse(tokens)
+			when "interface"
+				suite << InterfaceStmt.parse(tokens)
+			else
+				tokens.qError(<<ERROR_MSG)
+expected a "package","import","queue","queue_group", or "message" statement rather
+	than a #{token.data.inspect}, (whatever that is?)
+	oh! no! a feature request!?
+	Wot. Wot.
+ERROR_MSG
+			end
+		end
+		return self.new(filename,suite)
+	end
+end
diff --git a/aos/build/queues/objects/queue.rb b/aos/build/queues/objects/queue.rb
new file mode 100644
index 0000000..5693486
--- /dev/null
+++ b/aos/build/queues/objects/queue.rb
@@ -0,0 +1,154 @@
+class MessageElementStmt < QStmt
+	attr_accessor :name
+	def initialize(type,name,length = nil) #lengths are for arrays
+		@type = type
+		@name = name
+		@length = length
+	end
+	CommonMistakes = {"short" => "int16_t","int" => "int32_t","long" => "int64_t"}
+	def check_type_error()
+		if(!(Sizes[@type] || (@length != nil && @type == "char")) )
+			if(correction = CommonMistakes[@type])
+				raise QError.new(<<ERROR_MSG)
+Hey! you have a \"#{@type}\" in your message statement.
+\tplease use #{correction} instead. Your type is not supported because we
+\twant to guarantee that the sizes of the messages stay the same across platforms.
+\tWot. Wot.
+ERROR_MSG
+			elsif(@type == "char")
+				raise QError.new(<<ERROR_MSG)
+Hey! you have a \"#{@type}\" in your message statement.
+\tyou need your declaration to be a char array like: char[10].
+\tor, please use int8_t or uint8_t.
+\tWot. Wot.
+ERROR_MSG
+			else
+				raise QError.new(<<ERROR_MSG)
+Hey! you have a \"#{@type}\" in your message statement.
+\tThat is not in the list of supported types.
+\there is the list of supported types:
+\tint{8,16,32,64}_t,uint{8,16,32,64}_t,bool,float,double#{len_comment}
+\tWot. Wot.
+ERROR_MSG
+			end
+		end
+	end
+
+	PrintFormat = {"bool" => "%c",
+                       "float" => "%f",
+                       "char" => "%c",
+                       "double" => "%f",
+                       "uint8_t" => "%\"PRIu8\"",
+                       "uint16_t" => "%\"PRIu16\"",
+                       "uint32_t" => "%\"PRIu32\"",
+                       "uint64_t" => "%\"PRIu64\"",
+                       "int8_t" => "%\"PRId8\"",
+                       "int16_t" => "%\"PRId16\"",
+                       "int32_t" => "%\"PRId32\"",
+                       "int64_t" => "%\"PRId64\""}
+        def toPrintFormat()
+		if(format = PrintFormat[@type])
+			return format;
+		end
+		raise QError.new(<<ERROR_MSG)
+Somehow this slipped past me, but
+\tI couldn't find the print format of #{@type}. Really, my bad.
+\tWot. Wot.
+ERROR_MSG
+	end
+
+	Sizes = {"bool" => 1, "float" => 4,"double" => 8}
+	[8,16,32,64].each do |len|
+		Sizes["int#{len}_t"] = len / 8
+		Sizes["uint#{len}_t"] = len / 8
+	end
+	Zero = {"float" => "0.0f","double" => "0.0","bool" => "false"}
+	def size()
+		if(size = Sizes[@type]); return size; end
+		return 1 if(@type == "char")
+		raise QError.new(<<ERROR_MSG)
+Somehow this slipped past me, but
+\tI couldn't find the size of #{@type}. Really, my bad.
+\tWot. Wot.
+ERROR_MSG
+	end
+	def q_eval(locals)
+		check_type_error()
+		if(@length == nil)
+			member = Target::MessageElement.new(@type,@name)
+		else
+			member = Target::MessageArrayElement.new(@type,@name,@length)
+		end
+		member.size = size()
+		member.zero = Zero[@type] || "0";
+		member.printformat = toPrintFormat()
+		locals.local.add_member(member)
+	end
+	def self.parse(tokens)
+		line = tokens.pos
+		type = tokens.expect(:tWord).data
+		len = nil
+		if(tokens.peak == :tOpenB)
+			tokens.expect(:tOpenB)
+			len = tokens.expect(:tNumber).data
+			tokens.expect(:tCloseB)
+		end
+		name = tokens.expect(:tWord).data
+		tokens.expect(:tSemi)
+		return self.new(type,name,len).set_line(line)
+	end
+end
+class MessageStmt < QStmt
+	def initialize(name,suite)
+		@name = name
+		@suite = suite
+	end
+	def q_eval(locals)
+		group = Target::MessageDec.new(@name)
+		locals.register(group)
+		@suite.each do |stmt|
+			stmt.q_eval(locals.bind(group))
+		end 
+		return group
+	end
+	def self.parse(tokens)
+		name = tokens.expect(:tWord).data
+		values = []
+		tokens.expect(:tOpenB)
+		while(tokens.peak != :tCloseB)
+			values << MessageElementStmt.parse(tokens)
+		end
+		names = {}
+		values.each do |val|
+			if(names[val.name])
+				raise QSyntaxError.new(<<ERROR_MSG)
+Hey! duplicate name #{val.name.inspect} in your message declaration statement (message #{name}).
+\tI found them at: #{names[val.name].q_stack_name()} and #{val.q_stack_name()}.
+\tWot. Wot.
+ERROR_MSG
+			end
+			names[val.name] = val
+		end
+		tokens.expect(:tCloseB)
+		tokens.expect(:tSemi)
+		self.new(name,values)
+	end
+end
+class QueueStmt < QStmt
+	def initialize(type,name)
+		@type,@name = type,name
+	end
+	def q_eval(locals)
+		queue = Target::QueueDec.new(@type.lookup(locals),@name)
+		locals.register(queue)
+		locals.local.add_queue(queue) if(locals.local.respond_to?(:add_queue))
+		return queue
+	end
+	def self.parse(tokens)
+		line = tokens.pos
+		type_name = QualifiedName.parse(tokens)
+		name = tokens.expect(:tWord).data
+		tokens.expect(:tSemi)
+		return self.new(type_name,name).set_line(line)
+	end
+end
diff --git a/aos/build/queues/objects/queue_group.rb b/aos/build/queues/objects/queue_group.rb
new file mode 100644
index 0000000..136834f
--- /dev/null
+++ b/aos/build/queues/objects/queue_group.rb
@@ -0,0 +1,115 @@
+class QueueGroupTypeStmt < QStmt
+	def initialize(name,suite)
+		@name,@suite = name,suite
+	end
+	def q_eval(locals)
+		group = Target::QueueGroupDec.new(@name)
+		group.created_by = self
+		locals.register(group)
+		@suite.each do |stmt|
+			stmt.q_eval(locals.bind(group))
+		end
+		return group
+	end
+end
+class ImplementsStmt < QStmt
+	def initialize(name)
+		@name = name
+	end
+	def q_eval(locals)
+
+	end
+	def self.parse(tokens)
+		name = QualifiedName.parse(tokens)
+		tokens.expect(:tSemi)
+		return self.new(name)
+	end
+end
+class QueueGroupStmt < QStmt
+	def initialize(type,name)
+		@type,@name = type,name
+	end
+	def q_eval(locals)
+		group = Target::QueueGroup.new(@type.lookup(locals),@name)
+		group.created_by = self
+		locals.register(group)
+		return group
+	end
+	def self.parse(tokens)
+		line = tokens.pos
+		type_name = QualifiedName.parse(tokens)
+		if(type_name.names.include?("queue_group"))
+			tokens.qError(<<ERROR_MSG)
+I was looking at the identifier you gave 
+\tfor the queue group type between line #{line} and #{tokens.pos}
+\tThere shouldn't be a queue_group type called queue_group
+\tor including queue_group in it's path, it is a reserved keyword.
+\tWot. Wot.
+ERROR_MSG
+		end
+		if(tokens.peak == :tOpenB)
+			if(type_name.is_simple?())
+				type_name = type_name.to_simple
+			else
+				tokens.qError(<<ERROR_MSG)
+You gave the name: "#{type_name.to_s}" but you're only allowed to 
+\thave simple names like "#{type_name.names[-1]}" in queue_group definitions
+\ttry something like:
+\tqueue_group ControlLoop { }
+\tWot. Wot.
+ERROR_MSG
+			end
+			tokens.expect(:tOpenB)
+			suite = []
+			while(tokens.peak != :tCloseB)
+				token = tokens.expect(:tWord) do |token|
+					<<ERROR_MSG
+I'm a little confused, I found a #{token.humanize} at #{token.pos}
+\tbut what I really wanted was an identifier signifying a nested 
+\tmessage declaration, or queue definition, or an impliments statement.
+\tWot.Wot
+ERROR_MSG
+				end
+				case token.data
+				when "message"
+					suite << MessageStmt.parse(tokens)
+				when "queue"
+					suite << QueueStmt.parse(tokens)
+				when "implements"
+					suite << ImplementsStmt.parse(tokens)
+				else
+					tokens.qError(<<ERROR_MSG)
+expected a "queue","implements" or "message" statement rather
+\tthan a #{token.data.inspect}.
+\tWot. Wot.
+ERROR_MSG
+				end 
+			end
+			tokens.expect(:tCloseB)
+			obj = QueueGroupTypeStmt.new(type_name,suite).set_line(line)
+		else
+			name = (tokens.expect(:tWord) do |token|
+				<<ERROR_MSG
+I found a #{token.humanize} at #{token.pos}
+\tbut I was in the middle of parsing a queue_group statement, and
+\twhat I really wanted was an identifier to store the queue group.
+\tSomething like: queue_group control_loops.Drivetrain my_cool_group;
+\tWot.Wot
+ERROR_MSG
+			end).data
+			obj = QueueGroupStmt.new(type_name,name).set_line(line)
+			if(tokens.peak == :tDot)
+				tokens.qError(<<ERROR_MSG)
+Hey! It looks like you're trying to use a complex identifier at: #{tokens.pos}
+\tThats not going to work. Queue Group definitions have to be of the form:
+\tqueue_group ComplexID SimpleID
+\tWot. Wot.
+ERROR_MSG
+			end	
+		end
+		tokens.expect(:tSemi) do |token|
+			token.pos
+		end
+		return obj
+	end
+end
diff --git a/aos/build/queues/objects/tokenizer.rb b/aos/build/queues/objects/tokenizer.rb
new file mode 100644
index 0000000..2a33b90
--- /dev/null
+++ b/aos/build/queues/objects/tokenizer.rb
@@ -0,0 +1,213 @@
+class BufferedReader
+	def initialize(file)
+		@file = file
+		@line = 1
+		@chars = []
+		@col_nums = []
+		@col = 0
+	end
+	def filename
+		return File.basename(@file.path)
+	end
+	def pos()
+		"#{filename()}:#{lineno()},#{@col}"
+	end
+	def clear_comment()
+		@file.gets
+		@line += 1
+	end
+	def lineno()
+		return @line
+		return @file.lineno + 1
+	end
+	def pop_char()
+		val = @chars.pop() || @file.read(1)
+		@col_nums[@line] = @col += 1
+		if(val == "\n")
+			@line += 1
+			@col = 0
+		end
+
+		return val
+	end
+	def unpop_char(char)
+		if(char == "\n")
+			@line -= 1
+			@col = @col_nums[@line]
+		end
+		@col -= 1
+		@chars.push(char)
+	end
+end
+class Tokenizer
+	TOKEN_TYPES = {"{" => :tOpenB,"}"=> :tCloseB,";" => :tSemi,"," => :tComma,
+		"(" => :tOpenParan,")" => :tCloseParan,"=" => :tAssign,"." => :tDot,
+		"<<"=> :tLShift,"*" => :tMult,"+" => :tAdd,"[" => :tOpenB,
+		"]" => :tCloseB}
+	Humanize = TOKEN_TYPES.invert
+	class Token
+		attr_accessor :type,:data,:pos
+		def to_s
+			if(@type == :tString)
+				val = @data.inspect.to_s
+			elsif(@type == :tWord)
+				val = @data.to_s
+			else
+				val = @data.to_s
+			end
+			return "#{val.ljust(50)}:#{@type}"
+		end
+		def humanize()
+			if(@type == :tString)
+				return "#{@data.inspect.to_s} string"
+			elsif(@type == :tWord)
+				return "#{@data.inspect} identifier"
+			end
+			return Humanize[@type].inspect
+		end
+		def inspect()
+			data = ""
+			data = " #{@data.inspect}" if(@data)
+			"<Token :#{@type}#{data} at #{@pos}>"
+		end
+		def ==(other)
+			if(other.class == Symbol)
+				return @type == other
+			elsif(other.class == self.class)
+				return @type == other.type && @data == other.data
+			else
+				return nil
+			end
+		end
+	end
+	def initialize(file)
+		file = File.open(file,"r") if(file.class == String)
+		@read = BufferedReader.new(file)
+	end
+	def qError(error)
+		syntax_error(error)
+	end
+	def syntax_error(msg)
+		err = QSyntaxError.new(msg)
+		err.qstacktrace << "#{@read.lineno} of #{@read.filename}"
+		raise err
+	end
+	def peak_token()
+		@peak_token = next_token()
+	end
+	def peak()
+		peak_token()
+	end
+	def next()
+		next_token()
+	end
+	def pos()
+		@read.pos
+	end
+	def tokenize(string_token)
+		token = Token.new()
+		token.type = TOKEN_TYPES[string_token]
+		return token
+	end
+	def next_token()
+		if(token = @peak_token)
+			@peak_token = nil
+			return token
+		end
+		token = next_token_cache()
+		pos = self.pos()
+		token.pos = pos
+		return token
+	end
+	def next_token_cache()
+		token = Token.new()
+		token.data = ""
+		while (char = @read.pop_char())
+			#puts "#{char.inspect}:#{token.inspect}"
+			if(char == "/")
+				if(@read.pop_char == "/")
+					@read.clear_comment()
+				else
+					syntax_error("unexpected #{char.inspect}")
+				end
+			elsif(char == "#")
+				@read.clear_comment()
+			elsif(char =~ /[\s\r\n]/)
+				if(token.type)
+					return token
+				end
+			elsif(char =~ /\"/)
+				token.type = :tString
+				token.data = ""
+				while((char = @read.pop_char) != "\"")
+					token.data += char
+				end
+				return token
+			elsif(char =~ /[1-9]/)
+				token.type = :tNumber
+				token.data = char.to_i
+				while(char != ".")
+					char = @read.pop_char
+					if(char =~ /[0-9]/)
+						token.data = char.to_i + token.data * 10
+					elsif(char == ".")
+					else
+						@read.unpop_char(char)
+						return token
+					end
+				end
+				second_char = 0
+				man = 0
+				while(true)
+					char = @read.pop_char
+					if(char =~ /[0-9]/)
+						second_char = char.to_i + second_char * 10
+						man = man * 10
+					else
+						@read.unpop_char(char)
+						token.data += second_char / man.to_f
+						return token
+					end
+				end
+			elsif(char == ":")
+				if(@read.pop_char == "=")
+					return tokenize(":=")
+				end
+				syntax_error("unexpected \":\"")
+			elsif(char =~ /[;\{\}=()\",\*\+\.\[\]]/)
+				return(tokenize(char))
+			elsif(char =~ /[a-zA-Z_]/)
+				token.type = :tWord
+				token.data = char
+				while(true)
+					char = @read.pop_char()
+					if(char && char =~ /\w/)
+						token.data += char
+					else
+						@read.unpop_char(char)
+						return token
+					end
+				end
+			elsif(char == "<")
+				if((char = @read.pop_char()) == "<")
+					return tokenize("<<")
+				else
+					@read.unpop_char(char)
+					return tokenize("<")
+				end
+			else
+				syntax_error("unexpected #{char.inspect}")
+			end
+		end
+		token.type = :tEnd
+		return token
+	end
+	def expect(type,&blk)
+		token = self.next
+		if(token != type)
+			syntax_error(blk.call(token)) if(blk)
+			syntax_error("unexpected: #{token.type}")
+		end
+		return token
+	end
+end