mirror of
				https://github.com/GayPizzaSpecifications/pork.git
				synced 2025-11-03 17:39:38 +00:00 
			
		
		
		
	String literal support.
This commit is contained in:
		@ -49,4 +49,5 @@ results = [
 | 
				
			|||||||
  notEqual(5, 6)
 | 
					  notEqual(5, 6)
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					println("results:")
 | 
				
			||||||
println(results)
 | 
					println(results)
 | 
				
			||||||
 | 
				
			|||||||
@ -12,6 +12,7 @@ class NodeCoalescer(val handler: (Node) -> Unit) : NodeVisitor<Unit> {
 | 
				
			|||||||
  override fun visitIntLiteral(node: IntLiteral) = handler(node)
 | 
					  override fun visitIntLiteral(node: IntLiteral) = handler(node)
 | 
				
			||||||
  override fun visitBooleanLiteral(node: BooleanLiteral) = handler(node)
 | 
					  override fun visitBooleanLiteral(node: BooleanLiteral) = handler(node)
 | 
				
			||||||
  override fun visitListLiteral(node: ListLiteral) = handler(node)
 | 
					  override fun visitListLiteral(node: ListLiteral) = handler(node)
 | 
				
			||||||
 | 
					  override fun visitStringLiteral(node: StringLiteral) = handler(node)
 | 
				
			||||||
  override fun visitParentheses(node: Parentheses) = handler(node)
 | 
					  override fun visitParentheses(node: Parentheses) = handler(node)
 | 
				
			||||||
  override fun visitPrefixOperation(node: PrefixOperation) = handler(node)
 | 
					  override fun visitPrefixOperation(node: PrefixOperation) = handler(node)
 | 
				
			||||||
  override fun visitInfixOperation(node: InfixOperation) = handler(node)
 | 
					  override fun visitInfixOperation(node: InfixOperation) = handler(node)
 | 
				
			||||||
 | 
				
			|||||||
@ -1,20 +1,19 @@
 | 
				
			|||||||
package gay.pizza.pork.ast
 | 
					package gay.pizza.pork.ast
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import gay.pizza.pork.ast.NodeTypeTrait.*
 | 
					enum class NodeType(val parent: NodeType? = null) {
 | 
				
			||||||
 | 
					 | 
				
			||||||
enum class NodeType(val parent: NodeType? = null, vararg traits: NodeTypeTrait) {
 | 
					 | 
				
			||||||
  Node,
 | 
					  Node,
 | 
				
			||||||
  Symbol(Node),
 | 
					  Symbol(Node),
 | 
				
			||||||
  Expression(Node, Intermediate),
 | 
					  Expression(Node),
 | 
				
			||||||
  Program(Node),
 | 
					  Program(Node),
 | 
				
			||||||
  IntLiteral(Expression, Literal),
 | 
					  IntLiteral(Expression),
 | 
				
			||||||
  BooleanLiteral(Expression, Literal),
 | 
					  BooleanLiteral(Expression),
 | 
				
			||||||
  ListLiteral(Expression, Literal),
 | 
					  ListLiteral(Expression),
 | 
				
			||||||
 | 
					  StringLiteral(Expression),
 | 
				
			||||||
  Parentheses(Expression),
 | 
					  Parentheses(Expression),
 | 
				
			||||||
  Define(Expression),
 | 
					  Define(Expression),
 | 
				
			||||||
  Lambda(Expression),
 | 
					  Lambda(Expression),
 | 
				
			||||||
  PrefixOperation(Expression, Operation),
 | 
					  PrefixOperation(Expression),
 | 
				
			||||||
  InfixOperation(Expression, Operation),
 | 
					  InfixOperation(Expression),
 | 
				
			||||||
  SymbolReference(Expression),
 | 
					  SymbolReference(Expression),
 | 
				
			||||||
  FunctionCall(Expression),
 | 
					  FunctionCall(Expression),
 | 
				
			||||||
  If(Expression);
 | 
					  If(Expression);
 | 
				
			||||||
 | 
				
			|||||||
@ -13,6 +13,7 @@ interface NodeVisitor<T> {
 | 
				
			|||||||
  fun visitIntLiteral(node: IntLiteral): T
 | 
					  fun visitIntLiteral(node: IntLiteral): T
 | 
				
			||||||
  fun visitBooleanLiteral(node: BooleanLiteral): T
 | 
					  fun visitBooleanLiteral(node: BooleanLiteral): T
 | 
				
			||||||
  fun visitListLiteral(node: ListLiteral): T
 | 
					  fun visitListLiteral(node: ListLiteral): T
 | 
				
			||||||
 | 
					  fun visitStringLiteral(node: StringLiteral): T
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  fun visitParentheses(node: Parentheses): T
 | 
					  fun visitParentheses(node: Parentheses): T
 | 
				
			||||||
  fun visitPrefixOperation(node: PrefixOperation): T
 | 
					  fun visitPrefixOperation(node: PrefixOperation): T
 | 
				
			||||||
@ -24,6 +25,7 @@ interface NodeVisitor<T> {
 | 
				
			|||||||
    is IntLiteral -> visitIntLiteral(node)
 | 
					    is IntLiteral -> visitIntLiteral(node)
 | 
				
			||||||
    is BooleanLiteral -> visitBooleanLiteral(node)
 | 
					    is BooleanLiteral -> visitBooleanLiteral(node)
 | 
				
			||||||
    is ListLiteral -> visitListLiteral(node)
 | 
					    is ListLiteral -> visitListLiteral(node)
 | 
				
			||||||
 | 
					    is StringLiteral -> visitStringLiteral(node)
 | 
				
			||||||
    is Parentheses -> visitParentheses(node)
 | 
					    is Parentheses -> visitParentheses(node)
 | 
				
			||||||
    is InfixOperation -> visitInfixOperation(node)
 | 
					    is InfixOperation -> visitInfixOperation(node)
 | 
				
			||||||
    is PrefixOperation -> visitPrefixOperation(node)
 | 
					    is PrefixOperation -> visitPrefixOperation(node)
 | 
				
			||||||
@ -32,14 +34,12 @@ interface NodeVisitor<T> {
 | 
				
			|||||||
    is FunctionCall -> visitFunctionCall(node)
 | 
					    is FunctionCall -> visitFunctionCall(node)
 | 
				
			||||||
    is SymbolReference -> visitReference(node)
 | 
					    is SymbolReference -> visitReference(node)
 | 
				
			||||||
    is If -> visitIf(node)
 | 
					    is If -> visitIf(node)
 | 
				
			||||||
    else -> throw RuntimeException("Unknown Expression")
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  fun visit(node: Node): T = when (node) {
 | 
					  fun visit(node: Node): T = when (node) {
 | 
				
			||||||
    is Expression -> visitExpression(node)
 | 
					    is Expression -> visitExpression(node)
 | 
				
			||||||
    is Symbol -> visitSymbol(node)
 | 
					    is Symbol -> visitSymbol(node)
 | 
				
			||||||
    is Program -> visitProgram(node)
 | 
					    is Program -> visitProgram(node)
 | 
				
			||||||
    else -> throw RuntimeException("Unknown Node")
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  fun visitNodes(vararg nodes: Node?): List<T> =
 | 
					  fun visitNodes(vararg nodes: Node?): List<T> =
 | 
				
			||||||
 | 
				
			|||||||
@ -1,6 +1,7 @@
 | 
				
			|||||||
package gay.pizza.pork.ast
 | 
					package gay.pizza.pork.ast
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import gay.pizza.pork.ast.nodes.*
 | 
					import gay.pizza.pork.ast.nodes.*
 | 
				
			||||||
 | 
					import gay.pizza.pork.util.StringEscape
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Printer(private val buffer: StringBuilder) : NodeVisitor<Unit> {
 | 
					class Printer(private val buffer: StringBuilder) : NodeVisitor<Unit> {
 | 
				
			||||||
  private var indent = 0
 | 
					  private var indent = 0
 | 
				
			||||||
@ -109,6 +110,12 @@ class Printer(private val buffer: StringBuilder) : NodeVisitor<Unit> {
 | 
				
			|||||||
    append("]")
 | 
					    append("]")
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  override fun visitStringLiteral(node: StringLiteral) {
 | 
				
			||||||
 | 
					    append("\"")
 | 
				
			||||||
 | 
					    append(StringEscape.escape(node.text))
 | 
				
			||||||
 | 
					    append("\"")
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  override fun visitParentheses(node: Parentheses) {
 | 
					  override fun visitParentheses(node: Parentheses) {
 | 
				
			||||||
    append("(")
 | 
					    append("(")
 | 
				
			||||||
    visit(node.expression)
 | 
					    visit(node.expression)
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										18
									
								
								src/main/kotlin/gay/pizza/pork/ast/nodes/StringLiteral.kt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								src/main/kotlin/gay/pizza/pork/ast/nodes/StringLiteral.kt
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,18 @@
 | 
				
			|||||||
 | 
					package gay.pizza.pork.ast.nodes
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import gay.pizza.pork.ast.NodeType
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class StringLiteral(val text: String) : Expression() {
 | 
				
			||||||
 | 
					  override val type: NodeType = NodeType.StringLiteral
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  override fun equals(other: Any?): Boolean {
 | 
				
			||||||
 | 
					    if (other !is StringLiteral) return false
 | 
				
			||||||
 | 
					    return other.text == text
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  override fun hashCode(): Int {
 | 
				
			||||||
 | 
					    var result = text.hashCode()
 | 
				
			||||||
 | 
					    result = 31 * result + type.hashCode()
 | 
				
			||||||
 | 
					    return result
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -2,6 +2,7 @@ package gay.pizza.pork.compiler
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import gay.pizza.pork.ast.*
 | 
					import gay.pizza.pork.ast.*
 | 
				
			||||||
import gay.pizza.pork.ast.nodes.*
 | 
					import gay.pizza.pork.ast.nodes.*
 | 
				
			||||||
 | 
					import gay.pizza.pork.util.StringEscape
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class KotlinCompiler : NodeVisitor<String> {
 | 
					class KotlinCompiler : NodeVisitor<String> {
 | 
				
			||||||
  override fun visitDefine(node: Define): String =
 | 
					  override fun visitDefine(node: Define): String =
 | 
				
			||||||
@ -55,6 +56,9 @@ class KotlinCompiler : NodeVisitor<String> {
 | 
				
			|||||||
    append(")")
 | 
					    append(")")
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  override fun visitStringLiteral(node: StringLiteral): String =
 | 
				
			||||||
 | 
					    "\"" + StringEscape.escape(node.text) + "\""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  override fun visitParentheses(node: Parentheses): String =
 | 
					  override fun visitParentheses(node: Parentheses): String =
 | 
				
			||||||
    "(${visit(node.expression)})"
 | 
					    "(${visit(node.expression)})"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -1,7 +1,3 @@
 | 
				
			|||||||
package gay.pizza.pork.eval
 | 
					package gay.pizza.pork.eval
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Arguments(val values: List<Any>) {
 | 
					class Arguments(val values: List<Any>)
 | 
				
			||||||
  companion object {
 | 
					 | 
				
			||||||
    val Zero = Arguments(emptyList())
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
				
			|||||||
@ -58,6 +58,7 @@ class Evaluator(root: Scope) : NodeVisitor<Any> {
 | 
				
			|||||||
  override fun visitIntLiteral(node: IntLiteral): Any = node.value
 | 
					  override fun visitIntLiteral(node: IntLiteral): Any = node.value
 | 
				
			||||||
  override fun visitBooleanLiteral(node: BooleanLiteral): Any = node.value
 | 
					  override fun visitBooleanLiteral(node: BooleanLiteral): Any = node.value
 | 
				
			||||||
  override fun visitListLiteral(node: ListLiteral): Any = node.items.map { visit(it) }
 | 
					  override fun visitListLiteral(node: ListLiteral): Any = node.items.map { visit(it) }
 | 
				
			||||||
 | 
					  override fun visitStringLiteral(node: StringLiteral): Any = node.text
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  override fun visitParentheses(node: Parentheses): Any = visit(node.expression)
 | 
					  override fun visitParentheses(node: Parentheses): Any = visit(node.expression)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -3,6 +3,7 @@ package gay.pizza.pork.parse
 | 
				
			|||||||
open class AnsiHighlightScheme : HighlightScheme {
 | 
					open class AnsiHighlightScheme : HighlightScheme {
 | 
				
			||||||
  override fun highlight(token: Token): Highlight {
 | 
					  override fun highlight(token: Token): Highlight {
 | 
				
			||||||
    val attributes = when (token.type.family) {
 | 
					    val attributes = when (token.type.family) {
 | 
				
			||||||
 | 
					      TokenFamily.StringLiteralFamily -> string()
 | 
				
			||||||
      TokenFamily.OperatorFamily -> operator()
 | 
					      TokenFamily.OperatorFamily -> operator()
 | 
				
			||||||
      TokenFamily.KeywordFamily -> keyword()
 | 
					      TokenFamily.KeywordFamily -> keyword()
 | 
				
			||||||
      TokenFamily.SymbolFamily -> symbol()
 | 
					      TokenFamily.SymbolFamily -> symbol()
 | 
				
			||||||
@ -15,8 +16,9 @@ open class AnsiHighlightScheme : HighlightScheme {
 | 
				
			|||||||
    } else Highlight(token)
 | 
					    } else Highlight(token)
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  open fun operator(): AnsiAttributes = AnsiAttributes("32m")
 | 
					  open fun string(): AnsiAttributes = AnsiAttributes("32m")
 | 
				
			||||||
  open fun symbol(): AnsiAttributes = AnsiAttributes("33m")
 | 
					  open fun symbol(): AnsiAttributes = AnsiAttributes("33m")
 | 
				
			||||||
 | 
					  open fun operator(): AnsiAttributes = AnsiAttributes("34m")
 | 
				
			||||||
  open fun keyword(): AnsiAttributes = AnsiAttributes("35m")
 | 
					  open fun keyword(): AnsiAttributes = AnsiAttributes("35m")
 | 
				
			||||||
  open fun comment(): AnsiAttributes = AnsiAttributes("37m")
 | 
					  open fun comment(): AnsiAttributes = AnsiAttributes("37m")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -1,6 +1,7 @@
 | 
				
			|||||||
package gay.pizza.pork.parse
 | 
					package gay.pizza.pork.parse
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import gay.pizza.pork.ast.nodes.*
 | 
					import gay.pizza.pork.ast.nodes.*
 | 
				
			||||||
 | 
					import gay.pizza.pork.util.StringEscape
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Parser(source: PeekableSource<Token>) {
 | 
					class Parser(source: PeekableSource<Token>) {
 | 
				
			||||||
  private val unsanitizedSource = source
 | 
					  private val unsanitizedSource = source
 | 
				
			||||||
@ -65,6 +66,11 @@ class Parser(source: PeekableSource<Token>) {
 | 
				
			|||||||
  fun readExpression(): Expression {
 | 
					  fun readExpression(): Expression {
 | 
				
			||||||
    val token = peek()
 | 
					    val token = peek()
 | 
				
			||||||
    val expression = when (token.type) {
 | 
					    val expression = when (token.type) {
 | 
				
			||||||
 | 
					      TokenType.StringLiteral -> {
 | 
				
			||||||
 | 
					        expect(TokenType.StringLiteral)
 | 
				
			||||||
 | 
					        return StringLiteral(StringEscape.unescape(StringEscape.unquote(token.text)))
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      TokenType.IntLiteral -> {
 | 
					      TokenType.IntLiteral -> {
 | 
				
			||||||
        readIntLiteral()
 | 
					        readIntLiteral()
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
				
			|||||||
@ -5,6 +5,7 @@ enum class TokenFamily : TokenTypeProperty {
 | 
				
			|||||||
  KeywordFamily,
 | 
					  KeywordFamily,
 | 
				
			||||||
  SymbolFamily,
 | 
					  SymbolFamily,
 | 
				
			||||||
  NumericLiteralFamily,
 | 
					  NumericLiteralFamily,
 | 
				
			||||||
 | 
					  StringLiteralFamily,
 | 
				
			||||||
  CommentFamily,
 | 
					  CommentFamily,
 | 
				
			||||||
  OtherFamily
 | 
					  OtherFamily
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -6,6 +6,7 @@ import gay.pizza.pork.parse.TokenFamily.*
 | 
				
			|||||||
enum class TokenType(vararg properties: TokenTypeProperty) {
 | 
					enum class TokenType(vararg properties: TokenTypeProperty) {
 | 
				
			||||||
  Symbol(SymbolFamily),
 | 
					  Symbol(SymbolFamily),
 | 
				
			||||||
  IntLiteral(NumericLiteralFamily),
 | 
					  IntLiteral(NumericLiteralFamily),
 | 
				
			||||||
 | 
					  StringLiteral(StringLiteralFamily),
 | 
				
			||||||
  Equality(OperatorFamily),
 | 
					  Equality(OperatorFamily),
 | 
				
			||||||
  Inequality(OperatorFamily),
 | 
					  Inequality(OperatorFamily),
 | 
				
			||||||
  Equals(SingleChar('='), Promotion('=', Equality)),
 | 
					  Equals(SingleChar('='), Promotion('=', Equality)),
 | 
				
			||||||
 | 
				
			|||||||
@ -89,6 +89,23 @@ class Tokenizer(val source: CharSource) {
 | 
				
			|||||||
    return Token(TokenType.LineComment, tokenStart, comment)
 | 
					    return Token(TokenType.LineComment, tokenStart, comment)
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private fun readStringLiteral(firstChar: Char): Token {
 | 
				
			||||||
 | 
					    val string = buildString {
 | 
				
			||||||
 | 
					      append(firstChar)
 | 
				
			||||||
 | 
					      while (true) {
 | 
				
			||||||
 | 
					        val char = source.peek()
 | 
				
			||||||
 | 
					        if (char == CharSource.NullChar) {
 | 
				
			||||||
 | 
					          throw RuntimeException("Unterminated string.")
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        append(source.next())
 | 
				
			||||||
 | 
					        if (char == '"') {
 | 
				
			||||||
 | 
					          break
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return Token(TokenType.StringLiteral, tokenStart, string)
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  fun next(): Token {
 | 
					  fun next(): Token {
 | 
				
			||||||
    while (source.peek() != CharSource.NullChar) {
 | 
					    while (source.peek() != CharSource.NullChar) {
 | 
				
			||||||
      tokenStart = source.currentIndex
 | 
					      tokenStart = source.currentIndex
 | 
				
			||||||
@ -132,6 +149,11 @@ class Tokenizer(val source: CharSource) {
 | 
				
			|||||||
      if (isSymbol(char)) {
 | 
					      if (isSymbol(char)) {
 | 
				
			||||||
        return readSymbolOrKeyword(char)
 | 
					        return readSymbolOrKeyword(char)
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      if (char == '"') {
 | 
				
			||||||
 | 
					        return readStringLiteral(char)
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      throw RuntimeException("Failed to parse: (${char}) next ${source.peek()}")
 | 
					      throw RuntimeException("Failed to parse: (${char}) next ${source.peek()}")
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    return Token.endOfFile(source.currentIndex)
 | 
					    return Token.endOfFile(source.currentIndex)
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										8
									
								
								src/main/kotlin/gay/pizza/pork/util/StringEscape.kt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								src/main/kotlin/gay/pizza/pork/util/StringEscape.kt
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,8 @@
 | 
				
			|||||||
 | 
					package gay.pizza.pork.util
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					object StringEscape {
 | 
				
			||||||
 | 
					  fun escape(input: String): String = input.replace("\n", "\\n")
 | 
				
			||||||
 | 
					  fun unescape(input: String): String = input.replace("\\n", "\n")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  fun unquote(input: String): String = input.substring(1, input.length - 1)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
		Reference in New Issue
	
	Block a user