Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .github/workflows/check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ jobs:
- uses: actions/checkout@v4

- name: install
if: ${{ matrix.os != 'windows-latest' }}
run: |
curl -fsSL https://cli.moonbitlang.com/install/unix.sh | bash
echo "$HOME/.moon/bin" >> $GITHUB_PATH
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ target/
.mooncakes/

trace.json
.DS_Store
8 changes: 5 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,14 @@ Docs: https://mooncakes.io/docs/#/tiye/cirru-parser/lib/members
moon add tiye/cirru-parser
```

```moon
```moonbit
typealias @cirru_parser.Cirru

// parse Cirru code
@cirru_parser.parse(code: String) : Array[Cirru]!CirruParseError
Cirru::parse(code: String) : Array[Cirru]!CirruParseError

// format Cirru code
@cirru_parser.format(cirru: Array[Cirru], {use_inline: false}) : String!FormatCirruError
Cirru::format(cirru: Array[Cirru], {use_inline: false}) : String!FormatCirruError
```

### License
Expand Down
2 changes: 1 addition & 1 deletion moon.mod.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "tiye/cirru-parser",
"version": "0.0.13",
"version": "0.1.0",
"deps": {},
"readme": "README.md",
"repository": "https://github.com/Cirru/parser.mbt",
Expand Down
151 changes: 75 additions & 76 deletions src/lib/parser.mbt
Original file line number Diff line number Diff line change
Expand Up @@ -17,48 +17,47 @@ fn build_exprs(tokens : Array[CirruLexItem]) -> Array[Cirru]!CirruParseError {
None => return acc
Some(ck) =>
match ck {
CirruLexItem::Open => {
let mut pointer : Array[Cirru] = Array::new()
Open => {
let mut pointer : Array[Cirru] = Array::new(capacity=8)
// guess a nested level of 16
let pointer_stack : Array[Array[Cirru]] = Array::new()
let pointer_stack : Array[Array[Cirru]] = Array::new(capacity=16)
for {
let cursor = pull_token()
match cursor {
None => raise CirruParseError("unexpected end of file")
Some(c) =>
match c {
CirruLexItem::Close =>
Close =>
match pointer_stack.pop() {
None => {
acc.push(Cirru::List(pointer))
acc.push(List(pointer))
break
}
Some(v) => {
let prev_p = pointer
pointer = v
pointer.push(Cirru::List(prev_p))
pointer.push(List(prev_p))
}
}
CirruLexItem::Open => {
Open => {
pointer_stack.push(pointer)
pointer = Array::new()
}
CirruLexItem::Str(s) => pointer.push(Cirru::Leaf(s))
CirruLexItem::Indent(n) =>
raise CirruParseError("unknown indent: \{n}")
Str(s) => pointer.push(Leaf(s))
Indent(n) => raise CirruParseError("unknown indent: \{n}")
}
}
}
}
CirruLexItem::Close => raise CirruParseError("unexpected \")\"")
Close => raise CirruParseError("unexpected \")\"")
a => raise CirruParseError("unknown item: \{a}")
}
}
}
}

///| main function to parse Cirru code into syntax tree
pub fn parse(code : String) -> Array[Cirru]!CirruParseError {
pub fn Cirru::parse(code : String) -> Array[Cirru]!CirruParseError {
let tokens = resolve_indentations(lex!(code))
// println("tokens: \{tokens}")
let tree = build_exprs!(tokens)
Expand All @@ -79,7 +78,7 @@ pub fn to_string(self : CirruParseError) -> String {
///|
fn parse_indentation(size : Int) -> CirruLexItem!CirruParseError {
if size % 2 == 0 {
CirruLexItem::Indent(size >> 1)
Indent(size >> 1)
} else {
raise CirruParseError("odd indentation size: \{size}")
}
Expand All @@ -88,7 +87,7 @@ fn parse_indentation(size : Int) -> CirruLexItem!CirruParseError {
///| lexer is a simpler state machine to tokenize Cirru code
fn lex(initial_code : String) -> Array[CirruLexItem]!CirruParseError {
let acc = []
let mut state = CirruLexState::Indent
let mut state : CirruLexState = Indent
let mut buffer = ""
let code = initial_code.to_array()
for idx, c in code {
Expand All @@ -97,103 +96,103 @@ fn lex(initial_code : String) -> Array[CirruLexItem]!CirruParseError {
// "lexing \{c.to_string().escape()} in \{state} and \{buffer.escape()}",
// )
match state {
CirruLexState::Space =>
Space =>
match c {
' ' => {
state = CirruLexState::Space
state = Space
buffer = ""
}
'\n' => {
state = CirruLexState::Indent
state = Indent
buffer = ""
}
'(' => {
acc.push(CirruLexItem::Open)
state = CirruLexState::Space
acc.push(Open)
state = Space
buffer = ""
}
')' => {
acc.push(CirruLexItem::Close)
state = CirruLexState::Space
acc.push(Close)
state = Space
buffer = ""
}
'"' => {
state = CirruLexState::Str
state = Str
buffer = ""
}
_ => {
state = CirruLexState::Token
state = Token
buffer = c.to_string()
}
}
CirruLexState::Token =>
Token =>
match c {
' ' => {
acc.push(CirruLexItem::Str(buffer))
state = CirruLexState::Space
acc.push(Str(buffer))
state = Space
buffer = ""
}
'"' => {
acc.push(CirruLexItem::Str(buffer))
state = CirruLexState::Str
acc.push(Str(buffer))
state = Str
buffer = ""
}
'\n' => {
acc.push(CirruLexItem::Str(buffer))
state = CirruLexState::Indent
acc.push(Str(buffer))
state = Indent
buffer = ""
}
'(' => {
acc.push(CirruLexItem::Str(buffer))
acc.push(CirruLexItem::Open)
state = CirruLexState::Space
acc.push(Str(buffer))
acc.push(Open)
state = Space
buffer = ""
}
')' => {
acc.push(CirruLexItem::Str(buffer))
acc.push(CirruLexItem::Close)
state = CirruLexState::Space
acc.push(Str(buffer))
acc.push(Close)
state = Space
buffer = ""
}
_ => {
state = CirruLexState::Token
state = Token
buffer += c.to_string()
}
}
CirruLexState::Str =>
Str =>
match c {
'"' => {
acc.push(CirruLexItem::Str(buffer))
state = CirruLexState::Space
acc.push(Str(buffer))
state = Space
buffer = ""
}
'\\' => state = CirruLexState::Escape
'\\' => state = Escape
'\n' => raise CirruParseError("unexpected newline in string")
_ => {
state = CirruLexState::Str
state = Str
buffer += c.to_string()
}
}
CirruLexState::Escape =>
Escape =>
match c {
'"' => {
state = CirruLexState::Str
state = Str
buffer += "\""
}
'\'' => {
state = CirruLexState::Str
state = Str
buffer += "'"
}
't' => {
state = CirruLexState::Str
state = Str
buffer += "\t"
}
'n' => {
state = CirruLexState::Str
state = Str
buffer += "\n"
}
'r' => {
state = CirruLexState::Str
state = Str
buffer += "\r"
}
'u' => {
Expand All @@ -210,10 +209,10 @@ fn lex(initial_code : String) -> Array[CirruLexItem]!CirruParseError {
}
println("Unicode escaping is not supported yet: \{preview} ...")
buffer += "\\u"
state = CirruLexState::Str
state = Str
}
'\\' => {
state = CirruLexState::Str
state = Str
buffer += "\\"
}
_ =>
Expand All @@ -224,46 +223,46 @@ fn lex(initial_code : String) -> Array[CirruLexItem]!CirruParseError {
Indent =>
match c {
' ' => {
state = CirruLexState::Indent
state = Indent
buffer += c.to_string()
}
'\n' => {
state = CirruLexState::Indent
state = Indent
buffer = ""
}
'"' => {
let level = parse_indentation!(buffer.length())
acc.push(level)
state = CirruLexState::Str
state = Str
buffer = ""
}
'(' => {
let level = parse_indentation!(buffer.length())
acc.push(level)
acc.push(CirruLexItem::Open)
state = CirruLexState::Space
state = Space
buffer = ""
}
')' => raise CirruParseError("unexpected ) at line start")
_ => {
let level = parse_indentation!(buffer.length())
acc.push(level)
state = CirruLexState::Token
state = Token
buffer = c.to_string()
}
}
}
}
// println("end of file in lex \{acc}")
match state {
CirruLexState::Space => acc
CirruLexState::Token => {
Space => acc
Token => {
acc.push(CirruLexItem::Str(buffer))
acc
}
CirruLexState::Escape => raise CirruParseError("unknown escape")
CirruLexState::Indent => acc
CirruLexState::Str => raise CirruParseError("finished at string")
Escape => raise CirruParseError("unknown escape")
Indent => acc
Str => raise CirruParseError("finished at string")
}
}

Expand All @@ -278,51 +277,51 @@ fn resolve_indentations(tokens : Array[CirruLexItem]) -> Array[CirruLexItem] {
if acc.is_empty() {
return Array::new()
}
acc.insert(0, CirruLexItem::Open)
acc.insert(0, Open)
for i = 0; i < level; i = i + 1 {
acc.push(CirruLexItem::Close)
acc.push(Close)
}
acc.push(CirruLexItem::Close)
acc.push(Close)
return acc
}
match tokens[pointer] {
CirruLexItem::Str(s) => {
acc.push(CirruLexItem::Str(s))
Str(s) => {
acc.push(Str(s))
pointer += 1
}
CirruLexItem::Open => {
acc.push(CirruLexItem::Open)
Open => {
acc.push(Open)
pointer += 1
}
CirruLexItem::Close => {
acc.push(CirruLexItem::Close)
Close => {
acc.push(Close)
pointer += 1
}
CirruLexItem::Indent(n) =>
Indent(n) =>
if n > level {
let delta = n - level
// for _ in 0..delta {
for i = 0; i < delta; i = i + 1 {
acc.push(CirruLexItem::Open)
acc.push(Open)
}
pointer += 1
level = n
} else if n < level {
let delta = level - n
// for _ in 0..delta {
for i = 0; i < delta; i = i + 1 {
acc.push(CirruLexItem::Close)
acc.push(Close)
}
acc.push(CirruLexItem::Close)
acc.push(CirruLexItem::Open)
acc.push(Close)
acc.push(Open)
pointer += 1
level = n
} else {
if acc.is_empty() {
acc = Array::new()
} else {
acc.push(CirruLexItem::Close)
acc.push(CirruLexItem::Open)
acc.push(Close)
acc.push(Open)
}
pointer += 1
}
Expand Down
Loading