1+ local function parse_yaml (content )
2+ local data = {}
3+ local key = nil
4+ local item = {}
5+ local in_details = false
6+
7+ for line in content :gmatch (" [^\r\n ]+" ) do
8+ local line_trimmed = line :gsub (" ^%s+" , " " ):gsub (" %s+$" , " " ) -- trim whitespace
9+ local indent_level = # (line :match (" ^%s*" ) or " " )
10+
11+ if line_trimmed == " " then
12+ -- Skip empty lines
13+ elseif indent_level == 0 and line_trimmed :match (" ^[%w_-]+:$" ) then
14+ if key and next (item ) then
15+ data [key ] = item
16+ end
17+ key = line_trimmed :gsub (" :$" , " " )
18+ item = {}
19+ in_details = false
20+ elseif indent_level > 0 and line_trimmed :match (" ^title:%s*(.+)" ) then
21+ item .title = line_trimmed :match (" ^title:%s*(.+)" )
22+ elseif indent_level > 0 and line_trimmed :match (" ^location:%s*(.+)" ) then
23+ item .location = line_trimmed :match (" ^location:%s*(.+)" )
24+ elseif indent_level > 0 and line_trimmed :match (" ^date:%s*(.+)" ) then
25+ item .date = line_trimmed :match (" ^date:%s*(.+)" )
26+ elseif indent_level > 0 and line_trimmed :match (" ^description:%s*(.+)" ) then
27+ item .description = line_trimmed :match (" ^description:%s*(.+)" )
28+ elseif indent_level > 0 and line_trimmed :match (" ^details:%s*$" ) then
29+ item .details = {}
30+ in_details = true
31+ elseif in_details and line :match (" ^%s*-%s*(.+)" ) then
32+ local detail = line :match (" ^%s*-%s*(.+)" )
33+ table.insert (item .details , detail )
34+ elseif in_details and indent_level > 0 and not line :match (" ^%s*-%s*" ) then
35+ in_details = false
36+ end
37+ end
38+
39+ -- Add the last item
40+ if key and next (item ) then
41+ data [key ] = item
42+ end
43+
44+ return data
45+ end
46+
47+ local function construct_entry (data )
48+ local typst_code = {}
49+
50+ -- Convert table to array if it's a dictionary
51+ local entries = {}
52+ if data and type (data ) == " table" then
53+ -- Check if it's a dictionary (has string keys)
54+ local is_dict = false
55+ for k , v in pairs (data ) do
56+ if type (k ) == " string" then
57+ is_dict = true
58+ break
59+ end
60+ end
61+
62+ if is_dict then
63+ -- Convert dictionary values to array
64+ for k , v in pairs (data ) do
65+ table.insert (entries , v )
66+ end
67+ else
68+ entries = data
69+ end
70+ end
71+
72+ for _ , item in ipairs (entries ) do
73+ local entry_parts = {}
74+ table.insert (entry_parts , " #resume-entry(" )
75+
76+ -- Add title
77+ if item .title then
78+ table.insert (entry_parts , ' title: [' .. tostring (item .title ).. ' ],' )
79+ else
80+ table.insert (entry_parts , ' title: none,' )
81+ end
82+
83+ -- Add location
84+ if item .location then
85+ table.insert (entry_parts , ' location: [' .. tostring (item .location ).. ' ],' )
86+ else
87+ table.insert (entry_parts , ' location: none,' )
88+ end
89+
90+ -- Add date
91+ if item .date then
92+ table.insert (entry_parts , ' date: [' .. tostring (item .date ).. ' ],' )
93+ else
94+ table.insert (entry_parts , ' date: none,' )
95+ end
96+
97+ -- Add description
98+ if item .description then
99+ table.insert (entry_parts , ' description: [' .. tostring (item .description ).. ' ],' )
100+ else
101+ table.insert (entry_parts , ' description: none,' )
102+ end
103+
104+ table.insert (entry_parts , " )" )
105+
106+ -- Add details if present
107+ if item .details and type (item .details ) == " table" then
108+ table.insert (entry_parts , " #resume-item[" )
109+ if # item .details > 0 then
110+ -- Array
111+ for _ , detail in ipairs (item .details ) do
112+ table.insert (entry_parts , " - " .. tostring (detail ))
113+ end
114+ else
115+ -- Dictionary
116+ for _ , detail in pairs (item .details ) do
117+ table.insert (entry_parts , " - " .. tostring (detail ))
118+ end
119+ end
120+ table.insert (entry_parts , " ]" )
121+ end
122+
123+ table.insert (typst_code , table.concat (entry_parts , " \n " ))
124+ end
125+
126+ return table.concat (typst_code , " \n\n " )
127+ end
128+
129+ function yaml (args )
130+ local file_path = args [1 ]
131+ if not file_path then
132+ error (" yaml shortcode requires a file path argument" )
133+ end
134+
135+ -- Read YAML file
136+ local file = io.open (file_path , " r" )
137+ if not file then
138+ error (" Could not open file: " .. file_path )
139+ end
140+ local text_yaml = file :read (" *all" )
141+ file :close ()
142+
143+ -- Convert YAML to Typst
144+ local data = parse_yaml (text_yaml )
145+ local code_typst = construct_entry (data )
146+ return pandoc .RawInline (' typst' , code_typst )
147+ end
0 commit comments