Add a command line parsing procedure
This commit is contained in:
parent
6e415e474f
commit
1983ef8e71
17
Rakefile
17
Rakefile
@ -44,11 +44,12 @@ end
|
|||||||
.partition { |f| f.end_with? '.elna' }
|
.partition { |f| f.end_with? '.elna' }
|
||||||
|
|
||||||
File.open t.name, 'w' do |output|
|
File.open t.name, 'w' do |output|
|
||||||
puts
|
compiler_command = compiler + sources
|
||||||
puts(compiler * ' ')
|
|
||||||
|
|
||||||
Open3.popen2(*compiler) do |cl_in, cl_out|
|
puts
|
||||||
cl_in.write File.read(*sources)
|
puts(compiler_command * ' ')
|
||||||
|
|
||||||
|
Open3.popen2(*compiler_command) do |cl_in, cl_out|
|
||||||
cl_in.close
|
cl_in.close
|
||||||
|
|
||||||
IO.copy_stream cl_out, output
|
IO.copy_stream cl_out, output
|
||||||
@ -87,11 +88,11 @@ task default: 'source/Compiler.elna'
|
|||||||
task :default do |t|
|
task :default do |t|
|
||||||
exe, previous_output, source = t.prerequisites
|
exe, previous_output, source = t.prerequisites
|
||||||
|
|
||||||
cat_arguments = ['cat', source]
|
exe_arguments = [exe, source]
|
||||||
diff_arguments = ['diff', '-Nur', '--text', previous_output, '-']
|
diff_arguments = ['diff', '-Nur', '--text', previous_output, '-']
|
||||||
|
|
||||||
puts [cat_arguments * ' ', exe, diff_arguments * ' '].join(' | ')
|
puts [exe, diff_arguments * ' '].join(' | ')
|
||||||
Open3.pipeline(cat_arguments, exe, diff_arguments)
|
Open3.pipeline exe_arguments, diff_arguments
|
||||||
end
|
end
|
||||||
|
|
||||||
task :backport do
|
task :backport do
|
||||||
@ -104,7 +105,7 @@ task :backport do
|
|||||||
source
|
source
|
||||||
.gsub(/^(var|type|const|begin)/) { |match| match.upcase }
|
.gsub(/^(var|type|const|begin)/) { |match| match.upcase }
|
||||||
.gsub(/^[[:alnum:]]* ?module/) { |match| match.upcase }
|
.gsub(/^[[:alnum:]]* ?module/) { |match| match.upcase }
|
||||||
.gsub(/\b(record|nil|or)\b/) { |match| match.upcase }
|
.gsub(/\b(record|nil|or|false|true)\b/) { |match| match.upcase }
|
||||||
.gsub(/proc\(/, 'PROCEDURE(')
|
.gsub(/proc\(/, 'PROCEDURE(')
|
||||||
.gsub(/ & /, ' AND ')
|
.gsub(/ & /, ' AND ')
|
||||||
.gsub(/ -> /, ': ')
|
.gsub(/ -> /, ': ')
|
||||||
|
323
rakelib/cross.rake
Normal file
323
rakelib/cross.rake
Normal file
@ -0,0 +1,323 @@
|
|||||||
|
# This Source Code Form is subject to the terms of the Mozilla Public License,
|
||||||
|
# v. 2.0. If a copy of the MPL was not distributed with this file, You can
|
||||||
|
# obtain one at https://mozilla.org/MPL/2.0/. -}
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'pathname'
|
||||||
|
require 'uri'
|
||||||
|
require 'net/http'
|
||||||
|
require 'rake/clean'
|
||||||
|
require 'open3'
|
||||||
|
require 'etc'
|
||||||
|
|
||||||
|
GCC_VERSION = "15.1.0"
|
||||||
|
BINUTILS_VERSION = '2.44'
|
||||||
|
GLIBC_VERSION = '2.41'
|
||||||
|
KERNEL_VERSION = '5.15.181'
|
||||||
|
|
||||||
|
CLOBBER.include 'build'
|
||||||
|
|
||||||
|
class BuildTarget
|
||||||
|
attr_accessor(:build, :gcc, :target, :tmp)
|
||||||
|
|
||||||
|
def gxx
|
||||||
|
@gcc.gsub 'c', '+'
|
||||||
|
end
|
||||||
|
|
||||||
|
def sysroot
|
||||||
|
tmp + 'sysroot'
|
||||||
|
end
|
||||||
|
|
||||||
|
def rootfs
|
||||||
|
tmp + 'rootfs'
|
||||||
|
end
|
||||||
|
|
||||||
|
def tools
|
||||||
|
tmp + 'tools'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def gcc_verbose(gcc_binary)
|
||||||
|
read, write = IO.pipe
|
||||||
|
sh({'LANG' => 'C'}, gcc_binary, '--verbose', err: write)
|
||||||
|
write.close
|
||||||
|
output = read.read
|
||||||
|
read.close
|
||||||
|
output
|
||||||
|
end
|
||||||
|
|
||||||
|
def find_build_target(gcc_version, task)
|
||||||
|
gcc_binary = 'gcc'
|
||||||
|
output = gcc_verbose gcc_binary
|
||||||
|
|
||||||
|
if output.start_with? 'Apple clang'
|
||||||
|
gcc_binary = "gcc-#{gcc_version.split('.').first}"
|
||||||
|
output = gcc_verbose gcc_binary
|
||||||
|
end
|
||||||
|
result = output
|
||||||
|
.lines
|
||||||
|
.each_with_object(BuildTarget.new) do |line, accumulator|
|
||||||
|
if line.start_with? 'Target: '
|
||||||
|
accumulator.build = line.split(' ').last.strip
|
||||||
|
elsif line.start_with? 'COLLECT_GCC'
|
||||||
|
accumulator.gcc = line.split('=').last.strip
|
||||||
|
end
|
||||||
|
end
|
||||||
|
result.tmp = Pathname.new('./build')
|
||||||
|
task.with_defaults target: 'riscv32-unknown-linux-gnu'
|
||||||
|
result.target = task[:target]
|
||||||
|
result
|
||||||
|
end
|
||||||
|
|
||||||
|
def download_and_unarchive(url, target)
|
||||||
|
case File.extname url.path
|
||||||
|
when '.bz2'
|
||||||
|
archive_type = '-j'
|
||||||
|
root_directory = File.basename url.path, '.tar.bz2'
|
||||||
|
when '.xz'
|
||||||
|
archive_type = '-J'
|
||||||
|
root_directory = File.basename url.path, '.tar.xz'
|
||||||
|
else
|
||||||
|
raise "Unsupported archive type #{url.path}."
|
||||||
|
end
|
||||||
|
|
||||||
|
Net::HTTP.start(url.host, url.port, use_ssl: url.scheme == 'https') do |http|
|
||||||
|
request = Net::HTTP::Get.new url.request_uri
|
||||||
|
|
||||||
|
http.request request do |response|
|
||||||
|
case response
|
||||||
|
when Net::HTTPRedirection
|
||||||
|
download_and_unarchive URI.parse(response['location'])
|
||||||
|
when Net::HTTPSuccess
|
||||||
|
Open3.popen2 'tar', '-C', target.to_path, archive_type, '-xv' do |stdin, stdout, wait_thread|
|
||||||
|
Thread.new do
|
||||||
|
stdout.each { |line| puts line }
|
||||||
|
end
|
||||||
|
|
||||||
|
response.read_body do |chunk|
|
||||||
|
stdin.write chunk
|
||||||
|
end
|
||||||
|
stdin.close
|
||||||
|
|
||||||
|
wait_thread.value
|
||||||
|
end
|
||||||
|
else
|
||||||
|
response.error!
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
target + root_directory
|
||||||
|
end
|
||||||
|
|
||||||
|
namespace :cross do
|
||||||
|
desc 'Build cross binutils'
|
||||||
|
task :binutils, [:target] do |_, args|
|
||||||
|
options = find_build_target GCC_VERSION, args
|
||||||
|
options.tools.mkpath
|
||||||
|
source_directory = download_and_unarchive(
|
||||||
|
URI.parse("https://ftp.gnu.org/gnu/binutils/binutils-#{BINUTILS_VERSION}.tar.xz"),
|
||||||
|
options.tools)
|
||||||
|
|
||||||
|
cwd = source_directory.dirname + 'build-binutils'
|
||||||
|
cwd.mkpath
|
||||||
|
options.rootfs.mkpath
|
||||||
|
|
||||||
|
env = {
|
||||||
|
'CC' => options.gcc,
|
||||||
|
'CXX' => options.gxx
|
||||||
|
}
|
||||||
|
configure_options = [
|
||||||
|
"--prefix=#{options.rootfs.realpath}",
|
||||||
|
"--target=#{options.target}",
|
||||||
|
'--disable-nls',
|
||||||
|
'--enable-gprofng=no',
|
||||||
|
'--disable-werror',
|
||||||
|
'--enable-default-hash-style=gnu',
|
||||||
|
'--disable-libquadmath'
|
||||||
|
]
|
||||||
|
configure = source_directory.relative_path_from(cwd) + 'configure'
|
||||||
|
sh env, configure.to_path, *configure_options, chdir: cwd.to_path
|
||||||
|
sh env, 'make', '-j', Etc.nprocessors.to_s, chdir: cwd.to_path
|
||||||
|
sh env, 'make', 'install', chdir: cwd.to_path
|
||||||
|
end
|
||||||
|
|
||||||
|
desc 'Build stage 1 GCC'
|
||||||
|
task :gcc1, [:target] do |_, args|
|
||||||
|
options = find_build_target GCC_VERSION, args
|
||||||
|
options.tools.mkpath
|
||||||
|
source_directory = download_and_unarchive(
|
||||||
|
URI.parse("https://gcc.gnu.org/pub/gcc/releases/gcc-#{GCC_VERSION}/gcc-#{GCC_VERSION}.tar.xz"),
|
||||||
|
options.tools)
|
||||||
|
|
||||||
|
cwd = source_directory.dirname + 'build-gcc'
|
||||||
|
cwd.mkpath
|
||||||
|
options.rootfs.mkpath
|
||||||
|
options.sysroot.mkpath
|
||||||
|
|
||||||
|
sh 'contrib/download_prerequisites', chdir: source_directory.to_path
|
||||||
|
configure_options = [
|
||||||
|
"--prefix=#{options.rootfs.realpath}",
|
||||||
|
"--with-sysroot=#{options.sysroot.realpath}",
|
||||||
|
'--enable-languages=c,c++',
|
||||||
|
'--disable-shared',
|
||||||
|
'--with-arch=rv32imafdc',
|
||||||
|
'--with-abi=ilp32d',
|
||||||
|
'--with-tune=rocket',
|
||||||
|
'--with-isa-spec=20191213',
|
||||||
|
'--disable-bootstrap',
|
||||||
|
'--disable-multilib',
|
||||||
|
'--disable-libmudflap',
|
||||||
|
'--disable-libssp',
|
||||||
|
'--disable-libquadmath',
|
||||||
|
'--disable-libsanitizer',
|
||||||
|
'--disable-threads',
|
||||||
|
'--disable-libatomic',
|
||||||
|
'--disable-libgomp',
|
||||||
|
'--disable-libvtv',
|
||||||
|
'--disable-libstdcxx',
|
||||||
|
'--disable-nls',
|
||||||
|
'--with-newlib',
|
||||||
|
'--without-headers',
|
||||||
|
"--target=#{options.target}",
|
||||||
|
"--build=#{options.build}",
|
||||||
|
"--host=#{options.build}"
|
||||||
|
]
|
||||||
|
flags = '-O2 -fPIC'
|
||||||
|
env = {
|
||||||
|
'CC' => options.gcc,
|
||||||
|
'CXX' => options.gxx,
|
||||||
|
'CFLAGS' => flags,
|
||||||
|
'CXXFLAGS' => flags,
|
||||||
|
'PATH' => "#{options.rootfs.realpath + 'bin'}:#{ENV['PATH']}"
|
||||||
|
}
|
||||||
|
configure = source_directory.relative_path_from(cwd) + 'configure'
|
||||||
|
sh env, configure.to_path, *configure_options, chdir: cwd.to_path
|
||||||
|
sh env, 'make', '-j', Etc.nprocessors.to_s, chdir: cwd.to_path
|
||||||
|
sh env, 'make', 'install', chdir: cwd.to_path
|
||||||
|
end
|
||||||
|
|
||||||
|
desc 'Copy glibc headers'
|
||||||
|
task :headers, [:target] do |_, args|
|
||||||
|
options = find_build_target GCC_VERSION, args
|
||||||
|
options.tools.mkpath
|
||||||
|
|
||||||
|
source_directory = download_and_unarchive(
|
||||||
|
URI.parse("https://ftp.gnu.org/gnu/glibc/glibc-#{GLIBC_VERSION}.tar.xz"),
|
||||||
|
options.tools)
|
||||||
|
include_directory = options.tools + 'include'
|
||||||
|
|
||||||
|
include_directory.mkpath
|
||||||
|
cp (source_directory + 'elf/elf.h'), (include_directory + 'elf.h')
|
||||||
|
end
|
||||||
|
|
||||||
|
desc 'Build linux kernel'
|
||||||
|
task :kernel, [:target] do |_, args|
|
||||||
|
options = find_build_target GCC_VERSION, args
|
||||||
|
options.tools.mkpath
|
||||||
|
|
||||||
|
cwd = download_and_unarchive(
|
||||||
|
URI.parse("https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-#{KERNEL_VERSION}.tar.xz"),
|
||||||
|
options.tools)
|
||||||
|
|
||||||
|
env = {
|
||||||
|
'CROSS_COMPILE' => "#{options.target}-",
|
||||||
|
'ARCH' => 'riscv',
|
||||||
|
'PATH' => "#{options.rootfs.realpath + 'bin'}:#{ENV['PATH']}",
|
||||||
|
'HOSTCFLAGS' => "-D_UUID_T -D__GETHOSTUUID_H -I#{options.tools.realpath + 'include'}"
|
||||||
|
}
|
||||||
|
sh env, 'make', 'rv32_defconfig', chdir: cwd.to_path
|
||||||
|
sh env, 'make', '-j', Etc.nprocessors.to_s, chdir: cwd.to_path
|
||||||
|
sh env, 'make', 'headers', chdir: cwd.to_path
|
||||||
|
|
||||||
|
user_directory = options.sysroot + 'usr'
|
||||||
|
|
||||||
|
user_directory.mkpath
|
||||||
|
cp_r (cwd + 'usr/include'), (user_directory + 'include')
|
||||||
|
end
|
||||||
|
|
||||||
|
desc 'Build glibc'
|
||||||
|
task :glibc, [:target] do |_, args|
|
||||||
|
options = find_build_target GCC_VERSION, args
|
||||||
|
source_directory = options.tools + "glibc-#{GLIBC_VERSION}"
|
||||||
|
configure_options = [
|
||||||
|
'--prefix=/usr',
|
||||||
|
"--host=#{options.target}",
|
||||||
|
"--target=#{options.target}",
|
||||||
|
"--build=#{options.build}",
|
||||||
|
"--enable-kernel=#{KERNEL_VERSION}",
|
||||||
|
"--with-headers=#{options.sysroot.realpath + 'usr/include'}",
|
||||||
|
'--disable-nscd',
|
||||||
|
'--disable-libquadmath',
|
||||||
|
'--disable-libitm',
|
||||||
|
'--disable-werror',
|
||||||
|
'libc_cv_forced_unwind=yes'
|
||||||
|
]
|
||||||
|
bin = options.rootfs.realpath + 'bin'
|
||||||
|
env = {
|
||||||
|
'PATH' => "#{bin}:#{ENV['PATH']}",
|
||||||
|
'MAKE' => 'make' # Otherwise it uses gnumake which can be different and too old.
|
||||||
|
}
|
||||||
|
cwd = source_directory.dirname + 'build-glibc'
|
||||||
|
cwd.mkpath
|
||||||
|
|
||||||
|
configure = source_directory.relative_path_from(cwd) +'./configure'
|
||||||
|
sh env, configure.to_path, *configure_options, chdir: cwd.to_path
|
||||||
|
sh env, 'make', '-j', Etc.nprocessors.to_s, chdir: cwd.to_path
|
||||||
|
sh env, 'make', "install_root=#{options.sysroot.realpath}", 'install', chdir: cwd.to_path
|
||||||
|
end
|
||||||
|
|
||||||
|
desc 'Build stage 2 GCC'
|
||||||
|
task :gcc2, [:target] do |_, args|
|
||||||
|
options = find_build_target GCC_VERSION, args
|
||||||
|
source_directory = options.tools + "gcc-#{GCC_VERSION}"
|
||||||
|
cwd = options.tools + 'build-gcc'
|
||||||
|
|
||||||
|
rm_rf cwd
|
||||||
|
cwd.mkpath
|
||||||
|
|
||||||
|
configure_options = [
|
||||||
|
"--prefix=#{options.rootfs.realpath}",
|
||||||
|
"--with-sysroot=#{options.sysroot.realpath}",
|
||||||
|
'--enable-languages=c,c++,lto',
|
||||||
|
'--enable-lto',
|
||||||
|
'--enable-shared',
|
||||||
|
'--with-arch=rv32imafdc',
|
||||||
|
'--with-abi=ilp32d',
|
||||||
|
'--with-tune=rocket',
|
||||||
|
'--with-isa-spec=20191213',
|
||||||
|
'--disable-bootstrap',
|
||||||
|
'--disable-multilib',
|
||||||
|
'--enable-checking=release',
|
||||||
|
'--disable-libssp',
|
||||||
|
'--disable-libquadmath',
|
||||||
|
'--enable-threads=posix',
|
||||||
|
'--with-default-libstdcxx-abi=new',
|
||||||
|
'--disable-nls',
|
||||||
|
"--target=#{options.target}",
|
||||||
|
"--build=#{options.build}",
|
||||||
|
"--host=#{options.build}"
|
||||||
|
|
||||||
|
]
|
||||||
|
flags = '-O2 -fPIC'
|
||||||
|
env = {
|
||||||
|
'CFLAGS' => flags,
|
||||||
|
'CXXFLAGS' => flags,
|
||||||
|
'PATH' => "#{options.rootfs.realpath + 'bin'}:#{ENV['PATH']}"
|
||||||
|
}
|
||||||
|
configure = source_directory.relative_path_from(cwd) + 'configure'
|
||||||
|
sh env, configure.to_path, *configure_options, chdir: cwd.to_path
|
||||||
|
sh env, 'make', '-j', Etc.nprocessors.to_s, chdir: cwd.to_path
|
||||||
|
sh env, 'make', 'install', chdir: cwd.to_path
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
desc 'Build cross toolchain'
|
||||||
|
task cross: [
|
||||||
|
'cross:binutils',
|
||||||
|
'cross:gcc1',
|
||||||
|
'cross:headers',
|
||||||
|
'cross:kernel',
|
||||||
|
'cross:glibc',
|
||||||
|
'cross:gcc2'
|
||||||
|
] do
|
||||||
|
end
|
15
source/CommandLineInterface.def
Normal file
15
source/CommandLineInterface.def
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
DEFINITION MODULE CommandLineInterface;
|
||||||
|
|
||||||
|
FROM Common IMPORT ShortString;
|
||||||
|
|
||||||
|
TYPE
|
||||||
|
CommandLine = RECORD
|
||||||
|
input: ShortString;
|
||||||
|
lex: BOOLEAN;
|
||||||
|
parse: BOOLEAN
|
||||||
|
END;
|
||||||
|
PCommandLine = POINTER TO CommandLine;
|
||||||
|
|
||||||
|
PROCEDURE parse_command_line(): PCommandLine;
|
||||||
|
|
||||||
|
END CommandLineInterface.
|
75
source/CommandLineInterface.elna
Normal file
75
source/CommandLineInterface.elna
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
implementation module CommandLineInterface;
|
||||||
|
|
||||||
|
from SYSTEM import ADR, TSIZE;
|
||||||
|
|
||||||
|
from Args import GetArg, Narg;
|
||||||
|
from FIO import WriteString, WriteChar, WriteLine, StdErr;
|
||||||
|
from Storage import ALLOCATE;
|
||||||
|
from Strings import CompareStr, Length;
|
||||||
|
from MemUtils import MemZero;
|
||||||
|
|
||||||
|
from Common import ShortString;
|
||||||
|
|
||||||
|
proc parse_command_line() -> PCommandLine;
|
||||||
|
var
|
||||||
|
parameter: ShortString;
|
||||||
|
i: CARDINAL;
|
||||||
|
result: PCommandLine;
|
||||||
|
parsed: BOOLEAN;
|
||||||
|
begin
|
||||||
|
i := 1;
|
||||||
|
ALLOCATE(result, TSIZE(CommandLine));
|
||||||
|
result^.lex := false;
|
||||||
|
result^.parse := false;
|
||||||
|
MemZero(ADR(result^.input), 256);
|
||||||
|
|
||||||
|
while (i < Narg()) & (result <> nil) do
|
||||||
|
parsed := GetArg(parameter, i);
|
||||||
|
parsed := false;
|
||||||
|
|
||||||
|
if CompareStr(parameter, '--lex') = 0 then
|
||||||
|
parsed := true;
|
||||||
|
result^.lex := true
|
||||||
|
end;
|
||||||
|
if CompareStr(parameter, '--parse') = 0 then
|
||||||
|
parsed := true;
|
||||||
|
result^.parse := true
|
||||||
|
end;
|
||||||
|
if parameter[0] <> '-' then
|
||||||
|
parsed := true;
|
||||||
|
|
||||||
|
if Length(result^.input) > 0 then
|
||||||
|
WriteString(StdErr, 'Fatal error: only one source file can be compiled at once. First given "');
|
||||||
|
WriteString(StdErr, result^.input);
|
||||||
|
WriteString(StdErr, '", then "');
|
||||||
|
WriteString(StdErr, parameter);
|
||||||
|
WriteString(StdErr, '".');
|
||||||
|
WriteLine(StdErr);
|
||||||
|
result := nil
|
||||||
|
end;
|
||||||
|
if result <> nil then
|
||||||
|
result^.input := parameter
|
||||||
|
end
|
||||||
|
end;
|
||||||
|
if parsed = false then
|
||||||
|
WriteString(StdErr, 'Fatal error: unknown command line options: ');
|
||||||
|
|
||||||
|
WriteString(StdErr, parameter);
|
||||||
|
WriteChar(StdErr, '.');
|
||||||
|
WriteLine(StdErr);
|
||||||
|
|
||||||
|
result := nil
|
||||||
|
end;
|
||||||
|
|
||||||
|
i := i + 1
|
||||||
|
end;
|
||||||
|
if (result <> nil) & (Length(result^.input) = 0) then
|
||||||
|
WriteString(StdErr, 'Fatal error: no input files.');
|
||||||
|
WriteLine(StdErr);
|
||||||
|
result := nil
|
||||||
|
end;
|
||||||
|
|
||||||
|
return result
|
||||||
|
end;
|
||||||
|
|
||||||
|
end CommandLineInterface.
|
8
source/Common.def
Normal file
8
source/Common.def
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
DEFINITION MODULE Common;
|
||||||
|
|
||||||
|
TYPE
|
||||||
|
ShortString = ARRAY[0..255] OF CHAR;
|
||||||
|
Identifier = ARRAY[1..256] OF CHAR;
|
||||||
|
PIdentifier = POINTER TO Identifier;
|
||||||
|
|
||||||
|
END Common.
|
3
source/Common.elna
Normal file
3
source/Common.elna
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
implementation module Common;
|
||||||
|
|
||||||
|
end Common.
|
@ -1,18 +1,51 @@
|
|||||||
module Compiler;
|
module Compiler;
|
||||||
|
|
||||||
from FIO import StdIn;
|
from FIO import Close, IsNoError, File, OpenToRead, StdErr, StdOut, WriteLine, WriteString;
|
||||||
from SYSTEM import ADR;
|
from SYSTEM import ADR;
|
||||||
|
from M2RTS import HALT, ExitOnHalt;
|
||||||
|
|
||||||
from Lexer import Lexer, lexer_destroy, lexer_initialize;
|
from Lexer import Lexer, lexer_destroy, lexer_initialize;
|
||||||
from Transpiler import transpile;
|
from Transpiler import transpile;
|
||||||
|
from CommandLineInterface import PCommandLine, parse_command_line;
|
||||||
|
|
||||||
|
var
|
||||||
|
command_line: PCommandLine;
|
||||||
|
|
||||||
|
proc compile_from_stream();
|
||||||
var
|
var
|
||||||
lexer: Lexer;
|
lexer: Lexer;
|
||||||
|
source_input: File;
|
||||||
|
begin
|
||||||
|
source_input := OpenToRead(command_line^.input);
|
||||||
|
|
||||||
|
if IsNoError(source_input) = false then
|
||||||
|
WriteString(StdErr, 'Fatal error: failed to read the input file "');
|
||||||
|
WriteString(StdErr, command_line^.input);
|
||||||
|
WriteString(StdErr, '".');
|
||||||
|
WriteLine(StdErr);
|
||||||
|
|
||||||
|
ExitOnHalt(2)
|
||||||
|
end;
|
||||||
|
if IsNoError(source_input) then
|
||||||
|
lexer_initialize(ADR(lexer), source_input);
|
||||||
|
|
||||||
|
transpile(ADR(lexer), StdOut);
|
||||||
|
|
||||||
|
lexer_destroy(ADR(lexer));
|
||||||
|
|
||||||
|
Close(source_input)
|
||||||
|
end
|
||||||
|
end;
|
||||||
|
|
||||||
begin
|
begin
|
||||||
lexer_initialize(ADR(lexer), StdIn);
|
ExitOnHalt(0);
|
||||||
|
command_line := parse_command_line();
|
||||||
|
|
||||||
transpile(ADR(lexer));
|
if command_line <> nil then
|
||||||
|
compile_from_stream()
|
||||||
lexer_destroy(ADR(lexer))
|
end;
|
||||||
|
if command_line = nil then
|
||||||
|
ExitOnHalt(1)
|
||||||
|
end;
|
||||||
|
HALT()
|
||||||
end Compiler.
|
end Compiler.
|
||||||
|
@ -2,6 +2,8 @@ DEFINITION MODULE Lexer;
|
|||||||
|
|
||||||
FROM FIO IMPORT File;
|
FROM FIO IMPORT File;
|
||||||
|
|
||||||
|
FROM Common IMPORT Identifier;
|
||||||
|
|
||||||
TYPE
|
TYPE
|
||||||
PLexerBuffer = POINTER TO CHAR;
|
PLexerBuffer = POINTER TO CHAR;
|
||||||
Lexer = RECORD
|
Lexer = RECORD
|
||||||
@ -81,7 +83,8 @@ TYPE
|
|||||||
LexerToken = RECORD
|
LexerToken = RECORD
|
||||||
CASE kind: LexerKind OF
|
CASE kind: LexerKind OF
|
||||||
lexerKindBoolean: booleanKind: BOOLEAN |
|
lexerKindBoolean: booleanKind: BOOLEAN |
|
||||||
lexerKindIdentifier: identifierKind: ARRAY[1..256] OF CHAR
|
lexerKindIdentifier: identifierKind: Identifier |
|
||||||
|
lexerKindInteger: integerKind: INTEGER
|
||||||
END
|
END
|
||||||
END;
|
END;
|
||||||
PLexerToken = POINTER TO LexerToken;
|
PLexerToken = POINTER TO LexerToken;
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
implementation module Lexer;
|
implementation module Lexer;
|
||||||
|
|
||||||
from FIO import ReadNBytes;
|
from FIO import ReadNBytes, StdErr;
|
||||||
from SYSTEM import ADR;
|
from SYSTEM import ADR, TSIZE;
|
||||||
|
|
||||||
|
from DynamicStrings import String, InitStringCharStar, KillString;
|
||||||
|
from StringConvert import StringToInteger;
|
||||||
from Storage import DEALLOCATE, ALLOCATE;
|
from Storage import DEALLOCATE, ALLOCATE;
|
||||||
from Strings import Length;
|
from Strings import Length;
|
||||||
from MemUtils import MemCopy, MemZero;
|
from MemUtils import MemCopy, MemZero;
|
||||||
@ -211,13 +213,13 @@ begin
|
|||||||
end
|
end
|
||||||
end;
|
end;
|
||||||
|
|
||||||
proc compare_keyword(Keyword: ARRAY OF CHAR, TokenStart: PLexerBuffer, TokenEnd: PLexerBuffer) -> BOOLEAN;
|
proc compare_keyword(Keyword: ARRAY OF CHAR, TokenStart: PLexerBuffer, TokenEnd: PLexerBuffer) -> BOOLEAN;
|
||||||
var
|
var
|
||||||
result: BOOLEAN;
|
result: BOOLEAN;
|
||||||
index: CARDINAL;
|
index: CARDINAL;
|
||||||
begin
|
begin
|
||||||
index := 0;
|
index := 0;
|
||||||
result := TRUE;
|
result := true;
|
||||||
|
|
||||||
while (index < Length(Keyword)) & (TokenStart <> TokenEnd) & result DO
|
while (index < Length(Keyword)) & (TokenStart <> TokenEnd) & result DO
|
||||||
result := (Keyword[index] = TokenStart^) or (Lower(Keyword[index]) = TokenStart^);
|
result := (Keyword[index] = TokenStart^) or (Lower(Keyword[index]) = TokenStart^);
|
||||||
@ -229,20 +231,20 @@ begin
|
|||||||
end;
|
end;
|
||||||
|
|
||||||
(* Reached the end of file. *)
|
(* Reached the end of file. *)
|
||||||
proc transition_action_eof(lexer: PLexer, token: PLexerToken);
|
proc transition_action_eof(lexer: PLexer, token: PLexerToken);
|
||||||
begin
|
begin
|
||||||
token^.kind := lexerKindEof
|
token^.kind := lexerKindEof
|
||||||
end;
|
end;
|
||||||
|
|
||||||
(* Add the character to the token currently read and advance to the next character. *)
|
(* Add the character to the token currently read and advance to the next character. *)
|
||||||
proc transition_action_accumulate(lexer: PLexer, token: PLexerToken);
|
proc transition_action_accumulate(lexer: PLexer, token: PLexerToken);
|
||||||
begin
|
begin
|
||||||
INC(lexer^.Current)
|
INC(lexer^.Current)
|
||||||
end;
|
end;
|
||||||
|
|
||||||
(* The current character is not a part of the token. Finish the token already
|
(* The current character is not a part of the token. Finish the token already
|
||||||
* read. Don't advance to the next character. *)
|
* read. Don't advance to the next character. *)
|
||||||
proc transition_action_finalize(lexer: PLexer, token: PLexerToken);
|
proc transition_action_finalize(lexer: PLexer, token: PLexerToken);
|
||||||
begin
|
begin
|
||||||
if lexer^.Start^ = ':' then
|
if lexer^.Start^ = ':' then
|
||||||
token^.kind := lexerKindColon
|
token^.kind := lexerKindColon
|
||||||
@ -265,7 +267,7 @@ begin
|
|||||||
end;
|
end;
|
||||||
|
|
||||||
(* An action for tokens containing multiple characters. *)
|
(* An action for tokens containing multiple characters. *)
|
||||||
proc transition_action_composite(lexer: PLexer, token: PLexerToken);
|
proc transition_action_composite(lexer: PLexer, token: PLexerToken);
|
||||||
begin
|
begin
|
||||||
if lexer^.Start^ = '<' then
|
if lexer^.Start^ = '<' then
|
||||||
if lexer^.Current^ = '>' then
|
if lexer^.Current^ = '>' then
|
||||||
@ -291,14 +293,14 @@ begin
|
|||||||
end;
|
end;
|
||||||
|
|
||||||
(* Skip a space. *)
|
(* Skip a space. *)
|
||||||
proc transition_action_skip(lexer: PLexer, token: PLexerToken);
|
proc transition_action_skip(lexer: PLexer, token: PLexerToken);
|
||||||
begin
|
begin
|
||||||
INC(lexer^.Current);
|
INC(lexer^.Current);
|
||||||
INC(lexer^.Start)
|
INC(lexer^.Start)
|
||||||
end;
|
end;
|
||||||
|
|
||||||
(* Delimited string action. *)
|
(* Delimited string action. *)
|
||||||
proc transition_action_delimited(lexer: PLexer, token: PLexerToken);
|
proc transition_action_delimited(lexer: PLexer, token: PLexerToken);
|
||||||
begin
|
begin
|
||||||
if lexer^.Start^ = '(' then
|
if lexer^.Start^ = '(' then
|
||||||
token^.kind := lexerKindComment
|
token^.kind := lexerKindComment
|
||||||
@ -313,7 +315,7 @@ begin
|
|||||||
end;
|
end;
|
||||||
|
|
||||||
(* Finalize keyword or identifier. *)
|
(* Finalize keyword or identifier. *)
|
||||||
proc transition_action_key_id(lexer: PLexer, token: PLexerToken);
|
proc transition_action_key_id(lexer: PLexer, token: PLexerToken);
|
||||||
begin
|
begin
|
||||||
token^.kind := lexerKindIdentifier;
|
token^.kind := lexerKindIdentifier;
|
||||||
|
|
||||||
@ -409,17 +411,17 @@ begin
|
|||||||
end;
|
end;
|
||||||
if compare_keyword('TRUE', lexer^.Start, lexer^.Current) then
|
if compare_keyword('TRUE', lexer^.Start, lexer^.Current) then
|
||||||
token^.kind := lexerKindBoolean;
|
token^.kind := lexerKindBoolean;
|
||||||
token^.booleanKind := TRUE
|
token^.booleanKind := true
|
||||||
end;
|
end;
|
||||||
if compare_keyword('FALSE', lexer^.Start, lexer^.Current) then
|
if compare_keyword('FALSE', lexer^.Start, lexer^.Current) then
|
||||||
token^.kind := lexerKindBoolean;
|
token^.kind := lexerKindBoolean;
|
||||||
token^.booleanKind := FALSE
|
token^.booleanKind := false
|
||||||
end
|
end
|
||||||
end;
|
end;
|
||||||
|
|
||||||
(* Action for tokens containing only one character. The character cannot be
|
(* Action for tokens containing only one character. The character cannot be
|
||||||
* followed by other characters forming a composite token. *)
|
* followed by other characters forming a composite token. *)
|
||||||
proc transition_action_single(lexer: PLexer, token: PLexerToken);
|
proc transition_action_single(lexer: PLexer, token: PLexerToken);
|
||||||
begin
|
begin
|
||||||
if lexer^.Current^ = '&' then
|
if lexer^.Current^ = '&' then
|
||||||
token^.kind := lexerKindAnd
|
token^.kind := lexerKindAnd
|
||||||
@ -467,12 +469,24 @@ begin
|
|||||||
end;
|
end;
|
||||||
|
|
||||||
(* Handle an integer literal. *)
|
(* Handle an integer literal. *)
|
||||||
proc transition_action_integer(lexer: PLexer, token: PLexerToken);
|
proc transition_action_integer(lexer: PLexer, token: PLexerToken);
|
||||||
|
var
|
||||||
|
buffer: String;
|
||||||
|
integer_length: CARDINAL;
|
||||||
|
found: BOOLEAN;
|
||||||
begin
|
begin
|
||||||
token^.kind := lexerKindInteger
|
token^.kind := lexerKindInteger;
|
||||||
|
|
||||||
|
integer_length := lexer^.Current - lexer^.Start;
|
||||||
|
MemZero(ADR(token^.identifierKind), TSIZE(Identifier));
|
||||||
|
MemCopy(lexer^.Start, integer_length, ADR(token^.identifierKind[1]));
|
||||||
|
|
||||||
|
buffer := InitStringCharStar(ADR(token^.identifierKind[1]));
|
||||||
|
token^.integerKind := StringToInteger(buffer, 10, found);
|
||||||
|
buffer := KillString(buffer)
|
||||||
end;
|
end;
|
||||||
|
|
||||||
proc set_default_transition(CurrentState: TransitionState, DefaultAction: TransitionAction, NextState: TransitionState);
|
proc set_default_transition(CurrentState: TransitionState, DefaultAction: TransitionAction, NextState: TransitionState);
|
||||||
var
|
var
|
||||||
DefaultTransition: Transition;
|
DefaultTransition: Transition;
|
||||||
begin
|
begin
|
||||||
@ -757,7 +771,7 @@ begin
|
|||||||
transitions[ORD(transitionStateDecimalSuffix) + 1][ORD(transitionClassX) + 1].NextState := transitionStateEnd
|
transitions[ORD(transitionStateDecimalSuffix) + 1][ORD(transitionClassX) + 1].NextState := transitionStateEnd
|
||||||
end;
|
end;
|
||||||
|
|
||||||
proc lexer_initialize(lexer: PLexer, Input: File);
|
proc lexer_initialize(lexer: PLexer, Input: File);
|
||||||
begin
|
begin
|
||||||
lexer^.Input := Input;
|
lexer^.Input := Input;
|
||||||
lexer^.Length := 0;
|
lexer^.Length := 0;
|
||||||
|
59
source/Parser.def
Normal file
59
source/Parser.def
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
DEFINITION MODULE Parser;
|
||||||
|
|
||||||
|
FROM Common IMPORT Identifier, PIdentifier;
|
||||||
|
|
||||||
|
TYPE
|
||||||
|
AstConstantDeclaration = RECORD
|
||||||
|
END;
|
||||||
|
PAstConstantDeclaration = POINTER TO AstConstantDeclaration;
|
||||||
|
PPAstConstantDeclaration = POINTER TO PAstConstantDeclaration;
|
||||||
|
|
||||||
|
AstFieldDeclaration = RECORD
|
||||||
|
field_name: Identifier;
|
||||||
|
field_type: PAstTypeExpression
|
||||||
|
END;
|
||||||
|
PAstFieldDeclaration = POINTER TO AstFieldDeclaration;
|
||||||
|
|
||||||
|
AstTypeExpressionKind = (
|
||||||
|
astTypeExpressionKindNamed,
|
||||||
|
astTypeExpressionKindRecord,
|
||||||
|
astTypeExpressionKindEnumeration,
|
||||||
|
astTypeExpressionKindArray,
|
||||||
|
astTypeExpressionKindPointer,
|
||||||
|
astTypeExpressionKindProcedure
|
||||||
|
);
|
||||||
|
AstTypeExpression = RECORD
|
||||||
|
CASE kind: AstTypeExpressionKind OF
|
||||||
|
astTypeExpressionKindNamed: name: Identifier |
|
||||||
|
astTypeExpressionKindEnumeration: cases: PIdentifier |
|
||||||
|
astTypeExpressionKindPointer: target: PAstTypeExpression |
|
||||||
|
astTypeExpressionKindRecord: fields: PAstFieldDeclaration |
|
||||||
|
astTypeExpressionKindArray:
|
||||||
|
base: PAstTypeExpression;
|
||||||
|
length: CARDINAL |
|
||||||
|
astTypeExpressionKindProcedure: parameters: PPAstTypeExpression
|
||||||
|
END
|
||||||
|
END;
|
||||||
|
PAstTypeExpression = POINTER TO AstTypeExpression;
|
||||||
|
PPAstTypeExpression = POINTER TO PAstTypeExpression;
|
||||||
|
|
||||||
|
AstTypeDeclaration = RECORD
|
||||||
|
identifier: Identifier;
|
||||||
|
type_expression: PAstTypeExpression
|
||||||
|
END;
|
||||||
|
PAstTypeDeclaration = POINTER TO AstTypeDeclaration;
|
||||||
|
PPAstTypeDeclaration = POINTER TO PAstTypeDeclaration;
|
||||||
|
|
||||||
|
AstVariableDeclaration = RECORD
|
||||||
|
END;
|
||||||
|
PAstVariableDeclaration = POINTER TO AstVariableDeclaration;
|
||||||
|
PPAstVariableDeclaration = POINTER TO PAstVariableDeclaration;
|
||||||
|
|
||||||
|
AstModule = RECORD
|
||||||
|
constants: PPAstConstantDeclaration;
|
||||||
|
types: PPAstTypeDeclaration;
|
||||||
|
variables: PPAstVariableDeclaration
|
||||||
|
END;
|
||||||
|
PAstModule = POINTER TO AstModule;
|
||||||
|
|
||||||
|
END Parser.
|
3
source/Parser.elna
Normal file
3
source/Parser.elna
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
implementation module Parser;
|
||||||
|
|
||||||
|
end Parser.
|
@ -1,7 +1,17 @@
|
|||||||
DEFINITION MODULE Transpiler;
|
DEFINITION MODULE Transpiler;
|
||||||
|
|
||||||
FROM Lexer IMPORT PLexer;
|
FROM FIO IMPORT File;
|
||||||
|
|
||||||
PROCEDURE transpile(ALexer: PLexer);
|
FROM Lexer IMPORT PLexer, Lexer;
|
||||||
|
|
||||||
|
TYPE
|
||||||
|
TranspilerContext = RECORD
|
||||||
|
indentation: CARDINAL;
|
||||||
|
output: File;
|
||||||
|
lexer: PLexer
|
||||||
|
END;
|
||||||
|
PTranspilerContext = POINTER TO TranspilerContext;
|
||||||
|
|
||||||
|
PROCEDURE transpile(lexer: PLexer; output: File);
|
||||||
|
|
||||||
END Transpiler.
|
END Transpiler.
|
||||||
|
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user