Compare commits
	
		
			2 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 13ce1a72c1 | |||
| acbdf1de5f | 
							
								
								
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@ -0,0 +1,2 @@
 | 
				
			|||||||
 | 
					a.out
 | 
				
			||||||
 | 
					
 | 
				
			||||||
							
								
								
									
										3
									
								
								hello.bas
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								hello.bas
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,3 @@
 | 
				
			|||||||
 | 
					Function PBMain() as Long
 | 
				
			||||||
 | 
					    PRINT "Hello, world!"
 | 
				
			||||||
 | 
					End Function
 | 
				
			||||||
							
								
								
									
										162
									
								
								pb2c.rb
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										162
									
								
								pb2c.rb
									
									
									
									
									
										Executable file
									
								
							@ -0,0 +1,162 @@
 | 
				
			|||||||
 | 
					#!/usr/bin/ruby
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Tokenizer
 | 
				
			||||||
 | 
					    TOKEN_TYPES = [
 | 
				
			||||||
 | 
					        [:function, /\bfunction\b/i],
 | 
				
			||||||
 | 
					        [:sub, /\bsub\b/i],
 | 
				
			||||||
 | 
					        [:end, /\bend\b/i],
 | 
				
			||||||
 | 
					        [:as, /\bas\b/i],
 | 
				
			||||||
 | 
					        [:typename, /\blong\b/i],
 | 
				
			||||||
 | 
					        [:identifier, /\b[a-zA-Z]+\b/],
 | 
				
			||||||
 | 
					        [:integer, /\b[0-9]+\b/],
 | 
				
			||||||
 | 
					        [:string, /".*"/],
 | 
				
			||||||
 | 
					        [:oparen, /\(/],
 | 
				
			||||||
 | 
					        [:cparen, /\)/],
 | 
				
			||||||
 | 
					        [:comma, /,/],
 | 
				
			||||||
 | 
					        [:quote, /'/],
 | 
				
			||||||
 | 
					    ]
 | 
				
			||||||
 | 
					    def initialize(code)
 | 
				
			||||||
 | 
					        @code = code
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def tokenize
 | 
				
			||||||
 | 
					        tokens = []
 | 
				
			||||||
 | 
					        begin
 | 
				
			||||||
 | 
					            until @code.empty?
 | 
				
			||||||
 | 
					                tokens << tokenize_one_token
 | 
				
			||||||
 | 
					                @code = @code.strip
 | 
				
			||||||
 | 
					            end
 | 
				
			||||||
 | 
					        rescue RuntimeError => e
 | 
				
			||||||
 | 
					            puts tokens.join("\n")
 | 
				
			||||||
 | 
					            raise
 | 
				
			||||||
 | 
					        end
 | 
				
			||||||
 | 
					        tokens
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def tokenize_one_token
 | 
				
			||||||
 | 
					        TOKEN_TYPES.each do |type, re|
 | 
				
			||||||
 | 
					            re = /\A(#{re})/
 | 
				
			||||||
 | 
					            if @code =~ re
 | 
				
			||||||
 | 
					                value = $1
 | 
				
			||||||
 | 
					                @code = @code[value.length..-1]
 | 
				
			||||||
 | 
					                return Token.new(type, value)
 | 
				
			||||||
 | 
					            end
 | 
				
			||||||
 | 
					        end
 | 
				
			||||||
 | 
					        raise RuntimeError.new(
 | 
				
			||||||
 | 
					            "Couldn't match token on #{@code.inspect}")
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Token = Struct.new(:type, :value)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Parser
 | 
				
			||||||
 | 
					    def initialize(tokens)
 | 
				
			||||||
 | 
					        @tokens = tokens
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def parse
 | 
				
			||||||
 | 
					        parse_function
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def parse_function
 | 
				
			||||||
 | 
					        consume(:function)
 | 
				
			||||||
 | 
					        name = consume(:identifier).value
 | 
				
			||||||
 | 
					        arg_names = parse_arg_names
 | 
				
			||||||
 | 
					        consume(:as)
 | 
				
			||||||
 | 
					        rtype = consume(:typename).value
 | 
				
			||||||
 | 
					        body = parse_expr
 | 
				
			||||||
 | 
					        consume(:end)
 | 
				
			||||||
 | 
					        consume(:function)
 | 
				
			||||||
 | 
					        FunctionNode.new(name, rtype, arg_names, body)
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def parse_arg_names
 | 
				
			||||||
 | 
					        arg_names = []
 | 
				
			||||||
 | 
					        consume(:oparen)
 | 
				
			||||||
 | 
					        if peek(:identifier)
 | 
				
			||||||
 | 
					            arg_names << consume(:identifier).value
 | 
				
			||||||
 | 
					            while peek(:comma)
 | 
				
			||||||
 | 
					                consume(:comma)
 | 
				
			||||||
 | 
					                arg_names << consume(:identifier).value
 | 
				
			||||||
 | 
					            end
 | 
				
			||||||
 | 
					        end
 | 
				
			||||||
 | 
					        consume(:cparen)
 | 
				
			||||||
 | 
					        arg_names
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def parse_expr
 | 
				
			||||||
 | 
					        if peek(:integer)
 | 
				
			||||||
 | 
					            parse_integer
 | 
				
			||||||
 | 
					        elsif peek(:string)
 | 
				
			||||||
 | 
					            parse_string
 | 
				
			||||||
 | 
					        elsif peek(:identifier) && peek(:oparen, 1)
 | 
				
			||||||
 | 
					            parse_call
 | 
				
			||||||
 | 
					        elsif peek(:identifier) && peek(:string, 1)
 | 
				
			||||||
 | 
					            parse_stmt
 | 
				
			||||||
 | 
					        else
 | 
				
			||||||
 | 
					            parse_var_ref
 | 
				
			||||||
 | 
					        end
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def parse_stmt
 | 
				
			||||||
 | 
					        name = consume(:identifier).value
 | 
				
			||||||
 | 
					        arg_exprs = consume(:string).value
 | 
				
			||||||
 | 
					        CallNode.new(name, arg_exprs)
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def peek(expected_type, offset=0)
 | 
				
			||||||
 | 
					        @tokens.fetch(offset).type == expected_type
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def consume(expected_type)
 | 
				
			||||||
 | 
					        token = @tokens.shift
 | 
				
			||||||
 | 
					        if token.type == expected_type
 | 
				
			||||||
 | 
					            token
 | 
				
			||||||
 | 
					        else
 | 
				
			||||||
 | 
					            raise RuntimeError.new(
 | 
				
			||||||
 | 
					                "Expected token type #{expected_type.inspect} but got #{token.type.inspect}")
 | 
				
			||||||
 | 
					        end
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					FunctionNode = Struct.new(:name, :type, :arg_names, :body)
 | 
				
			||||||
 | 
					StringNode = Struct.new(:value)
 | 
				
			||||||
 | 
					CallNode = Struct.new(:name, :arg_exprs)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Generator
 | 
				
			||||||
 | 
					    def generate(node)
 | 
				
			||||||
 | 
					        case node
 | 
				
			||||||
 | 
					        when FunctionNode
 | 
				
			||||||
 | 
					            "%s %s(%s) { return %s ; }" % [
 | 
				
			||||||
 | 
					                node.type.downcase,
 | 
				
			||||||
 | 
					                node.name,
 | 
				
			||||||
 | 
					                node.arg_names.join(','),
 | 
				
			||||||
 | 
					                generate(node.body),
 | 
				
			||||||
 | 
					            ]
 | 
				
			||||||
 | 
					        when CallNode
 | 
				
			||||||
 | 
					            "%s(%s)" % [
 | 
				
			||||||
 | 
					                node.name,
 | 
				
			||||||
 | 
					                node.arg_exprs
 | 
				
			||||||
 | 
					            ]
 | 
				
			||||||
 | 
					        when StringNode
 | 
				
			||||||
 | 
					            node.value
 | 
				
			||||||
 | 
					        else
 | 
				
			||||||
 | 
					            raise RuntimeError.new("Unexpected node type: #{node.class}")
 | 
				
			||||||
 | 
					        end
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					tokens = Tokenizer.new(File.read("hello.bas")).tokenize
 | 
				
			||||||
 | 
					#puts "Tokens:\n"
 | 
				
			||||||
 | 
					#puts tokens.join("\n")
 | 
				
			||||||
 | 
					tree = Parser.new(tokens).parse
 | 
				
			||||||
 | 
					#puts "\nAST:\n"
 | 
				
			||||||
 | 
					#puts tree
 | 
				
			||||||
 | 
					RUNTIME = "#include <stdio.h>\n#define PRINT(a) printf(a)\n"
 | 
				
			||||||
 | 
					CMAIN   = "int main(void) { PBMain(); return 0; }"
 | 
				
			||||||
 | 
					generated = Generator.new.generate(tree)
 | 
				
			||||||
 | 
					#puts "\nGenerated:\n"
 | 
				
			||||||
 | 
					#puts generated
 | 
				
			||||||
 | 
					#puts "\nGenerated with preamble/postamble:\n"
 | 
				
			||||||
 | 
					puts [RUNTIME, generated, CMAIN].join("\n")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user