add basic macro expansion
Some checks are pending
CI / Julia 1.6 - ubuntu-latest - x64 (push) Waiting to run
CI / Julia 1.7 - ubuntu-latest - x64 (push) Waiting to run
CI / Julia pre - ubuntu-latest - x64 (push) Waiting to run

This commit is contained in:
Tan, Kian-ting 2025-01-25 02:23:13 +08:00
parent f010267f11
commit 38a752be5a
6 changed files with 150 additions and 25 deletions

View file

@ -5,6 +5,7 @@ version = "1.0.0-DEV"
[deps]
ArgParse = "c7e460c6-2fb9-53a9-8c5b-16f535851c63"
Match = "7eb4fadd-790c-5f42-8a69-bfa0b872bfbf"
ParserCombinator = "fae87a5f-d1ad-5cf0-8f61-c941e1580b46"
libharu_jll = "d4e8948d-4b7e-5538-9d15-404f6f0a9070"

View file

@ -1,2 +1,2 @@
@foo
{@foo|@bar|12\|}

13
src/classes.jl Normal file
View file

@ -0,0 +1,13 @@
module Classes
abstract type Node end
struct ID<:Node val end
struct SEQ<:Node val end # like (a b c) in scheme
struct ELE<:Node val end #an element in a seq
struct ESC_CHAR<:Node val end # character preceded by escape char "\"
struct CHAR<:Node val end #character
struct SPACE<:Node val end # space
struct NL<:Node val end # newline
struct PROG<:Node val end # all the program
# pattern in regex form
struct PTN_RGX<:Node val::Regex end
end

View file

@ -1,52 +1,130 @@
module Parsing
using ParserCombinator
abstract type Node end
struct ID<:Node val end
struct SEQ<:Node val end # like (a b c) in scheme
struct ELE<:Node val end #an element in a seq
struct ESC_CHAR<:Node val end # character preceded by escape char "\"
using Match
include("passes.jl")
using .Passes
#=
grammar rules of uahgi
=#
comment = p"\%[^%]+\%"
newline = p"(\r?\n)"
space = p"[ \t]"
comment = P"\%[^%]+\%"
newline = P"(\r?\n)" |> Passes.Classes.NL
space = p"[ \t]" > Passes.Classes.SPACE
id_name = p"[_a-zA-Z][_0-9a-zA-Z]*" > ID
id_name = p"[_a-zA-Z][_0-9a-zA-Z]*" > Passes.Classes.ID
id = E"@" + id_name
char = p"[^ \n\r\t\\]"#[1:2,:?]
char = p"[^ \n\r\t\\]" |> Passes.Classes.CHAR #[1:2,:?]
# chars should be preceded by "\" are \, {, }, |, @, %
esc_char = p"[\{\|\}\@\%]" > ESC_CHAR
esc_char = p"[\{\|\}\@\%]" > Passes.Classes.ESC_CHAR
esc_combined = E"\\" + esc_char
#=
seq = (foo x1 x2 " ")
=> {@foo|x1|x2| }
=#
char_and_combined = char | esc_combined
seq_item = id | Repeat(char_and_combined) |> ELE
seq_item = id | Repeat(char_and_combined) |> Passes.Classes.ELE
seq_item_rest = E"|" + seq_item
seq_inner = seq_item + (seq_item_rest)[0:end] |> SEQ
seq_inner = seq_item + (seq_item_rest)[0:end] |> Passes.Classes.SEQ
seq = E"{" + seq_inner + E"}"
part = seq | comment | space | newline | id | char
all = Repeat(part) + Eos()
all = (Repeat(part) + Eos()) |> Passes.Classes.PROG
function parse(input)
print(input)
b = parse_one(input, all)
print("\n" * string(b) * "\n")
ast = parse_one(input, all)[1]
print("\n" * string(ast) * "\n")
#print(parse_one(, Pattern(r".b.")))
passes = Passes.processed_passes
ast_val = ast.val
for pass in passes
ast_val = use_pass(ast_val, pass)
end
new_ast = Passes.Classes.PROG(ast_val)
print(ast_to_string(new_ast))
return ast
end
function ast_pattern_matched(pattern, ast_head)
zipped = zip(pattern, ast_head)
zipped_mapped = map(x -> match_unit(x), zipped)
is_all_matched = reduce((x,y)-> x && y, zipped_mapped)
return is_all_matched
end
function match_unit(pair)
pattern = pair[1]
ast_item = pair[2]
if typeof(pattern) == Passes.Classes.PTN_RGX
is_matched = match(pattern.val, ast_item.val)
return is_matched
else
return pattern.val == ast_item.val
end
end
function use_pass(ast_val, pass)
pass_pattern = pass.pattern
pass_pattern_length = length(pass_pattern)
if length(ast_val) < pass_pattern_length
return ast_val
else
ast_head = ast_val[1:pass_pattern_length]
if ast_pattern_matched(pass_pattern, ast_head)
ast_head = pass.func(ast_head)
remained = use_pass([ast_head[2:end];ast_val[pass_pattern_length+1:end]], pass)
ast_val = [ast_head[1]; remained]
else
return ast_val
end
end
end
function ast_to_string(ast)
item = 5
str = @match ast begin
Passes.Classes.PROG(v) => begin
prog_inner = reduce( (x, y) -> x*" "*y, map(i -> ast_to_string(i), v))
return "[" * prog_inner * "]"
end
Passes.Classes.SEQ(v) => begin
prog_inner = reduce( (x, y) -> x*" "*y, map(i -> ast_to_string(i), v))
return "(" * prog_inner * ")"
end
Passes.Classes.ID(v) => "[ID: " * v * "]"
Passes.Classes.ELE(v) => begin
prog_inner = reduce( (x, y) -> x*" "*y, map(i -> ast_to_string(i), v))
return "[ELE : (" * prog_inner * ")]"
end
Passes.Classes.ESC_CHAR(ch) => "[ESC:\"" * ch[1] * "\"]"
Passes.Classes.CHAR(ch) => "\"" * ch[1] * "\""
Passes.Classes.NL(_) => "NL"
Passes.Classes.SPACE(_) => "SPACE"
_ => string(ast)
end
return str
end
# Write your package code here.
#export dog
#export cat
#dog = 1.2
#cat = 1.2
end

29
src/passes.jl Normal file
View file

@ -0,0 +1,29 @@
module Passes
include("classes.jl")
using .Classes
export processed_passes, Pass
processed_passes = []
struct Pass
pattern
func
end
####definition of passes ####
# 2 newline become @par{}
function two_nl_to_par_pass_func(two_nl)
return [Classes.SEQ([Classes.ID("par")])]
end
two_nl_to_par_pattern = [Classes.NL([]), Classes.NL([])] #two continuous newline
two_nl_to_par_pass = Pass(two_nl_to_par_pattern,
two_nl_to_par_pass_func)
push!(processed_passes, two_nl_to_par_pass)
end

View file

@ -1,5 +1,5 @@
module uahgi
include("./parsing.jl")
include("parsing.jl")
using .Parsing
using ArgParse
@ -12,7 +12,7 @@ function parse_commandline()
@add_arg_table! s begin
"FILE"
help = "the file path to be converted."
required = true
#required = true
end
return parse_args(s)
@ -21,7 +21,11 @@ end
function main()
parsed_args = parse_commandline()
if parsed_args["FILE"] === nothing
file_path = "./example/ex1.ug" # for test
else
file_path = parsed_args["FILE"]
end
file_content = open(f->read(f, String), file_path)
Parsing.parse(file_content)
end