Compare commits
No commits in common. "develop" and "main" have entirely different histories.
2
.gitignore
vendored
2
.gitignore
vendored
@ -1,2 +0,0 @@
|
|||||||
a.out
|
|
||||||
|
|
162
pb2c.rb
162
pb2c.rb
@ -1,162 +0,0 @@
|
|||||||
#!/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