From b9f5be26b3b7a7e492d9e358a96a2e80c3f2d211 Mon Sep 17 00:00:00 2001 From: "Michael D. Lowis" Date: Sun, 26 Jan 2020 23:21:30 -0500 Subject: [PATCH] tweaked syntax based on ideas from oberon --- compile.rb | 393 ++++++++++++++++++++++------------------------------ example.src | 39 ++++-- 2 files changed, 197 insertions(+), 235 deletions(-) diff --git a/compile.rb b/compile.rb index d9fae36..2971519 100755 --- a/compile.rb +++ b/compile.rb @@ -4,122 +4,62 @@ require 'strscan' require 'pp' module Type - Void = "void" - Float = "float" - - Range = Struct.new(:start, :stop) - Int = Range.new(-2147483647, 2147483647) - UInt = Range.new(0, 0xFFFFFFFF) - Byte = Range.new(0, 0xFF) - Char = Range.new(0, 0x10FFFF) - - Enum = Struct.new(:entries) - Bool = Enum.new([:true, :false]) - + Array = Struct.new(:type, :count) + Record = Struct.new(:fields) Func = Struct.new(:args, :return) Ptr = Struct.new(:type) - Array = Struct.new(:type, :count) + Int = Struct.new(:min, :max) + Real = Struct.new(:bits) end -module AST - Let = Struct.new(:type, :name, :value) - Apply = Struct.new(:type, :func, :args) - Value = Struct.new(:type, :value) - Func = Struct.new(:type, :args, :body) - Var = Struct.new(:type, :name) - If = Struct.new(:type, :cond, :then, :else) - Block = Struct.new(:type, :exprs) - Array = Struct.new(:items) -end - -module Codegen - def self.type_to_s(type) - if type.is_a? Type::Ptr then - "#{type_to_s(type.type)}*" - else - type - end - end - - def self.genfunc(ast) - print "#{type_to_s(ast.type.return)} #{ast.name}(" - print (ast.type.args.each_with_index.map do |t,i| - type_to_s(t) + " " + ast.value.args[i] - end.join(", ")) - print ") {\n" - print " #{type_to_s(ast.type.return)} _rv_;\n" - print " return _rv_;\n}\n\n" - end - - def self.array_type(type) - if type.is_a? Type::Array then - array_type(type.type) - else - type - end - end - - def self.array_dims(type) - if type.is_a? Type::Array then - "[#{type.count}]" + array_dims(type.type) - else - "" - end - end - - def self.gendef(ast) - if ast.type.is_a? Type::Func then - genfunc(ast) - elsif ast.type.is_a? Type::Array then - type = array_type(ast.type) - printf "#{type} #{ast.name}#{array_dims(ast.type)};\n\n" - else - printf "#{type_to_s(ast.type)} #{ast.name};\n\n" - end - end +module Ast + Val = Struct.new(:type, :value) end -#Codegen.gendef( -# AST::Let.new(Type::Int, "stuff", AST::Value.new(Type::Int, 42))) -#Codegen.gendef( -# AST::Let.new(Type::Bool, "stuff", AST::Value.new(Type::Bool, true))) -#Codegen.gendef( -# AST::Let.new(Type::Char, "stuff", AST::Value.new(Type::Char, "A"))) -#Codegen.gendef( -# AST::Let.new( -# Type::Array.new(Type::Int), "stuff", -# AST::Value.new(Type::Array.new(Type::Int), "A"))) -#Codegen.gendef( -# AST::Let.new( -# Type::Array.new(Type::Int, 42), "stuff", -# AST::Value.new(Type::Array.new(Type::Int, 42), "A"))) -#Codegen.gendef( -# AST::Let.new( -# Type::Array.new(Type::Array.new(Type::Int, 42), 24), "stuff", -# AST::Value.new(Type::Array.new(Type::Int, 42), "A"))) -#Codegen.gendef( -# AST::Let.new( -# Type::Func.new([Type::Ptr.new(Type::Ptr.new(Type::Char))], Type::Int), -# "main", -# AST::Func.new( -# Type::Func.new([Type::Ptr.new(Type::Ptr.new(Type::Char))], Type::Int), -# ["args"], -# AST::Value.new(Type::Int, 0))) -#) +BuiltinSyms = { + "Bool" => { + kind: :type, + type: Type::Int.new(0, 1) + }, + "true" => { + kind: :const, + type: "Bool", + value: Ast::Val.new("Bool", 1) + }, + "false" => { + kind: :const, + type: "Bool", + value: Ast::Val.new("Bool", 0) + }, + "Char" => { + kind: :type, + type: Type::Int.new(0, 0x10FFFF) + }, + "Int" => { + kind: :type, + type: Type::Int.new(-2**63-1, 2**63-1) + }, + "Real" => { + kind: :type, + type: Type::Real.new(64) + }, + "Byte" => { + kind: :type, + type: Type::Int.new(0, 255) + }, +} class Lexer Tok = Struct.new(:text, :file, :pos, :type) - SPACE = /[ \t\v\n\r]+/ + SPACE = /([ \t\v\n\r]+|#.*\n)/ IDENT = /[_a-zA-Z][_a-zA-Z0-9]*/ BRACES = /[\(\)\[\]\{\}]/ - OPERATORS = /[*\/=+-]/ + OPERATORS = /[*\/=+-:\$]/ INTEGER = /[0-9]+/ ID_TYPES = { - "requires" => :requires, - "provides" => :provides, - "let" => :let, - "var" => :var, - "fun" => :fun + "module" => :module, + "imports" => :imports, + "is" => :is } def initialize(path) @@ -132,19 +72,18 @@ class Lexer end def next - @data.skip(SPACE) + while @data.skip(SPACE) do + end if not @data.eos? - type = nil + type = :eof if @data.scan(IDENT) type = get_id_type(@data.matched) + elsif @data.scan(INTEGER) + type = :int elsif @data.scan(BRACES) type = @data.matched elsif @data.scan(OPERATORS) type = @data.matched - elsif @data.scan(INTEGER) - type = :int - else - nil end Tok.new(@data.matched, @file, @data.pos, type) if type else @@ -186,150 +125,148 @@ class Parser @next = nil end - def requires() - expect(:requires) + ####################################### + # Top Level Forms + ####################################### + def module() + expect(:module) + expect(:ident).text + end + + def imports() + expect(:imports) expect("(") reqs = [] - while (peek().type != ")") + while (!matches(")")) reqs << expect(:ident).text + expect(",") if !matches(")") end expect(")") reqs end - def provides() - expect(:provides) - expect("{") - reqs = {} - while (peek().type != "}") - declaration(reqs) - end - expect("}") - reqs - end - - def definitions(defs = {}) - while not eof? - topdef(defs) - end - defs - end - - private - - def topdef(defs) - dectype = declaration(defs) - if dectype == :fun then - expect("{") - value = expression() - expect("}") - else - expect("=") - value = expression() - end - pp value - end - - def declaration(syms) - deftype = peek().type - if (accept(:fun) || accept(:let)) - name = expect(:ident).text - if deftype == :fun - type = Type::Func.new({}, nil) - expect("(") - while (peek().type != ")") - type.args[expect(:ident).text] = type_annotation() - expect(",") if (peek().type != ")") - end - expect(")") - type.return = type_annotation() + def definitions() + defs = {} + while !matches(:eof) + sym = { + export: accept("$"), + name: expect(:ident).text + } + error("#{sym[:name]} multiply defined in module") if defs[sym[:name]] + if matches(:is) + typedef(sym) + elsif matches(":") + vardef(sym) + elsif matches("(") + funcdef(sym) else - type = type_annotation() + error("invalid symbol definition") end - syms[name] = { type: type, value: nil } - else - error("invalid declaration") + defs[sym[:name]] = sym end - deftype + defs end - def type_annotation() - name = expect(:ident).text - if accept("[") - expect("]") - Type::Array.new(name, -1) + ####################################### + # Type Definitions + ####################################### + def typedef(sym) + expect(:is) + sym[:kind] = :type + sym[:type] = type_specifier() + end + + def type_specifier() + if matches("[") + array_type() + elsif matches("{") + struct_type() + elsif matches("*") + pointer_type() +# elsif matches(:func) +# func_type() else + name = expect(:ident).text + # TODO: check the type exists name end end - def expression() - parseLevel(:assign) + def array_type() + expect("[") + count = expect(:int).text.to_i if matches(:int) + basetype = type_specifier() + expect("]") + Type::Array.new(basetype, count) end - def getRule(tok) - RULES[tok.type] + def struct_type() + struct = Type::Record.new({}) + expect("{") + while !matches("}") + name = expect(:ident).text + expect(":") + type = type_specifier() + struct.fields[name] = type + end + expect("}") + struct end - def keepGoing(level) - rule = getRule(peek()) - (LEVELS[level] <= LEVELS[rule[:level]]) if rule + def pointer_type() + expect("*") + Type::Ptr.new(type_specifier()) end - def parseLevel(level) - consume() - prefixRule = getRule(@prev)[:prefix] - if not prefixRule then - error("expected an expression") - end - ast = send(prefixRule) - while keepGoing(level) - consume() - infixRule = getRule(@prev)[:infix] - ast = send(infixRule, ast) + ####################################### + # Variable Definitions + ####################################### + def vardef(sym) + expect(":") + sym[:kind] = :var + sym[:type] = type_specifier() + if accept("=") + sym[:value] = const_expr() end - ast end - def constant() - if (@prev.type == :int) - AST::Value.new(Type::Int, @prev.text.to_i) - else - error("not a valid constant") - end + def const_expr() + expect("{") + expect("}") end - def grouping() - ast = expression() - expect(")") - ast + ####################################### + # Function Definitions + ####################################### + def funcdef(sym) + sym[:kind] = :func + sym[:type] = Type::Func.new([], nil) + func_args(sym) + sym[:type].return = type_specifier() if accept(":") + expect("{") + expect("}") end - def func_call(ast) + def func_args(sym) + expect("(") + while !matches(")") + arg = { name: expect(:ident).text } + expect(":") + arg[:type] = type_specifier() + expect(",") if !matches(")") + sym[:type].args << arg + end expect(")") - AST::Apply.new(nil, ast, []) end - def unary() - type = @prev.type - AST::Apply.new(nil, @prev.type, [parseLevel(:unary)]) - end - - def nextLevel(tok) - level = getRule(@prev)[:level] - index = LVLNAMES.find_index(level) - LVLNAMES[index + 1] - end - - def binary(left) - type = @prev.type - nlvl = nextLevel(type) - right = parseLevel(nlvl) - AST::Apply.new(nil, type, [left, right]) + def func_body(sym) + expect("{") + expect("}") end def error(str) - raise str + puts str + exit 1 end def peek() @@ -369,18 +306,19 @@ class Parser end class Package - def initialize(name) - @name = name - @externs = {} - @provides = {} + def initialize(path) + @name = nil + @imports = {} + @declares = {} @definitions = {} + add_file(path) end def add_file(path) parse = Parser.new(path) - parse.requires - @provides = parse.provides - @definitions = parse.definitions + @name = parse.module() + @imports = parse.imports() + @definitions = parse.definitions() end def add_files(paths) @@ -388,10 +326,13 @@ class Package end def dump - pp @definitions + pp({ + name: @name, + imports: @imports, + defines: @definitions + }) end end -pkg = Package.new("example") -pkg.add_file("example.src") +pkg = Package.new("example.src") pkg.dump() diff --git a/example.src b/example.src index 8e99f27..de48bf8 100644 --- a/example.src +++ b/example.src @@ -1,11 +1,32 @@ -requires (io) -provides { - let foo int - fun main(args string[]) int -} - -let foo int = 1 * 2 + 3 +module Main +imports (X11, XSel, Posix) -fun main(args string[]) int { - 42() +Command is [ 1 String ] +Command2 is [10 [5 String]] +PointRef is **Point +MyStruct is { + foo : Int + bar : [10 Int] } + +##Command is { +## name : String +##} +# +#cmd : Command #= [ "fetch", nil, nil ] +# +#fetchsel(sel : String) +#{ +## cmd[1] = sel; +## Posix.exec(cmd) +#} +# +#$main(args : array of String) : Int +#{ +## x : X11.Config = {0} +## X11.init(&x) +## X11.mkwin(&x, 1, 1, X11.PropertyChangeMask) +## XSel.init(&x) +## X11.loop(&x, nil) +#} +# -- 2.52.0