123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161 |
- const AST = require('./ast')
- const Error = require('./Error')
- const tokenTypes = require('./tokenTypes')
-
- module.exports = class Parser {
- constructor(tokenStream) {
- this.tokenStream = tokenStream
- }
-
- parse() {
- let tree = []
- while (this.tokenStream.peek().type !== tokenTypes.EOF) {
- let expr = this.expr()
- tree.push(expr)
- if (this.tokenStream.error) {
- return {
- error: this.tokenStream.error,
- }
- }
- }
- return tree
- }
-
- expr() {
- let token = this.tokenStream.peek()
- switch (token.type) {
- case tokenTypes.ATTRIBUTE:
- return this.attribute()
- case tokenTypes.BOOLEAN:
- return this.bool()
- case tokenTypes.IDENTIFIER:
- return this.identifier()
- case tokenTypes.NUMBER:
- return this.number()
- case tokenTypes.QUOTE:
- return this.string()
- case tokenTypes.SYMBOL:
- return this.symbol()
- case tokenTypes.OPAREN:
- return this.form()
- default:
- this.tokenStream.error = `Unexpected ${token.type} on line ${
- token.line
- }`
- break
- }
- }
-
- form() {
- this.tokenStream.eat(tokenTypes.OPAREN)
-
- let node
-
- if (this.tokenStream.peek().value === 'define') {
- this.tokenStream.eat(tokenTypes.IDENTIFIER)
- node = this.functionDefinition()
- } else if (this.tokenStream.peek().value === 'lambda') {
- this.tokenStream.eat(tokenTypes.IDENTIFIER)
- node = this.functionDefinition(true)
- } else {
- node = new AST.Application()
-
- node.function = this.expr()
-
- node.args = []
-
- while (this.tokenStream.peek().type !== tokenTypes.CPAREN) {
- node.args.push(this.expr())
- }
- }
-
- this.tokenStream.eat(tokenTypes.CPAREN)
-
- return node
- }
-
- attribute() {
- return new AST.Attribute({
- name: this.tokenStream.eat(tokenTypes.ATTRIBUTE).value,
- value: this.expr(),
- })
- }
-
- bool() {
- return new AST.Boolean({
- value: this.tokenStream.eat(tokenTypes.BOOLEAN).value,
- })
- }
-
- functionDefinition(anonymous = false) {
- let name
-
- if (anonymous) {
- name = false
- } else {
- name = new AST.Identifier({
- name: this.tokenStream.eat(tokenTypes.IDENTIFIER).value,
- })
- }
-
- let parameters = []
-
- this.tokenStream.eat(tokenTypes.OPAREN)
-
- while (this.tokenStream.peek().type !== tokenTypes.CPAREN) {
- parameters.push(
- new AST.Identifier({
- name: this.tokenStream.eat(tokenTypes.IDENTIFIER).value,
- }),
- )
-
- if (this.tokenStream.peek().type === tokenTypes.COMMA) {
- this.tokenStream.eat(tokenTypes.COMMA)
- }
- }
-
- this.tokenStream.eat(tokenTypes.CPAREN)
-
- return new AST.FunctionDefinition({
- name: name,
- parameters: parameters,
- body: this.form(),
- })
- }
-
- identifier() {
- return new AST.Identifier({
- name: this.tokenStream.eat(tokenTypes.IDENTIFIER).value,
- })
- }
-
- number() {
- return new AST.Number({
- value: this.tokenStream.eat(tokenTypes.NUMBER).value,
- })
- }
-
- string() {
- this.tokenStream.eat(tokenTypes.QUOTE)
-
- let value = ''
-
- if (this.tokenStream.peek().type === tokenTypes.LITERAL) {
- value = this.tokenStream.eat(tokenTypes.LITERAL).value
- }
-
- let node = new AST.String({
- value: value,
- })
-
- this.tokenStream.eat(tokenTypes.QUOTE)
-
- return node
- }
-
- symbol() {
- return new AST.Symbol({
- value: this.tokenStream.eat(tokenTypes.SYMBOL).value,
- })
- }
- }
|